github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/staticcheck/sa4027/sa4027.go (about) 1 package sa4027 2 3 import ( 4 "go/ast" 5 6 "github.com/amarpal/go-tools/analysis/code" 7 "github.com/amarpal/go-tools/analysis/lint" 8 "github.com/amarpal/go-tools/analysis/report" 9 "github.com/amarpal/go-tools/pattern" 10 11 "golang.org/x/tools/go/analysis" 12 "golang.org/x/tools/go/analysis/passes/inspect" 13 ) 14 15 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{ 16 Analyzer: &analysis.Analyzer{ 17 Name: "SA4027", 18 Run: run, 19 Requires: []*analysis.Analyzer{inspect.Analyzer}, 20 }, 21 Doc: &lint.Documentation{ 22 Title: `\'(*net/url.URL).Query\' returns a copy, modifying it doesn't change the URL`, 23 Text: `\'(*net/url.URL).Query\' parses the current value of \'net/url.URL.RawQuery\' 24 and returns it as a map of type \'net/url.Values\'. Subsequent changes to 25 this map will not affect the URL unless the map gets encoded and 26 assigned to the URL's \'RawQuery\'. 27 28 As a consequence, the following code pattern is an expensive no-op: 29 \'u.Query().Add(key, value)\'.`, 30 Since: "2021.1", 31 Severity: lint.SeverityWarning, 32 MergeIf: lint.MergeIfAny, 33 }, 34 }) 35 36 var Analyzer = SCAnalyzer.Analyzer 37 38 var ineffectiveURLQueryAddQ = pattern.MustParse(`(CallExpr (SelectorExpr (CallExpr (SelectorExpr recv (Ident "Query")) []) (Ident meth)) _)`) 39 40 func run(pass *analysis.Pass) (interface{}, error) { 41 // TODO(dh): We could make this check more complex and detect 42 // pointless modifications of net/url.Values in general, but that 43 // requires us to get the state machine correct, else we'll cause 44 // false positives. 45 46 fn := func(node ast.Node) { 47 m, ok := code.Match(pass, ineffectiveURLQueryAddQ, node) 48 if !ok { 49 return 50 } 51 if !code.IsOfType(pass, m.State["recv"].(ast.Expr), "*net/url.URL") { 52 return 53 } 54 switch m.State["meth"].(string) { 55 case "Add", "Del", "Set": 56 default: 57 return 58 } 59 report.Report(pass, node, "(*net/url.URL).Query returns a copy, modifying it doesn't change the URL") 60 } 61 code.Preorder(pass, fn, (*ast.CallExpr)(nil)) 62 63 return nil, nil 64 }