s1005.go raw

   1  package s1005
   2  
   3  import (
   4  	"go/ast"
   5  	"go/types"
   6  
   7  	"honnef.co/go/tools/analysis/code"
   8  	"honnef.co/go/tools/analysis/edit"
   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/go/ast/astutil"
  13  	"honnef.co/go/tools/pattern"
  14  
  15  	"golang.org/x/tools/go/analysis"
  16  	"golang.org/x/tools/go/analysis/passes/inspect"
  17  )
  18  
  19  var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
  20  	Analyzer: &analysis.Analyzer{
  21  		Name:     "S1005",
  22  		Run:      run,
  23  		Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},
  24  	},
  25  	Doc: &lint.RawDocumentation{
  26  		Title: `Drop unnecessary use of the blank identifier`,
  27  		Text:  `In many cases, assigning to the blank identifier is unnecessary.`,
  28  		Before: `
  29  for _ = range s {}
  30  x, _ = someMap[key]
  31  _ = <-ch`,
  32  		After: `
  33  for range s{}
  34  x = someMap[key]
  35  <-ch`,
  36  		Since:   "2017.1",
  37  		MergeIf: lint.MergeIfAny,
  38  	},
  39  })
  40  
  41  var Analyzer = SCAnalyzer.Analyzer
  42  
  43  var (
  44  	checkUnnecessaryBlankQ1 = pattern.MustParse(`
  45  		(AssignStmt
  46  			[_ (Ident "_")]
  47  			_
  48  			(Or
  49  				(IndexExpr _ _)
  50  				(UnaryExpr "<-" _))) `)
  51  	checkUnnecessaryBlankQ2 = pattern.MustParse(`
  52  		(AssignStmt
  53  			(Ident "_") _ recv@(UnaryExpr "<-" _))`)
  54  )
  55  
  56  func run(pass *analysis.Pass) (interface{}, error) {
  57  	fn1 := func(node ast.Node) {
  58  		if _, ok := code.Match(pass, checkUnnecessaryBlankQ1, node); ok {
  59  			r := *node.(*ast.AssignStmt)
  60  			r.Lhs = r.Lhs[0:1]
  61  			report.Report(pass, node, "unnecessary assignment to the blank identifier",
  62  				report.FilterGenerated(),
  63  				report.Fixes(edit.Fix("remove assignment to blank identifier", edit.ReplaceWithNode(pass.Fset, node, &r))))
  64  		} else if m, ok := code.Match(pass, checkUnnecessaryBlankQ2, node); ok {
  65  			report.Report(pass, node, "unnecessary assignment to the blank identifier",
  66  				report.FilterGenerated(),
  67  				report.Fixes(edit.Fix("simplify channel receive operation", edit.ReplaceWithNode(pass.Fset, node, m.State["recv"].(ast.Node)))))
  68  		}
  69  	}
  70  
  71  	fn3 := func(node ast.Node) {
  72  		rs := node.(*ast.RangeStmt)
  73  
  74  		if _, ok := pass.TypesInfo.TypeOf(rs.X).Underlying().(*types.Signature); ok {
  75  			// iteration variables are not optional with rangefunc
  76  			return
  77  		}
  78  
  79  		// for _
  80  		if rs.Value == nil && astutil.IsBlank(rs.Key) {
  81  			report.Report(pass, rs.Key, "unnecessary assignment to the blank identifier",
  82  				report.FilterGenerated(),
  83  				report.MinimumLanguageVersion("go1.4"),
  84  				report.Fixes(edit.Fix("remove assignment to blank identifier", edit.Delete(edit.Range{rs.Key.Pos(), rs.TokPos + 1}))))
  85  		}
  86  
  87  		// for _, _
  88  		if astutil.IsBlank(rs.Key) && astutil.IsBlank(rs.Value) {
  89  			// FIXME we should mark both key and value
  90  			report.Report(pass, rs.Key, "unnecessary assignment to the blank identifier",
  91  				report.FilterGenerated(),
  92  				report.MinimumLanguageVersion("go1.4"),
  93  				report.Fixes(edit.Fix("remove assignment to blank identifier", edit.Delete(edit.Range{rs.Key.Pos(), rs.TokPos + 1}))))
  94  		}
  95  
  96  		// for x, _
  97  		if !astutil.IsBlank(rs.Key) && astutil.IsBlank(rs.Value) {
  98  			report.Report(pass, rs.Value, "unnecessary assignment to the blank identifier",
  99  				report.FilterGenerated(),
 100  				report.MinimumLanguageVersion("go1.4"),
 101  				report.Fixes(edit.Fix("remove assignment to blank identifier", edit.Delete(edit.Range{rs.Key.End(), rs.Value.End()}))))
 102  		}
 103  	}
 104  
 105  	code.Preorder(pass, fn1, (*ast.AssignStmt)(nil))
 106  	code.Preorder(pass, fn3, (*ast.RangeStmt)(nil))
 107  	return nil, nil
 108  }
 109