GoPLS Viewer

Home|gopls/go/analysis/passes/composite/composite.go
1// Copyright 2012 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 composite defines an Analyzer that checks for unkeyed
6// composite literals.
7package composite
8
9import (
10    "fmt"
11    "go/ast"
12    "go/types"
13    "strings"
14
15    "golang.org/x/tools/go/analysis"
16    "golang.org/x/tools/go/analysis/passes/inspect"
17    "golang.org/x/tools/go/ast/inspector"
18    "golang.org/x/tools/internal/typeparams"
19)
20
21const Doc = `check for unkeyed composite literals
22
23This analyzer reports a diagnostic for composite literals of struct
24types imported from another package that do not use the field-keyed
25syntax. Such literals are fragile because the addition of a new field
26(even if unexported) to the struct will cause compilation to fail.
27
28As an example,
29
30    err = &net.DNSConfigError{err}
31
32should be replaced by:
33
34    err = &net.DNSConfigError{Err: err}
35`
36
37var Analyzer = &analysis.Analyzer{
38    Name:             "composites",
39    Doc:              Doc,
40    Requires:         []*analysis.Analyzer{inspect.Analyzer},
41    RunDespiteErrorstrue,
42    Run:              run,
43}
44
45var whitelist = true
46
47func init() {
48    Analyzer.Flags.BoolVar(&whitelist"whitelist"whitelist"use composite white list; for testing only")
49}
50
51// runUnkeyedLiteral checks if a composite literal is a struct literal with
52// unkeyed fields.
53func run(pass *analysis.Pass) (interface{}, error) {
54    inspect := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
55
56    nodeFilter := []ast.Node{
57        (*ast.CompositeLit)(nil),
58    }
59    inspect.Preorder(nodeFilter, func(n ast.Node) {
60        cl := n.(*ast.CompositeLit)
61
62        typ := pass.TypesInfo.Types[cl].Type
63        if typ == nil {
64            // cannot determine composite literals' type, skip it
65            return
66        }
67        typeName := typ.String()
68        if whitelist && unkeyedLiteral[typeName] {
69            // skip whitelisted types
70            return
71        }
72        var structuralTypes []types.Type
73        switch typ := typ.(type) {
74        case *typeparams.TypeParam:
75            termserr := typeparams.StructuralTerms(typ)
76            if err != nil {
77                return // invalid type
78            }
79            for _term := range terms {
80                structuralTypes = append(structuralTypesterm.Type())
81            }
82        default:
83            structuralTypes = append(structuralTypestyp)
84        }
85        for _typ := range structuralTypes {
86            under := deref(typ.Underlying())
87            strctok := under.(*types.Struct)
88            if !ok {
89                // skip non-struct composite literals
90                continue
91            }
92            if isLocalType(passtyp) {
93                // allow unkeyed locally defined composite literal
94                continue
95            }
96
97            // check if the struct contains an unkeyed field
98            allKeyValue := true
99            var suggestedFixAvailable = len(cl.Elts) == strct.NumFields()
100            var missingKeys []analysis.TextEdit
101            for ie := range cl.Elts {
102                if _ok := e.(*ast.KeyValueExpr); !ok {
103                    allKeyValue = false
104                    if i >= strct.NumFields() {
105                        break
106                    }
107                    field := strct.Field(i)
108                    if !field.Exported() {
109                        // Adding unexported field names for structs not defined
110                        // locally will not work.
111                        suggestedFixAvailable = false
112                        break
113                    }
114                    missingKeys = append(missingKeysanalysis.TextEdit{
115                        Pos:     e.Pos(),
116                        End:     e.Pos(),
117                        NewText: []byte(fmt.Sprintf("%s: "field.Name())),
118                    })
119                }
120            }
121            if allKeyValue {
122                // all the struct fields are keyed
123                continue
124            }
125
126            diag := analysis.Diagnostic{
127                Pos:     cl.Pos(),
128                End:     cl.End(),
129                Messagefmt.Sprintf("%s struct literal uses unkeyed fields"typeName),
130            }
131            if suggestedFixAvailable {
132                diag.SuggestedFixes = []analysis.SuggestedFix{{
133                    Message:   "Add field names to struct literal",
134                    TextEditsmissingKeys,
135                }}
136            }
137            pass.Report(diag)
138            return
139        }
140    })
141    return nilnil
142}
143
144func deref(typ types.Typetypes.Type {
145    for {
146        ptrok := typ.(*types.Pointer)
147        if !ok {
148            break
149        }
150        typ = ptr.Elem().Underlying()
151    }
152    return typ
153}
154
155func isLocalType(pass *analysis.Passtyp types.Typebool {
156    switch x := typ.(type) {
157    case *types.Struct:
158        // struct literals are local types
159        return true
160    case *types.Pointer:
161        return isLocalType(passx.Elem())
162    case *types.Named:
163        // names in package foo are local to foo_test too
164        return strings.TrimSuffix(x.Obj().Pkg().Path(), "_test") == strings.TrimSuffix(pass.Pkg.Path(), "_test")
165    case *typeparams.TypeParam:
166        return strings.TrimSuffix(x.Obj().Pkg().Path(), "_test") == strings.TrimSuffix(pass.Pkg.Path(), "_test")
167    }
168    return false
169}
170
MembersX
analysis
run.BlockStmt.BlockStmt.err
run.BlockStmt.BlockStmt.RangeStmt_2065.term
run.BlockStmt.RangeStmt_2225.BlockStmt.under
isLocalType.typ
run.BlockStmt.structuralTypes
run.BlockStmt.RangeStmt_2225.BlockStmt.RangeStmt_2696.e
run.BlockStmt.RangeStmt_2225.BlockStmt.RangeStmt_2696.BlockStmt.BlockStmt.field
ast
inspector
init
run.nodeFilter
run.BlockStmt.typeName
run.BlockStmt.RangeStmt_2225.BlockStmt.allKeyValue
run.BlockStmt.RangeStmt_2225.BlockStmt.missingKeys
run.BlockStmt.RangeStmt_2225.BlockStmt.RangeStmt_2696.i
isLocalType
run.pass
run.BlockStmt.BlockStmt.terms
deref.typ
strings
Doc
isLocalType.pass
types
inspect
whitelist
run
run.BlockStmt.typ
fmt
run.BlockStmt.RangeStmt_2225.typ
run.BlockStmt.RangeStmt_2225.BlockStmt.diag
deref
typeparams
Members
X