src

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

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 }