-
-
Notifications
You must be signed in to change notification settings - Fork 381
/
Copy paths1007.go
93 lines (84 loc) · 2.29 KB
/
s1007.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package s1007
import (
"fmt"
"go/ast"
"go/token"
"strings"
"honnef.co/go/tools/analysis/code"
"honnef.co/go/tools/analysis/facts/generated"
"honnef.co/go/tools/analysis/lint"
"honnef.co/go/tools/analysis/report"
"honnef.co/go/tools/knowledge"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
)
var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
Analyzer: &analysis.Analyzer{
Name: "S1007",
Run: run,
Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},
},
Doc: &lint.RawDocumentation{
Title: `Simplify regular expression by using raw string literal`,
Text: `Raw string literals use backticks instead of quotation marks and do not support
any escape sequences. This means that the backslash can be used
freely, without the need of escaping.
Since regular expressions have their own escape sequences, raw strings
can improve their readability.`,
Before: `regexp.Compile("\\A(\\w+) profile: total \\d+\\n\\z")`,
After: "regexp.Compile(`\\A(\\w+) profile: total \\d+\\n\\z`)",
Since: "2017.1",
MergeIf: lint.MergeIfAny,
},
})
var Analyzer = SCAnalyzer.Analyzer
func run(pass *analysis.Pass) (interface{}, error) {
fn := func(node ast.Node) {
call := node.(*ast.CallExpr)
if !code.IsCallToAny(pass, call, "regexp.MustCompile", "regexp.Compile") {
return
}
sel, ok := call.Fun.(*ast.SelectorExpr)
if !ok {
return
}
lit, ok := call.Args[knowledge.Arg("regexp.Compile.expr")].(*ast.BasicLit)
if !ok {
// TODO(dominikh): support string concat, maybe support constants
return
}
if lit.Kind != token.STRING {
// invalid function call
return
}
if lit.Value[0] != '"' {
// already a raw string
return
}
val := lit.Value
if !strings.Contains(val, `\\`) {
return
}
if strings.Contains(val, "`") {
return
}
bs := false
for _, c := range val {
if !bs && c == '\\' {
bs = true
continue
}
if bs && c == '\\' {
bs = false
continue
}
if bs {
// backslash followed by non-backslash -> escape sequence
return
}
}
report.Report(pass, call, fmt.Sprintf("should use raw string (`...`) with regexp.%s to avoid having to escape twice", sel.Sel.Name), report.FilterGenerated())
}
code.Preorder(pass, fn, (*ast.CallExpr)(nil))
return nil, nil
}