Source file src/cmd/gofmt/simplify.go

     1  // Copyright 2010 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package main
     6  
     7  import (
     8  	"go/ast"
     9  	"go/token"
    10  	"reflect"
    11  )
    12  
    13  type simplifier struct{}
    14  
    15  func (s simplifier) Visit(node ast.Node) ast.Visitor {
    16  	switch n := node.(type) {
    17  	case *ast.CompositeLit:
    18  		// array, slice, and map composite literals may be simplified
    19  		outer := n
    20  		var keyType, eltType ast.Expr
    21  		switch typ := outer.Type.(type) {
    22  		case *ast.ArrayType:
    23  			eltType = typ.Elt
    24  		case *ast.MapType:
    25  			keyType = typ.Key
    26  			eltType = typ.Value
    27  		}
    28  
    29  		if eltType != nil {
    30  			var ktyp reflect.Value
    31  			if keyType != nil {
    32  				ktyp = reflect.ValueOf(keyType)
    33  			}
    34  			typ := reflect.ValueOf(eltType)
    35  			for i, x := range outer.Elts {
    36  				px := &outer.Elts[i]
    37  				// look at value of indexed/named elements
    38  				if t, ok := x.(*ast.KeyValueExpr); ok {
    39  					if keyType != nil {
    40  						s.simplifyLiteral(ktyp, keyType, t.Key, &t.Key)
    41  					}
    42  					x = t.Value
    43  					px = &t.Value
    44  				}
    45  				s.simplifyLiteral(typ, eltType, x, px)
    46  			}
    47  			// node was simplified - stop walk (there are no subnodes to simplify)
    48  			return nil
    49  		}
    50  
    51  	case *ast.SliceExpr:
    52  		// a slice expression of the form: s[a:len(s)]
    53  		// can be simplified to: s[a:]
    54  		// if s is "simple enough" (for now we only accept identifiers)
    55  		//
    56  		// Note: This may not be correct because len may have been redeclared in another
    57  		//       file belonging to the same package. However, this is extremely unlikely
    58  		//       and so far (April 2016, after years of supporting this rewrite feature)
    59  		//       has never come up, so let's keep it working as is (see also #15153).
    60  		if n.Max != nil {
    61  			// - 3-index slices always require the 2nd and 3rd index
    62  			break
    63  		}
    64  		if s, _ := n.X.(*ast.Ident); s != nil && s.Obj != nil {
    65  			// the array/slice object is a single, resolved identifier
    66  			if call, _ := n.High.(*ast.CallExpr); call != nil && len(call.Args) == 1 && !call.Ellipsis.IsValid() {
    67  				// the high expression is a function call with a single argument
    68  				if fun, _ := call.Fun.(*ast.Ident); fun != nil && fun.Name == "len" && fun.Obj == nil {
    69  					// the function called is "len" and it is not locally defined; and
    70  					// because we don't have dot imports, it must be the predefined len()
    71  					if arg, _ := call.Args[0].(*ast.Ident); arg != nil && arg.Obj == s.Obj {
    72  						// the len argument is the array/slice object
    73  						n.High = nil
    74  					}
    75  				}
    76  			}
    77  		}
    78  		// Note: We could also simplify slice expressions of the form s[0:b] to s[:b]
    79  		//       but we leave them as is since sometimes we want to be very explicit
    80  		//       about the lower bound.
    81  		// An example where the 0 helps:
    82  		//       x, y, z := b[0:2], b[2:4], b[4:6]
    83  		// An example where it does not:
    84  		//       x, y := b[:n], b[n:]
    85  
    86  	case *ast.RangeStmt:
    87  		// - a range of the form: for x, _ = range v {...}
    88  		// can be simplified to: for x = range v {...}
    89  		// - a range of the form: for _ = range v {...}
    90  		// can be simplified to: for range v {...}
    91  		if isBlank(n.Value) {
    92  			n.Value = nil
    93  		}
    94  		if isBlank(n.Key) && n.Value == nil {
    95  			n.Key = nil
    96  		}
    97  	}
    98  
    99  	return s
   100  }
   101  
   102  func (s simplifier) simplifyLiteral(typ reflect.Value, astType, x ast.Expr, px *ast.Expr) {
   103  	ast.Walk(s, x) // simplify x
   104  
   105  	// if the element is a composite literal and its literal type
   106  	// matches the outer literal's element type exactly, the inner
   107  	// literal type may be omitted
   108  	if inner, ok := x.(*ast.CompositeLit); ok {
   109  		if match(nil, typ, reflect.ValueOf(inner.Type)) {
   110  			inner.Type = nil
   111  		}
   112  	}
   113  	// if the outer literal's element type is a pointer type *T
   114  	// and the element is & of a composite literal of type T,
   115  	// the inner &T may be omitted.
   116  	if ptr, ok := astType.(*ast.StarExpr); ok {
   117  		if addr, ok := x.(*ast.UnaryExpr); ok && addr.Op == token.AND {
   118  			if inner, ok := addr.X.(*ast.CompositeLit); ok {
   119  				if match(nil, reflect.ValueOf(ptr.X), reflect.ValueOf(inner.Type)) {
   120  					inner.Type = nil // drop T
   121  					*px = inner      // drop &
   122  				}
   123  			}
   124  		}
   125  	}
   126  }
   127  
   128  func isBlank(x ast.Expr) bool {
   129  	ident, ok := x.(*ast.Ident)
   130  	return ok && ident.Name == "_"
   131  }
   132  
   133  func simplify(f *ast.File) {
   134  	// remove empty declarations such as "const ()", etc
   135  	removeEmptyDeclGroups(f)
   136  
   137  	var s simplifier
   138  	ast.Walk(s, f)
   139  }
   140  
   141  func removeEmptyDeclGroups(f *ast.File) {
   142  	i := 0
   143  	for _, d := range f.Decls {
   144  		if g, ok := d.(*ast.GenDecl); !ok || !isEmpty(f, g) {
   145  			f.Decls[i] = d
   146  			i++
   147  		}
   148  	}
   149  	f.Decls = f.Decls[:i]
   150  }
   151  
   152  func isEmpty(f *ast.File, g *ast.GenDecl) bool {
   153  	if g.Doc != nil || g.Specs != nil {
   154  		return false
   155  	}
   156  
   157  	for _, c := range f.Comments {
   158  		// if there is a comment in the declaration, it is not considered empty
   159  		if g.Pos() <= c.Pos() && c.End() <= g.End() {
   160  			return false
   161  		}
   162  	}
   163  
   164  	return true
   165  }
   166  

View as plain text