github.com/joomcode/cue@v0.4.4-0.20221111115225-539fe3512047/tools/fix/fix.go (about) 1 // Copyright 2019 CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package fix contains functionality for writing CUE files with legacy 16 // syntax to newer ones. 17 // 18 // Note: the transformations that are supported in this package will change 19 // over time. 20 package fix 21 22 import ( 23 "strings" 24 25 "github.com/joomcode/cue/cue/ast" 26 "github.com/joomcode/cue/cue/ast/astutil" 27 "github.com/joomcode/cue/cue/token" 28 ) 29 30 type Option func(*options) 31 32 type options struct { 33 simplify bool 34 deprecated bool 35 } 36 37 // Simplify enables fixes that simplify the code, but are not strictly 38 // necessary. 39 func Simplify() Option { 40 return func(o *options) { o.simplify = true } 41 } 42 43 // File applies fixes to f and returns it. It alters the original f. 44 func File(f *ast.File, o ...Option) *ast.File { 45 var options options 46 for _, f := range o { 47 f(&options) 48 } 49 50 // Rewrite integer division operations to use builtins. 51 f = astutil.Apply(f, func(c astutil.Cursor) bool { 52 n := c.Node() 53 switch x := n.(type) { 54 case *ast.BinaryExpr: 55 switch x.Op { 56 case token.IDIV, token.IMOD, token.IQUO, token.IREM: 57 ast.SetRelPos(x.X, token.NoSpace) 58 c.Replace(&ast.CallExpr{ 59 // Use the __foo version to prevent accidental shadowing. 60 Fun: ast.NewIdent("__" + x.Op.String()), 61 Args: []ast.Expr{x.X, x.Y}, 62 }) 63 } 64 } 65 return true 66 }, nil).(*ast.File) 67 68 // Rewrite an old-style alias to a let clause. 69 ast.Walk(f, func(n ast.Node) bool { 70 var decls []ast.Decl 71 switch x := n.(type) { 72 case *ast.StructLit: 73 decls = x.Elts 74 case *ast.File: 75 decls = x.Decls 76 } 77 for i, d := range decls { 78 if a, ok := d.(*ast.Alias); ok { 79 x := &ast.LetClause{ 80 Ident: a.Ident, 81 Equal: a.Equal, 82 Expr: a.Expr, 83 } 84 astutil.CopyMeta(x, a) 85 decls[i] = x 86 } 87 } 88 return true 89 }, nil) 90 91 // Rewrite block comments to regular comments. 92 ast.Walk(f, func(n ast.Node) bool { 93 switch x := n.(type) { 94 case *ast.CommentGroup: 95 comments := []*ast.Comment{} 96 for _, c := range x.List { 97 s := c.Text 98 if !strings.HasPrefix(s, "/*") || !strings.HasSuffix(s, "*/") { 99 comments = append(comments, c) 100 continue 101 } 102 if x.Position > 0 { 103 // Moving to the end doesn't work, as it still 104 // may inject at a false line break position. 105 x.Position = 0 106 x.Doc = true 107 } 108 s = strings.TrimSpace(s[2 : len(s)-2]) 109 for _, s := range strings.Split(s, "\n") { 110 for i := 0; i < 3; i++ { 111 if strings.HasPrefix(s, " ") || strings.HasPrefix(s, "*") { 112 s = s[1:] 113 } 114 } 115 comments = append(comments, &ast.Comment{Text: "// " + s}) 116 } 117 } 118 x.List = comments 119 return false 120 } 121 return true 122 }, nil) 123 124 // TODO: we are probably reintroducing slices. Disable for now. 125 // 126 // Rewrite slice expression. 127 // f = astutil.Apply(f, func(c astutil.Cursor) bool { 128 // n := c.Node() 129 // getVal := func(n ast.Expr) ast.Expr { 130 // if n == nil { 131 // return nil 132 // } 133 // if id, ok := n.(*ast.Ident); ok && id.Name == "_" { 134 // return nil 135 // } 136 // return n 137 // } 138 // switch x := n.(type) { 139 // case *ast.SliceExpr: 140 // ast.SetRelPos(x.X, token.NoRelPos) 141 142 // lo := getVal(x.Low) 143 // hi := getVal(x.High) 144 // if lo == nil { // a[:j] 145 // lo = mustParseExpr("0") 146 // astutil.CopyMeta(lo, x.Low) 147 // } 148 // if hi == nil { // a[i:] 149 // hi = ast.NewCall(ast.NewIdent("len"), x.X) 150 // astutil.CopyMeta(lo, x.High) 151 // } 152 // if pkg := c.Import("list"); pkg != nil { 153 // c.Replace(ast.NewCall(ast.NewSel(pkg, "Slice"), x.X, lo, hi)) 154 // } 155 // } 156 // return true 157 // }, nil).(*ast.File) 158 159 if options.simplify { 160 f = simplify(f) 161 } 162 163 return f 164 }