src

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

s1031.go (1857B)


      1 package s1031
      2 
      3 import (
      4 	"go/types"
      5 
      6 	"honnef.co/go/tools/analysis/code"
      7 	"honnef.co/go/tools/analysis/facts/generated"
      8 	"honnef.co/go/tools/analysis/lint"
      9 	"honnef.co/go/tools/analysis/report"
     10 	"honnef.co/go/tools/go/types/typeutil"
     11 	"honnef.co/go/tools/pattern"
     12 
     13 	"golang.org/x/tools/go/analysis"
     14 )
     15 
     16 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
     17 	Analyzer: &analysis.Analyzer{
     18 		Name:     "S1031",
     19 		Run:      run,
     20 		Requires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...),
     21 	},
     22 	Doc: &lint.RawDocumentation{
     23 		Title: `Omit redundant nil check around loop`,
     24 		Text: `You can use range on nil slices and maps, the loop will simply never
     25 execute. This makes an additional nil check around the loop
     26 unnecessary.`,
     27 		Before: `
     28 if s != nil {
     29     for _, x := range s {
     30         ...
     31     }
     32 }`,
     33 		After: `
     34 for _, x := range s {
     35     ...
     36 }`,
     37 		Since: "2017.1",
     38 		// MergeIfAll because x might be a channel under some build tags.
     39 		// you shouldn't write code like that…
     40 		MergeIf: lint.MergeIfAll,
     41 	},
     42 })
     43 
     44 var Analyzer = SCAnalyzer.Analyzer
     45 
     46 var checkNilCheckAroundRangeQ = pattern.MustParse(`
     47 	(IfStmt
     48 		nil
     49 		(BinaryExpr x@(Object _) "!=" (Builtin "nil"))
     50 		[(RangeStmt _ _ _ x _)]
     51 		nil)`)
     52 
     53 func run(pass *analysis.Pass) (any, error) {
     54 	for node, m := range code.Matches(pass, checkNilCheckAroundRangeQ) {
     55 		ok := typeutil.All(m.State["x"].(types.Object).Type(), func(term *types.Term) bool {
     56 			switch term.Type().Underlying().(type) {
     57 			case *types.Slice, *types.Map:
     58 				return true
     59 			case *types.TypeParam, *types.Chan, *types.Pointer, *types.Signature:
     60 				return false
     61 			default:
     62 				lint.ExhaustiveTypeSwitch(term.Type().Underlying())
     63 				return false
     64 			}
     65 		})
     66 		if ok {
     67 			report.Report(pass, node, "unnecessary nil check around range", report.ShortRange(), report.FilterGenerated())
     68 		}
     69 	}
     70 	return nil, nil
     71 }