Skip to content

Commit 9bbf122

Browse files
committed
Import some code from main Go repository @ c55a170083dc;9u
1 parent 45d8e0b commit 9bbf122

File tree

2 files changed

+282
-0
lines changed

2 files changed

+282
-0
lines changed

internal/imports/gofmt_rewrite.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package imports
2+
3+
import (
4+
"go/ast"
5+
"go/token"
6+
"reflect"
7+
"unicode"
8+
"unicode/utf8"
9+
)
10+
11+
// Values/types for special cases.
12+
var (
13+
objectPtrNil = reflect.ValueOf((*ast.Object)(nil))
14+
scopePtrNil = reflect.ValueOf((*ast.Scope)(nil))
15+
16+
identType = reflect.TypeOf((*ast.Ident)(nil))
17+
objectPtrType = reflect.TypeOf((*ast.Object)(nil))
18+
positionType = reflect.TypeOf(token.NoPos)
19+
callExprType = reflect.TypeOf((*ast.CallExpr)(nil))
20+
scopePtrType = reflect.TypeOf((*ast.Scope)(nil))
21+
)
22+
23+
func isWildcard(s string) bool {
24+
rune, size := utf8.DecodeRuneInString(s)
25+
return size == len(s) && unicode.IsLower(rune)
26+
}
27+
28+
// match reports whether pattern matches val,
29+
// recording wildcard submatches in m.
30+
// If m == nil, match checks whether pattern == val.
31+
func match(m map[string]reflect.Value, pattern, val reflect.Value) bool {
32+
// Wildcard matches any expression. If it appears multiple
33+
// times in the pattern, it must match the same expression
34+
// each time.
35+
if m != nil && pattern.IsValid() && pattern.Type() == identType {
36+
name := pattern.Interface().(*ast.Ident).Name
37+
if isWildcard(name) && val.IsValid() {
38+
// wildcards only match valid (non-nil) expressions.
39+
if _, ok := val.Interface().(ast.Expr); ok && !val.IsNil() {
40+
if old, ok := m[name]; ok {
41+
return match(nil, old, val)
42+
}
43+
m[name] = val
44+
return true
45+
}
46+
}
47+
}
48+
49+
// Otherwise, pattern and val must match recursively.
50+
if !pattern.IsValid() || !val.IsValid() {
51+
return !pattern.IsValid() && !val.IsValid()
52+
}
53+
if pattern.Type() != val.Type() {
54+
return false
55+
}
56+
57+
// Special cases.
58+
switch pattern.Type() {
59+
case identType:
60+
// For identifiers, only the names need to match
61+
// (and none of the other *ast.Object information).
62+
// This is a common case, handle it all here instead
63+
// of recursing down any further via reflection.
64+
p := pattern.Interface().(*ast.Ident)
65+
v := val.Interface().(*ast.Ident)
66+
return p == nil && v == nil || p != nil && v != nil && p.Name == v.Name
67+
case objectPtrType, positionType:
68+
// object pointers and token positions always match
69+
return true
70+
case callExprType:
71+
// For calls, the Ellipsis fields (token.Pos) must
72+
// match since that is how f(x) and f(x...) are different.
73+
// Check them here but fall through for the remaining fields.
74+
p := pattern.Interface().(*ast.CallExpr)
75+
v := val.Interface().(*ast.CallExpr)
76+
if p.Ellipsis.IsValid() != v.Ellipsis.IsValid() {
77+
return false
78+
}
79+
}
80+
81+
p := reflect.Indirect(pattern)
82+
v := reflect.Indirect(val)
83+
if !p.IsValid() || !v.IsValid() {
84+
return !p.IsValid() && !v.IsValid()
85+
}
86+
87+
switch p.Kind() {
88+
case reflect.Slice:
89+
if p.Len() != v.Len() {
90+
return false
91+
}
92+
for i := 0; i < p.Len(); i++ {
93+
if !match(m, p.Index(i), v.Index(i)) {
94+
return false
95+
}
96+
}
97+
return true
98+
99+
case reflect.Struct:
100+
for i := 0; i < p.NumField(); i++ {
101+
if !match(m, p.Field(i), v.Field(i)) {
102+
return false
103+
}
104+
}
105+
return true
106+
107+
case reflect.Interface:
108+
return match(m, p.Elem(), v.Elem())
109+
}
110+
111+
// Handle token integers, etc.
112+
return p.Interface() == v.Interface()
113+
}

