src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

s1034.go (3385B)


      1 package s1034
      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/edit"
     10 	"honnef.co/go/tools/analysis/facts/generated"
     11 	"honnef.co/go/tools/analysis/lint"
     12 	"honnef.co/go/tools/analysis/report"
     13 	"honnef.co/go/tools/pattern"
     14 
     15 	"golang.org/x/tools/go/analysis"
     16 )
     17 
     18 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
     19 	Analyzer: &analysis.Analyzer{
     20 		Name:     "S1034",
     21 		Run:      run,
     22 		Requires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...),
     23 	},
     24 	Doc: &lint.RawDocumentation{
     25 		Title:   `Use result of type assertion to simplify cases`,
     26 		Since:   "2019.2",
     27 		MergeIf: lint.MergeIfAny,
     28 	},
     29 })
     30 
     31 var Analyzer = SCAnalyzer.Analyzer
     32 
     33 var (
     34 	checkSimplifyTypeSwitchQ = pattern.MustParse(`
     35 		(TypeSwitchStmt
     36 			nil
     37 			expr@(TypeAssertExpr ident@(Ident _) _)
     38 			body)`)
     39 	checkSimplifyTypeSwitchR = pattern.MustParse(`(AssignStmt ident ":=" expr)`)
     40 )
     41 
     42 func run(pass *analysis.Pass) (any, error) {
     43 	for node, m := range code.Matches(pass, checkSimplifyTypeSwitchQ) {
     44 		stmt := node.(*ast.TypeSwitchStmt)
     45 		expr := m.State["expr"].(ast.Node)
     46 		ident := m.State["ident"].(*ast.Ident)
     47 
     48 		x := pass.TypesInfo.ObjectOf(ident)
     49 		var allOffenders []*ast.TypeAssertExpr
     50 		canSuggestFix := true
     51 		for _, clause := range stmt.Body.List {
     52 			clause := clause.(*ast.CaseClause)
     53 			if len(clause.List) != 1 {
     54 				continue
     55 			}
     56 			hasUnrelatedAssertion := false
     57 			var offenders []*ast.TypeAssertExpr
     58 			ast.Inspect(clause, func(node ast.Node) bool {
     59 				assert2, ok := node.(*ast.TypeAssertExpr)
     60 				if !ok {
     61 					return true
     62 				}
     63 				ident, ok := assert2.X.(*ast.Ident)
     64 				if !ok {
     65 					hasUnrelatedAssertion = true
     66 					return false
     67 				}
     68 				if pass.TypesInfo.ObjectOf(ident) != x {
     69 					hasUnrelatedAssertion = true
     70 					return false
     71 				}
     72 
     73 				if !types.Identical(pass.TypesInfo.TypeOf(clause.List[0]), pass.TypesInfo.TypeOf(assert2.Type)) {
     74 					hasUnrelatedAssertion = true
     75 					return false
     76 				}
     77 				offenders = append(offenders, assert2)
     78 				return true
     79 			})
     80 			if !hasUnrelatedAssertion {
     81 				// don't flag cases that have other type assertions
     82 				// unrelated to the one in the case clause. often
     83 				// times, this is done for symmetry, when two
     84 				// different values have to be asserted to the same
     85 				// type.
     86 				allOffenders = append(allOffenders, offenders...)
     87 			}
     88 			canSuggestFix = canSuggestFix && !hasUnrelatedAssertion
     89 		}
     90 		if len(allOffenders) != 0 {
     91 			var opts []report.Option
     92 			for _, offender := range allOffenders {
     93 				opts = append(opts, report.Related(offender, "could eliminate this type assertion"))
     94 			}
     95 			opts = append(opts, report.FilterGenerated())
     96 
     97 			msg := fmt.Sprintf("assigning the result of this type assertion to a variable (switch %s := %s.(type)) could eliminate type assertions in switch cases",
     98 				report.Render(pass, ident), report.Render(pass, ident))
     99 			if canSuggestFix {
    100 				var edits []analysis.TextEdit
    101 				edits = append(edits, edit.ReplaceWithPattern(pass.Fset, expr, checkSimplifyTypeSwitchR, m.State))
    102 				for _, offender := range allOffenders {
    103 					edits = append(edits, edit.ReplaceWithNode(pass.Fset, offender, offender.X))
    104 				}
    105 				opts = append(opts, report.Fixes(edit.Fix("Simplify type switch", edits...)))
    106 				report.Report(pass, expr, msg, opts...)
    107 			} else {
    108 				report.Report(pass, expr, msg, opts...)
    109 			}
    110 		}
    111 	}
    112 	return nil, nil
    113 }