1 package s1040
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 13 "golang.org/x/tools/go/analysis"
14 "golang.org/x/tools/go/analysis/passes/inspect"
15 )
16 17 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
18 Analyzer: &analysis.Analyzer{
19 Name: "S1040",
20 Run: run,
21 Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},
22 },
23 Doc: &lint.RawDocumentation{
24 Title: "Type assertion to current type",
25 Text: `The type assertion \'x.(SomeInterface)\', when \'x\' already has type
26 \'SomeInterface\', can only fail if \'x\' is nil. Usually, this is
27 left-over code from when \'x\' had a different type and you can safely
28 delete the type assertion. If you want to check that \'x\' is not nil,
29 consider being explicit and using an actual \'if x == nil\' comparison
30 instead of relying on the type assertion panicking.`,
31 Since: "2021.1",
32 // MergeIfAll because x might have different types under different build tags.
33 // You shouldn't write code like that…
34 MergeIf: lint.MergeIfAll,
35 },
36 })
37 38 var Analyzer = SCAnalyzer.Analyzer
39 40 func run(pass *analysis.Pass) (interface{}, error) {
41 fn := func(node ast.Node) {
42 expr := node.(*ast.TypeAssertExpr)
43 if expr.Type == nil {
44 // skip type switches
45 //
46 // TODO(dh): we could flag type switches, too, when a case
47 // statement has the same type as expr.X – however,
48 // depending on the location of that case, it might behave
49 // identically to a default branch. we need to think
50 // carefully about the instances we want to flag. We also
51 // have to take nil interface values into consideration.
52 //
53 // It might make more sense to extend SA4020 to handle
54 // this.
55 return
56 }
57 t1 := pass.TypesInfo.TypeOf(expr.Type)
58 t2 := pass.TypesInfo.TypeOf(expr.X)
59 if types.IsInterface(t1) && types.Identical(t1, t2) {
60 report.Report(pass, expr,
61 fmt.Sprintf("type assertion to the same type: %s already has type %s", report.Render(pass, expr.X), report.Render(pass, expr.Type)),
62 report.FilterGenerated())
63 }
64 }
65 66 // TODO(dh): add suggested fixes. we need different fixes depending on the context:
67 // - assignment with 1 or 2 lhs
68 // - assignment to blank identifiers (as the first, second or both lhs)
69 // - initializers in if statements, with the same variations as above
70 71 code.Preorder(pass, fn, (*ast.TypeAssertExpr)(nil))
72 return nil, nil
73 }
74