@@ -3,20 +3,22 @@ package analyzer
3
3
import (
4
4
"fmt"
5
5
"go/ast"
6
+ "go/types"
6
7
"regexp"
7
- "strconv"
8
- "strings"
9
8
10
9
"golang.org/x/tools/go/analysis"
10
+ "golang.org/x/tools/go/analysis/passes/inspect"
11
+ "golang.org/x/tools/go/ast/inspector"
11
12
)
12
13
13
14
const FlagPattern = "pattern"
14
15
15
16
func New () * analysis.Analyzer {
16
17
a := & analysis.Analyzer {
17
- Name : "reassign" ,
18
- Doc : "Checks that package variables are not reassigned" ,
19
- Run : run ,
18
+ Name : "reassign" ,
19
+ Doc : "Checks that package variables are not reassigned" ,
20
+ Requires : []* analysis.Analyzer {inspect .Analyzer },
21
+ Run : run ,
20
22
}
21
23
a .Flags .String (FlagPattern , `^(Err.*|EOF)$` , "Pattern to match package variables against to prevent reassignment" )
22
24
return a
@@ -27,60 +29,56 @@ func run(pass *analysis.Pass) (interface{}, error) {
27
29
if err != nil {
28
30
return nil , fmt .Errorf ("invalid pattern: %w" , err )
29
31
}
30
- for _ , f := range pass .Files {
31
- state := & fileState {imports : make (map [string ]struct {})}
32
- ast .Inspect (f , func (node ast.Node ) bool {
33
- return inspect (pass , node , checkRE , state )
34
- })
35
- }
32
+
33
+ inspect := pass .ResultOf [inspect .Analyzer ].(* inspector.Inspector )
34
+ inspect .Preorder ([]ast.Node {(* ast .AssignStmt )(nil ), (* ast .UnaryExpr )(nil )}, func (node ast.Node ) {
35
+ switch node := node .(type ) {
36
+ case * ast.AssignStmt :
37
+ for _ , lhs := range node .Lhs {
38
+ reportImported (pass , lhs , checkRE , "reassigning" )
39
+ }
40
+ default :
41
+ // TODO(chokoswitch): Consider handling operations other than assignment on globals, for example
42
+ // taking their address.
43
+ }
44
+ })
36
45
return nil , nil
37
46
}
38
47
39
- type fileState struct {
40
- imports map [string ]struct {}
41
- }
48
+ func reportImported (pass * analysis.Pass , expr ast.Expr , checkRE * regexp.Regexp , prefix string ) {
49
+ switch x := expr .(type ) {
50
+ case * ast.SelectorExpr :
51
+ if ! checkRE .MatchString (x .Sel .Name ) {
52
+ return
53
+ }
42
54
43
- func inspect (pass * analysis.Pass , node ast.Node , checkRE * regexp.Regexp , state * fileState ) bool {
44
- if importSpec , ok := node .(* ast.ImportSpec ); ok {
45
- if importSpec .Name != nil {
46
- state .imports [importSpec .Name .Name ] = struct {}{}
47
- } else {
48
- n , err := strconv .Unquote (importSpec .Path .Value )
49
- if err != nil {
50
- return true
51
- }
52
- if idx := strings .LastIndexByte (n , '/' ); idx != - 1 {
53
- n = n [idx + 1 :]
55
+ selectIdent , ok := x .X .(* ast.Ident )
56
+ if ! ok {
57
+ return
58
+ }
59
+
60
+ if selectObj , ok := pass .TypesInfo .Uses [selectIdent ]; ok {
61
+ if pkg , ok := selectObj .(* types.PkgName ); ! ok || pkg .Imported () == pass .Pkg {
62
+ return
54
63
}
55
- state .imports [n ] = struct {}{}
56
64
}
57
- return true
58
- }
59
65
60
- assignStmt , ok := node .(* ast.AssignStmt )
61
- if ! ok {
62
- return true
63
- }
66
+ pass .Reportf (expr .Pos (), "%s variable %s in other package %s" , prefix , x .Sel .Name , selectIdent .Name )
64
67
65
- for _ , lhs := range assignStmt . Lhs {
66
- selector , ok := lhs .( * ast. SelectorExpr )
68
+ case * ast. Ident :
69
+ use , ok := pass . TypesInfo . Uses [ x ].( * types. Var )
67
70
if ! ok {
68
- return true
71
+ return
69
72
}
70
73
71
- if ! checkRE . MatchString ( selector . Sel . Name ) {
72
- return true
74
+ if use . Pkg () == pass . Pkg {
75
+ return
73
76
}
74
77
75
- selectIdent , ok := selector .X .(* ast.Ident )
76
- if ! ok {
77
- return true
78
+ if ! checkRE .MatchString (x .Name ) {
79
+ return
78
80
}
79
81
80
- if _ , ok := state .imports [selectIdent .Name ]; ok {
81
- pass .Reportf (node .Pos (), "reassigning variable %s in other package %s" , selector .Sel .Name , selectIdent .Name )
82
- }
82
+ pass .Reportf (expr .Pos (), "%s variable %s from other package %s" , prefix , x .Name , use .Pkg ().Path ())
83
83
}
84
-
85
- return true
86
84
}
0 commit comments