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 }