s1007.go (2077B)
1 package s1007 2 3 import ( 4 "fmt" 5 "go/ast" 6 "strings" 7 8 "honnef.co/go/tools/analysis/code" 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/pattern" 13 14 "golang.org/x/tools/go/analysis" 15 ) 16 17 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 18 Analyzer: &analysis.Analyzer{ 19 Name: "S1007", 20 Run: run, 21 Requires: append([]*analysis.Analyzer{generated.Analyzer}, code.RequiredAnalyzers...), 22 }, 23 Doc: &lint.RawDocumentation{ 24 Title: `Simplify regular expression by using raw string literal`, 25 Text: `Raw string literals use backticks instead of quotation marks and do not support 26 any escape sequences. This means that the backslash can be used 27 freely, without the need of escaping. 28 29 Since regular expressions have their own escape sequences, raw strings 30 can improve their readability.`, 31 Before: `regexp.Compile("\\A(\\w+) profile: total \\d+\\n\\z")`, 32 After: "regexp.Compile(`\\A(\\w+) profile: total \\d+\\n\\z`)", 33 Since: "2017.1", 34 MergeIf: lint.MergeIfAny, 35 }, 36 }) 37 38 var Analyzer = SCAnalyzer.Analyzer 39 40 // TODO(dominikh): support string concat, maybe support constants 41 var query = pattern.MustParse(`(CallExpr (Symbol fn@(Or "regexp.MustCompile" "regexp.Compile")) [lit@(BasicLit "STRING" _)])`) 42 43 func run(pass *analysis.Pass) (any, error) { 44 outer: 45 for _, m := range code.Matches(pass, query) { 46 lit := m.State["lit"].(*ast.BasicLit) 47 val := lit.Value 48 if lit.Value[0] != '"' { 49 // already a raw string 50 continue 51 } 52 if !strings.Contains(val, `\\`) { 53 continue 54 } 55 if strings.Contains(val, "`") { 56 continue 57 } 58 59 bs := false 60 for _, c := range val { 61 if !bs && c == '\\' { 62 bs = true 63 continue 64 } 65 if bs && c == '\\' { 66 bs = false 67 continue 68 } 69 if bs { 70 // backslash followed by non-backslash -> escape sequence 71 continue outer 72 } 73 } 74 75 report.Report(pass, lit, fmt.Sprintf("should use raw string (`...`) with %s to avoid having to escape twice", m.State["fn"]), report.FilterGenerated()) 76 } 77 return nil, nil 78 }