OLD | NEW |
1 // Copyright 2010 The Go Authors. All rights reserved. | 1 // Copyright 2010 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 package main | 5 package main |
6 | 6 |
7 import ( | 7 import ( |
8 "go/ast" | 8 "go/ast" |
9 "go/token" | 9 "go/token" |
10 "reflect" | 10 "reflect" |
11 ) | 11 ) |
12 | 12 |
13 type simplifier struct{} | 13 type simplifier struct { |
| 14 » hasDotImport bool // package file contains: import . "some/import/path" |
| 15 } |
14 | 16 |
15 func (s *simplifier) Visit(node ast.Node) ast.Visitor { | 17 func (s *simplifier) Visit(node ast.Node) ast.Visitor { |
16 switch n := node.(type) { | 18 switch n := node.(type) { |
17 case *ast.CompositeLit: | 19 case *ast.CompositeLit: |
18 // array, slice, and map composite literals may be simplified | 20 // array, slice, and map composite literals may be simplified |
19 outer := n | 21 outer := n |
20 var eltType ast.Expr | 22 var eltType ast.Expr |
21 switch typ := outer.Type.(type) { | 23 switch typ := outer.Type.(type) { |
22 case *ast.ArrayType: | 24 case *ast.ArrayType: |
23 eltType = typ.Elt | 25 eltType = typ.Elt |
24 case *ast.MapType: | 26 case *ast.MapType: |
25 eltType = typ.Value | 27 eltType = typ.Value |
26 } | 28 } |
27 | 29 |
28 if eltType != nil { | 30 if eltType != nil { |
29 typ := reflect.ValueOf(eltType) | 31 typ := reflect.ValueOf(eltType) |
30 for i, x := range outer.Elts { | 32 for i, x := range outer.Elts { |
31 px := &outer.Elts[i] | 33 px := &outer.Elts[i] |
32 // look at value of indexed/named elements | 34 // look at value of indexed/named elements |
33 if t, ok := x.(*ast.KeyValueExpr); ok { | 35 if t, ok := x.(*ast.KeyValueExpr); ok { |
34 x = t.Value | 36 x = t.Value |
35 px = &t.Value | 37 px = &t.Value |
36 } | 38 } |
37 » » » » simplify(x) | 39 » » » » ast.Walk(s, x) // simplify x |
38 // if the element is a composite literal and its
literal type | 40 // if the element is a composite literal and its
literal type |
39 // matches the outer literal's element type exac
tly, the inner | 41 // matches the outer literal's element type exac
tly, the inner |
40 // literal type may be omitted | 42 // literal type may be omitted |
41 if inner, ok := x.(*ast.CompositeLit); ok { | 43 if inner, ok := x.(*ast.CompositeLit); ok { |
42 if match(nil, typ, reflect.ValueOf(inner
.Type)) { | 44 if match(nil, typ, reflect.ValueOf(inner
.Type)) { |
43 inner.Type = nil | 45 inner.Type = nil |
44 } | 46 } |
45 } | 47 } |
46 // if the outer literal's element type is a poin
ter type *T | 48 // if the outer literal's element type is a poin
ter type *T |
47 // and the element is & of a composite literal o
f type T, | 49 // and the element is & of a composite literal o
f type T, |
48 // the inner &T may be omitted. | 50 // the inner &T may be omitted. |
49 if ptr, ok := eltType.(*ast.StarExpr); ok { | 51 if ptr, ok := eltType.(*ast.StarExpr); ok { |
50 if addr, ok := x.(*ast.UnaryExpr); ok &&
addr.Op == token.AND { | 52 if addr, ok := x.(*ast.UnaryExpr); ok &&
addr.Op == token.AND { |
51 if inner, ok := addr.X.(*ast.Com
positeLit); ok { | 53 if inner, ok := addr.X.(*ast.Com
positeLit); ok { |
52 if match(nil, reflect.Va
lueOf(ptr.X), reflect.ValueOf(inner.Type)) { | 54 if match(nil, reflect.Va
lueOf(ptr.X), reflect.ValueOf(inner.Type)) { |
53 inner.Type = nil
// drop T | 55 inner.Type = nil
// drop T |
54 *px = inner
// drop & | 56 *px = inner
// drop & |
55 } | 57 } |
56 } | 58 } |
57 } | 59 } |
58 } | 60 } |
59 } | 61 } |
60 | 62 |
61 // node was simplified - stop walk (there are no subnode
s to simplify) | 63 // node was simplified - stop walk (there are no subnode
s to simplify) |
62 return nil | 64 return nil |
63 } | 65 } |
64 | 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" identifiers 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 identifi
er |
| 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 i
s not locally defined; |
| 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 v
ery explicit |
| 92 // about the lower bound. |
| 93 |
65 case *ast.RangeStmt: | 94 case *ast.RangeStmt: |
66 » » // range of the form: for x, _ = range v {...} | 95 » » // a range of the form: for x, _ = range v {...} |
67 // can be simplified to: for x = range v {...} | 96 // can be simplified to: for x = range v {...} |
68 » » if n.Value != nil { | 97 » » if ident, _ := n.Value.(*ast.Ident); ident != nil && ident.Name
== "_" { |
69 » » » if ident, ok := n.Value.(*ast.Ident); ok && ident.Name =
= "_" { | 98 » » » n.Value = nil |
70 » » » » n.Value = nil | |
71 » » » } | |
72 } | 99 } |
73 } | 100 } |
74 | 101 |
75 return s | 102 return s |
76 } | 103 } |
77 | 104 |
78 func simplify(node ast.Node) { | 105 func simplify(f *ast.File) { |
79 var s simplifier | 106 var s simplifier |
80 » ast.Walk(&s, node) | 107 |
| 108 » // determine if f contains dot imports |
| 109 » for _, imp := range f.Imports { |
| 110 » » if imp.Name != nil && imp.Name.Name == "." { |
| 111 » » » s.hasDotImport = true |
| 112 » » » break |
| 113 » » } |
| 114 » } |
| 115 |
| 116 » ast.Walk(&s, f) |
81 } | 117 } |
OLD | NEW |