aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/github.com/lufeee/execinquery/execinquery.go
diff options
context:
space:
mode:
authorTaras Madan <tarasmadan@google.com>2022-09-05 14:27:54 +0200
committerGitHub <noreply@github.com>2022-09-05 12:27:54 +0000
commitb2f2446b46bf02821d90ebedadae2bf7ae0e880e (patch)
tree923cf42842918d6bebca1d6bbdc08abed54d274d /vendor/github.com/lufeee/execinquery/execinquery.go
parente6654faff4bcca4be92e9a8596fd4b77f747c39e (diff)
go.mod, vendor: update (#3358)
* go.mod, vendor: remove unnecessary dependencies Commands: 1. go mod tidy 2. go mod vendor * go.mod, vendor: update cloud.google.com/go Commands: 1. go get -u cloud.google.com/go 2. go mod tidy 3. go mod vendor * go.mod, vendor: update cloud.google.com/* Commands: 1. go get -u cloud.google.com/storage cloud.google.com/logging 2. go mod tidy 3. go mod vendor * go.mod, .golangci.yml, vendor: update *lint* Commands: 1. go get -u golang.org/x/tools github.com/golangci/golangci-lint@v1.47.0 2. go mod tidy 3. go mod vendor 4. edit .golangci.yml to suppress new errors (resolved in the same PR later) * all: fix lint errors hash.go: copy() recommended by gosimple parse.go: ent is never nil verifier.go: signal.Notify() with unbuffered channel is bad. Have no idea why. * .golangci.yml: adjust godot rules check-all is deprecated, but still work if you're hesitating too - I'll remove this commit
Diffstat (limited to 'vendor/github.com/lufeee/execinquery/execinquery.go')
-rw-r--r--vendor/github.com/lufeee/execinquery/execinquery.go135
1 files changed, 135 insertions, 0 deletions
diff --git a/vendor/github.com/lufeee/execinquery/execinquery.go b/vendor/github.com/lufeee/execinquery/execinquery.go
new file mode 100644
index 000000000..c37dc1701
--- /dev/null
+++ b/vendor/github.com/lufeee/execinquery/execinquery.go
@@ -0,0 +1,135 @@
+package execinquery
+
+import (
+ "go/ast"
+ "regexp"
+ "strings"
+
+ "golang.org/x/tools/go/analysis"
+ "golang.org/x/tools/go/analysis/passes/inspect"
+ "golang.org/x/tools/go/ast/inspector"
+)
+
+const doc = "execinquery is a linter about query string checker in Query function which reads your Go src files and warning it finds"
+
+// Analyzer is checking database/sql pkg Query's function
+var Analyzer = &analysis.Analyzer{
+ Name: "execinquery",
+ Doc: doc,
+ Run: newLinter().run,
+ Requires: []*analysis.Analyzer{
+ inspect.Analyzer,
+ },
+}
+
+type linter struct {
+ commentExp *regexp.Regexp
+ multilineCommentExp *regexp.Regexp
+}
+
+func newLinter() *linter {
+ return &linter{
+ commentExp: regexp.MustCompile(`--[^\n]*\n`),
+ multilineCommentExp: regexp.MustCompile(`(?s)/\*.*?\*/`),
+ }
+}
+
+func (l linter) run(pass *analysis.Pass) (interface{}, error) {
+ result := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
+
+ nodeFilter := []ast.Node{
+ (*ast.CallExpr)(nil),
+ }
+
+ result.Preorder(nodeFilter, func(n ast.Node) {
+ switch n := n.(type) {
+ case *ast.CallExpr:
+ selector, ok := n.Fun.(*ast.SelectorExpr)
+ if !ok {
+ return
+ }
+
+ if pass.TypesInfo == nil || pass.TypesInfo.Uses[selector.Sel] == nil || pass.TypesInfo.Uses[selector.Sel].Pkg() == nil {
+ return
+ }
+
+ if "database/sql" != pass.TypesInfo.Uses[selector.Sel].Pkg().Path() {
+ return
+ }
+
+ if !strings.Contains(selector.Sel.Name, "Query") {
+ return
+ }
+
+ replacement := "Exec"
+ var i int // the index of the query argument
+ if strings.Contains(selector.Sel.Name, "Context") {
+ replacement += "Context"
+ i = 1
+ }
+
+ if len(n.Args) <= i {
+ return
+ }
+
+ query := l.getQueryString(n.Args[i])
+ if query == "" {
+ return
+ }
+
+ query = strings.TrimSpace(l.cleanValue(query))
+ parts := strings.SplitN(query, " ", 2)
+ cmd := strings.ToUpper(parts[0])
+
+ if strings.HasPrefix(cmd, "SELECT") {
+ return
+ }
+
+ pass.Reportf(n.Fun.Pos(), "Use %s instead of %s to execute `%s` query", replacement, selector.Sel.Name, cmd)
+ }
+ })
+
+ return nil, nil
+}
+
+func (l linter) cleanValue(s string) string {
+ v := strings.NewReplacer(`"`, "", "`", "").Replace(s)
+
+ v = l.multilineCommentExp.ReplaceAllString(v, "")
+
+ return l.commentExp.ReplaceAllString(v, "")
+}
+
+func (l linter) getQueryString(exp interface{}) string {
+ switch e := exp.(type) {
+ case *ast.AssignStmt:
+ var v string
+ for _, stmt := range e.Rhs {
+ v += l.cleanValue(l.getQueryString(stmt))
+ }
+ return v
+
+ case *ast.BasicLit:
+ return e.Value
+
+ case *ast.ValueSpec:
+ var v string
+ for _, value := range e.Values {
+ v += l.cleanValue(l.getQueryString(value))
+ }
+ return v
+
+ case *ast.Ident:
+ if e.Obj == nil {
+ return ""
+ }
+ return l.getQueryString(e.Obj.Decl)
+
+ case *ast.BinaryExpr:
+ v := l.cleanValue(l.getQueryString(e.X))
+ v += l.cleanValue(l.getQueryString(e.Y))
+ return v
+ }
+
+ return ""
+}