github.com/sbinet/go@v0.0.0-20160827155028-54d7de7dd62b/src/cmd/gofmt/simplify.go (about)

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