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  }