1 // Copyright 2020 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.
5 // Package simplifycompositelit defines an Analyzer that simplifies composite literals.
6 // https://github.com/golang/go/blob/master/src/cmd/gofmt/simplify.go
7 // https://golang.org/cmd/gofmt/#hdr-The_simplify_command
8 package simplifycompositelit
18 "golang.org/x/tools/go/analysis"
19 "golang.org/x/tools/go/analysis/passes/inspect"
20 "golang.org/x/tools/go/ast/inspector"
23 const Doc = `check for composite literal simplifications
25 An array, slice, or map composite literal of the form:
27 will be simplified to:
30 This is one of the simplifications that "gofmt -s" applies.`
32 var Analyzer = &analysis.Analyzer{
33 Name: "simplifycompositelit",
35 Requires: []*analysis.Analyzer{inspect.Analyzer},
39 func run(pass *analysis.Pass) (interface{}, error) {
40 inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
41 nodeFilter := []ast.Node{(*ast.CompositeLit)(nil)}
42 inspect.Preorder(nodeFilter, func(n ast.Node) {
43 expr := n.(*ast.CompositeLit)
46 var keyType, eltType ast.Expr
47 switch typ := outer.Type.(type) {
58 var ktyp reflect.Value
60 ktyp = reflect.ValueOf(keyType)
62 typ := reflect.ValueOf(eltType)
63 for _, x := range outer.Elts {
64 // look at value of indexed/named elements
65 if t, ok := x.(*ast.KeyValueExpr); ok {
67 simplifyLiteral(pass, ktyp, keyType, t.Key)
71 simplifyLiteral(pass, typ, eltType, x)
77 func simplifyLiteral(pass *analysis.Pass, typ reflect.Value, astType, x ast.Expr) {
78 // if the element is a composite literal and its literal type
79 // matches the outer literal's element type exactly, the inner
80 // literal type may be omitted
81 if inner, ok := x.(*ast.CompositeLit); ok && match(typ, reflect.ValueOf(inner.Type)) {
83 printer.Fprint(&b, pass.Fset, inner.Type)
84 createDiagnostic(pass, inner.Type.Pos(), inner.Type.End(), b.String())
86 // if the outer literal's element type is a pointer type *T
87 // and the element is & of a composite literal of type T,
88 // the inner &T may be omitted.
89 if ptr, ok := astType.(*ast.StarExpr); ok {
90 if addr, ok := x.(*ast.UnaryExpr); ok && addr.Op == token.AND {
91 if inner, ok := addr.X.(*ast.CompositeLit); ok {
92 if match(reflect.ValueOf(ptr.X), reflect.ValueOf(inner.Type)) {
94 printer.Fprint(&b, pass.Fset, inner.Type)
95 // Account for the & by subtracting 1 from typ.Pos().
96 createDiagnostic(pass, inner.Type.Pos()-1, inner.Type.End(), "&"+b.String())
103 func createDiagnostic(pass *analysis.Pass, start, end token.Pos, typ string) {
104 pass.Report(analysis.Diagnostic{
107 Message: "redundant type from array, slice, or map composite literal",
108 SuggestedFixes: []analysis.SuggestedFix{{
109 Message: fmt.Sprintf("Remove '%s'", typ),
110 TextEdits: []analysis.TextEdit{{
119 // match reports whether pattern matches val,
120 // recording wildcard submatches in m.
121 // If m == nil, match checks whether pattern == val.
122 // from https://github.com/golang/go/blob/26154f31ad6c801d8bad5ef58df1e9263c6beec7/src/cmd/gofmt/rewrite.go#L160
123 func match(pattern, val reflect.Value) bool {
124 // Otherwise, pattern and val must match recursively.
125 if !pattern.IsValid() || !val.IsValid() {
126 return !pattern.IsValid() && !val.IsValid()
128 if pattern.Type() != val.Type() {
133 switch pattern.Type() {
135 // For identifiers, only the names need to match
136 // (and none of the other *ast.Object information).
137 // This is a common case, handle it all here instead
138 // of recursing down any further via reflection.
139 p := pattern.Interface().(*ast.Ident)
140 v := val.Interface().(*ast.Ident)
141 return p == nil && v == nil || p != nil && v != nil && p.Name == v.Name
142 case objectPtrType, positionType:
143 // object pointers and token positions always match
146 // For calls, the Ellipsis fields (token.Position) must
147 // match since that is how f(x) and f(x...) are different.
148 // Check them here but fall through for the remaining fields.
149 p := pattern.Interface().(*ast.CallExpr)
150 v := val.Interface().(*ast.CallExpr)
151 if p.Ellipsis.IsValid() != v.Ellipsis.IsValid() {
156 p := reflect.Indirect(pattern)
157 v := reflect.Indirect(val)
158 if !p.IsValid() || !v.IsValid() {
159 return !p.IsValid() && !v.IsValid()
164 if p.Len() != v.Len() {
167 for i := 0; i < p.Len(); i++ {
168 if !match(p.Index(i), v.Index(i)) {
175 for i := 0; i < p.NumField(); i++ {
176 if !match(p.Field(i), v.Field(i)) {
182 case reflect.Interface:
183 return match(p.Elem(), v.Elem())
186 // Handle token integers, etc.
187 return p.Interface() == v.Interface()
190 // Values/types for special cases.
192 identType = reflect.TypeOf((*ast.Ident)(nil))
193 objectPtrType = reflect.TypeOf((*ast.Object)(nil))
194 positionType = reflect.TypeOf(token.NoPos)
195 callExprType = reflect.TypeOf((*ast.CallExpr)(nil))