internal/imports/gofmt_simplify.go

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
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 imports
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
57+
// the same package. However, this is extremely unlikely and so far
58+
// (April 2022, after years of supporting this rewrite feature)
59+
// has never come up, so let's keep it working as is (see also #15153).
60+
//
61+
// Also note that this code used to use go/ast's object tracking,
62+
// which was removed in exchange for go/parser.Mode.SkipObjectResolution.
63+
// False positives are extremely unlikely as described above,
64+
// and go/ast's object tracking is incomplete in any case.
65+
if n.Max != nil {
66+
// - 3-index slices always require the 2nd and 3rd index
67+
break
68+
}
69+
if s, _ := n.X.(*ast.Ident); s != nil {
70+
// the array/slice object is a single identifier
71+
if call, _ := n.High.(*ast.CallExpr); call != nil && len(call.Args) == 1 && !call.Ellipsis.IsValid() {
72+
// the high expression is a function call with a single argument
73+
if fun, _ := call.Fun.(*ast.Ident); fun != nil && fun.Name == "len" {
74+
// the function called is "len"
75+
if arg, _ := call.Args[0].(*ast.Ident); arg != nil && arg.Name == s.Name {
76+
// the len argument is the array/slice object
77+
n.High = nil
78+
}
79+
}
80+
}
81+
}
82+
// Note: We could also simplify slice expressions of the form s[0:b] to s[:b]
83+
// but we leave them as is since sometimes we want to be very explicit
84+
// about the lower bound.
85+
// An example where the 0 helps:
86+
// x, y, z := b[0:2], b[2:4], b[4:6]
87+
// An example where it does not:
88+
// x, y := b[:n], b[n:]
89+
90+
case *ast.RangeStmt:
91+
// - a range of the form: for x, _ = range v {...}
92+
// can be simplified to: for x = range v {...}
93+
// - a range of the form: for _ = range v {...}
94+
// can be simplified to: for range v {...}
95+
if isBlank(n.Value) {
96+
n.Value = nil
97+
}
98+
if isBlank(n.Key) && n.Value == nil {
99+
n.Key = nil
100+
}
101+
}
102+
103+
return s
104+
}
105+
106+
func (s simplifier) simplifyLiteral(typ reflect.Value, astType, x ast.Expr, px *ast.Expr) {
107+
ast.Walk(s, x) // simplify x
108+
109+
// if the element is a composite literal and its literal type
110+
// matches the outer literal's element type exactly, the inner
111+
// literal type may be omitted
112+
if inner, ok := x.(*ast.CompositeLit); ok {
113+
if match(nil, typ, reflect.ValueOf(inner.Type)) {
114+
inner.Type = nil
115+
}
116+
}
117+
// if the outer literal's element type is a pointer type *T
118+
// and the element is & of a composite literal of type T,
119+
// the inner &T may be omitted.
120+
if ptr, ok := astType.(*ast.StarExpr); ok {
121+
if addr, ok := x.(*ast.UnaryExpr); ok && addr.Op == token.AND {
122+
if inner, ok := addr.X.(*ast.CompositeLit); ok {
123+
if match(nil, reflect.ValueOf(ptr.X), reflect.ValueOf(inner.Type)) {
124+
inner.Type = nil // drop T
125+
*px = inner // drop &
126+
}
127+
}
128+
}
129+
}
130+
}
131+
132+
func isBlank(x ast.Expr) bool {
133+
ident, ok := x.(*ast.Ident)
134+
return ok && ident.Name == "_"
135+
}
136+
137+
func simplify(f *ast.File) {
138+
// remove empty declarations such as "const ()", etc
139+
removeEmptyDeclGroups(f)
140+
141+
var s simplifier
142+
ast.Walk(s, f)
143+
}
144+
145+
func removeEmptyDeclGroups(f *ast.File) {
146+
i := 0
147+
for _, d := range f.Decls {
148+
if g, ok := d.(*ast.GenDecl); !ok || !isEmpty(f, g) {
149+
f.Decls[i] = d
150+
i++
151+
}
152+
}
153+
f.Decls = f.Decls[:i]
154+
}
155+
156+
func isEmpty(f *ast.File, g *ast.GenDecl) bool {
157+
if g.Doc != nil || g.Specs != nil {
158+
return false
159+
}
160+
161+
for _, c := range f.Comments {
162+
// if there is a comment in the declaration, it is not considered empty
163+
if g.Pos() <= c.Pos() && c.End() <= g.End() {
164+
return false
165+
}
166+
}
167+
168+
return true
169+
}

0 commit comments

Comments
 (0)