github.com/spotify/syslog-redirector-golang@v0.0.0-20140320174030-4859f03d829a/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 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 s.hasDotImport { 72 // if dot imports are present, we cannot be certain that an 73 // unresolved "len" identifier refers to the predefined len() 74 break 75 } 76 if s, _ := n.X.(*ast.Ident); s != nil && s.Obj != nil { 77 // the array/slice object is a single, resolved identifier 78 if call, _ := n.High.(*ast.CallExpr); call != nil && len(call.Args) == 1 && !call.Ellipsis.IsValid() { 79 // the high expression is a function call with a single argument 80 if fun, _ := call.Fun.(*ast.Ident); fun != nil && fun.Name == "len" && fun.Obj == nil { 81 // the function called is "len" and it is not locally defined; and 82 // because we don't have dot imports, it must be the predefined len() 83 if arg, _ := call.Args[0].(*ast.Ident); arg != nil && arg.Obj == s.Obj { 84 // the len argument is the array/slice object 85 n.High = nil 86 } 87 } 88 } 89 } 90 // Note: We could also simplify slice expressions of the form s[0:b] to s[:b] 91 // but we leave them as is since sometimes we want to be very explicit 92 // about the lower bound. 93 // An example where the 0 helps: 94 // x, y, z := b[0:2], b[2:4], b[4:6] 95 // An example where it does not: 96 // x, y := b[:n], b[n:] 97 98 case *ast.RangeStmt: 99 // a range of the form: for x, _ = range v {...} 100 // can be simplified to: for x = range v {...} 101 if ident, _ := n.Value.(*ast.Ident); ident != nil && ident.Name == "_" { 102 n.Value = nil 103 } 104 } 105 106 return s 107 } 108 109 func simplify(f *ast.File) { 110 var s simplifier 111 112 // determine if f contains dot imports 113 for _, imp := range f.Imports { 114 if imp.Name != nil && imp.Name.Name == "." { 115 s.hasDotImport = true 116 break 117 } 118 } 119 120 ast.Walk(&s, f) 121 }