github.com/ltltlt/go-source-code@v0.0.0-20190830023027-95be009773aa/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 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 }