s1020.go raw
1 package s1020
2
3 import (
4 "fmt"
5 "go/ast"
6 "go/types"
7
8 "honnef.co/go/tools/analysis/code"
9 "honnef.co/go/tools/analysis/facts/generated"
10 "honnef.co/go/tools/analysis/lint"
11 "honnef.co/go/tools/analysis/report"
12 "honnef.co/go/tools/pattern"
13
14 "golang.org/x/tools/go/analysis"
15 "golang.org/x/tools/go/analysis/passes/inspect"
16 )
17
18 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
19 Analyzer: &analysis.Analyzer{
20 Name: "S1020",
21 Run: run,
22 Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},
23 },
24 Doc: &lint.RawDocumentation{
25 Title: `Omit redundant nil check in type assertion`,
26 Before: `if _, ok := i.(T); ok && i != nil {}`,
27 After: `if _, ok := i.(T); ok {}`,
28 Since: "2017.1",
29 MergeIf: lint.MergeIfAny,
30 },
31 })
32
33 var Analyzer = SCAnalyzer.Analyzer
34
35 var (
36 checkAssertNotNilFn1Q = pattern.MustParse(`
37 (IfStmt
38 (AssignStmt [(Ident "_") ok@(Object _)] _ [(TypeAssertExpr assert@(Object _) _)])
39 (Or
40 (BinaryExpr ok "&&" (BinaryExpr assert "!=" (Builtin "nil")))
41 (BinaryExpr (BinaryExpr assert "!=" (Builtin "nil")) "&&" ok))
42 _
43 _)`)
44 checkAssertNotNilFn2Q = pattern.MustParse(`
45 (IfStmt
46 nil
47 (BinaryExpr lhs@(Object _) "!=" (Builtin "nil"))
48 [
49 ifstmt@(IfStmt
50 (AssignStmt [(Ident "_") ok@(Object _)] _ [(TypeAssertExpr lhs _)])
51 ok
52 _
53 nil)
54 ]
55 nil)`)
56 )
57
58 func run(pass *analysis.Pass) (interface{}, error) {
59 fn1 := func(node ast.Node) {
60 m, ok := code.Match(pass, checkAssertNotNilFn1Q, node)
61 if !ok {
62 return
63 }
64 assert := m.State["assert"].(types.Object)
65 assign := m.State["ok"].(types.Object)
66 report.Report(pass, node, fmt.Sprintf("when %s is true, %s can't be nil", assign.Name(), assert.Name()),
67 report.ShortRange(),
68 report.FilterGenerated())
69 }
70 fn2 := func(node ast.Node) {
71 m, ok := code.Match(pass, checkAssertNotNilFn2Q, node)
72 if !ok {
73 return
74 }
75 ifstmt := m.State["ifstmt"].(*ast.IfStmt)
76 lhs := m.State["lhs"].(types.Object)
77 assignIdent := m.State["ok"].(types.Object)
78 report.Report(pass, ifstmt, fmt.Sprintf("when %s is true, %s can't be nil", assignIdent.Name(), lhs.Name()),
79 report.ShortRange(),
80 report.FilterGenerated())
81 }
82 // OPT(dh): merge fn1 and fn2
83 code.Preorder(pass, fn1, (*ast.IfStmt)(nil))
84 code.Preorder(pass, fn2, (*ast.IfStmt)(nil))
85 return nil, nil
86 }
87