qf1006.go (1607B)
1 package qf1006 2 3 import ( 4 "go/ast" 5 "go/token" 6 7 "honnef.co/go/tools/analysis/code" 8 "honnef.co/go/tools/analysis/edit" 9 "honnef.co/go/tools/analysis/lint" 10 "honnef.co/go/tools/analysis/report" 11 "honnef.co/go/tools/go/ast/astutil" 12 "honnef.co/go/tools/pattern" 13 14 "golang.org/x/tools/go/analysis" 15 ) 16 17 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 18 Analyzer: &analysis.Analyzer{ 19 Name: "QF1006", 20 Run: run, 21 Requires: code.RequiredAnalyzers, 22 }, 23 Doc: &lint.RawDocumentation{ 24 Title: `Lift \'if\'+\'break\' into loop condition`, 25 Before: ` 26 for { 27 if done { 28 break 29 } 30 ... 31 }`, 32 33 After: ` 34 for !done { 35 ... 36 }`, 37 Since: "2021.1", 38 Severity: lint.SeverityHint, 39 }, 40 }) 41 42 var Analyzer = SCAnalyzer.Analyzer 43 44 var checkForLoopIfBreak = pattern.MustParse(`(ForStmt nil nil nil if@(IfStmt nil cond (BranchStmt "BREAK" nil) nil):_)`) 45 46 func run(pass *analysis.Pass) (any, error) { 47 for node, m := range code.Matches(pass, checkForLoopIfBreak) { 48 pos := node.Pos() + token.Pos(len("for")) 49 r := astutil.NegateDeMorgan(m.State["cond"].(ast.Expr), false) 50 51 // FIXME(dh): we're leaving behind an empty line when we 52 // delete the old if statement. However, we can't just delete 53 // an additional character, in case there closing curly brace 54 // is followed by a comment, or Windows newlines. 55 report.Report(pass, m.State["if"].(ast.Node), "could lift into loop condition", 56 report.Fixes(edit.Fix("Lift into loop condition", 57 edit.ReplaceWithString(edit.Range{pos, pos}, " "+report.Render(pass, r)), 58 edit.Delete(m.State["if"].(ast.Node))))) 59 } 60 return nil, nil 61 }