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