package s1040 import ( "fmt" "go/ast" "go/types" "honnef.co/go/tools/analysis/code" "honnef.co/go/tools/analysis/facts/generated" "honnef.co/go/tools/analysis/lint" "honnef.co/go/tools/analysis/report" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/inspect" ) var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ Analyzer: &analysis.Analyzer{ Name: "S1040", Run: run, Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer}, }, Doc: &lint.RawDocumentation{ Title: "Type assertion to current type", Text: `The type assertion \'x.(SomeInterface)\', when \'x\' already has type \'SomeInterface\', can only fail if \'x\' is nil. Usually, this is left-over code from when \'x\' had a different type and you can safely delete the type assertion. If you want to check that \'x\' is not nil, consider being explicit and using an actual \'if x == nil\' comparison instead of relying on the type assertion panicking.`, Since: "2021.1", // MergeIfAll because x might have different types under different build tags. // You shouldn't write code like that… MergeIf: lint.MergeIfAll, }, }) var Analyzer = SCAnalyzer.Analyzer func run(pass *analysis.Pass) (interface{}, error) { fn := func(node ast.Node) { expr := node.(*ast.TypeAssertExpr) if expr.Type == nil { // skip type switches // // TODO(dh): we could flag type switches, too, when a case // statement has the same type as expr.X – however, // depending on the location of that case, it might behave // identically to a default branch. we need to think // carefully about the instances we want to flag. We also // have to take nil interface values into consideration. // // It might make more sense to extend SA4020 to handle // this. return } t1 := pass.TypesInfo.TypeOf(expr.Type) t2 := pass.TypesInfo.TypeOf(expr.X) if types.IsInterface(t1) && types.Identical(t1, t2) { report.Report(pass, expr, fmt.Sprintf("type assertion to the same type: %s already has type %s", report.Render(pass, expr.X), report.Render(pass, expr.Type)), report.FilterGenerated()) } } // TODO(dh): add suggested fixes. we need different fixes depending on the context: // - assignment with 1 or 2 lhs // - assignment to blank identifiers (as the first, second or both lhs) // - initializers in if statements, with the same variations as above code.Preorder(pass, fn, (*ast.TypeAssertExpr)(nil)) return nil, nil }