src

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

s1002.go (2190B)


      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) (any, 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 }