s1002.go raw

   1  package s1002
   2  
   3  import (
   4  	"fmt"
   5  	"go/ast"
   6  	"go/token"
   7  	"go/types"
   8  	"strings"
   9  
  10  	"honnef.co/go/tools/analysis/code"
  11  	"honnef.co/go/tools/analysis/edit"
  12  	"honnef.co/go/tools/analysis/facts/generated"
  13  	"honnef.co/go/tools/analysis/lint"
  14  	"honnef.co/go/tools/analysis/report"
  15  	"honnef.co/go/tools/go/types/typeutil"
  16  
  17  	"golang.org/x/tools/go/analysis"
  18  	"golang.org/x/tools/go/analysis/passes/inspect"
  19  )
  20  
  21  var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
  22  	Analyzer: &analysis.Analyzer{
  23  		Name:     "S1002",
  24  		Run:      run,
  25  		Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},
  26  	},
  27  	Doc: &lint.RawDocumentation{
  28  		Title:  `Omit comparison with boolean constant`,
  29  		Before: `if x == true {}`,
  30  		After:  `if x {}`,
  31  		Since:  "2017.1",
  32  		// MergeIfAll because 'true' might not be the builtin constant under all 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  		if code.IsInTest(pass, node) {
  43  			return
  44  		}
  45  
  46  		expr := node.(*ast.BinaryExpr)
  47  		if expr.Op != token.EQL && expr.Op != token.NEQ {
  48  			return
  49  		}
  50  		x := code.IsBoolConst(pass, expr.X)
  51  		y := code.IsBoolConst(pass, expr.Y)
  52  		if !x && !y {
  53  			return
  54  		}
  55  		var other ast.Expr
  56  		var val bool
  57  		if x {
  58  			val = code.BoolConst(pass, expr.X)
  59  			other = expr.Y
  60  		} else {
  61  			val = code.BoolConst(pass, expr.Y)
  62  			other = expr.X
  63  		}
  64  
  65  		ok := typeutil.All(pass.TypesInfo.TypeOf(other), func(term *types.Term) bool {
  66  			basic, ok := term.Type().Underlying().(*types.Basic)
  67  			return ok && basic.Kind() == types.Bool
  68  		})
  69  		if !ok {
  70  			return
  71  		}
  72  		op := ""
  73  		if (expr.Op == token.EQL && !val) || (expr.Op == token.NEQ && val) {
  74  			op = "!"
  75  		}
  76  		r := op + report.Render(pass, other)
  77  		l1 := len(r)
  78  		r = strings.TrimLeft(r, "!")
  79  		if (l1-len(r))%2 == 1 {
  80  			r = "!" + r
  81  		}
  82  		report.Report(pass, expr, fmt.Sprintf("should omit comparison to bool constant, can be simplified to %s", r),
  83  			report.FilterGenerated(),
  84  			report.Fixes(edit.Fix("simplify bool comparison", edit.ReplaceWithString(expr, r))))
  85  	}
  86  	code.Preorder(pass, fn, (*ast.BinaryExpr)(nil))
  87  	return nil, nil
  88  }
  89