Skip to content
This repository has been archived by the owner on Mar 7, 2024. It is now read-only.

Commit

Permalink
Merge pull request #10 from ldez/feat/improve-code
Browse files Browse the repository at this point in the history
Support var, const, and concatenation.
Trim comments.

Improve CI
  • Loading branch information
1uf3 authored May 12, 2022
2 parents 38646fc + 72ed5bc commit 35ebba6
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 109 deletions.
45 changes: 45 additions & 0 deletions .github/workflows/go-cross.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Go Matrix
on: [push, pull_request]

jobs:

cross:
name: Go
runs-on: ${{ matrix.os }}
env:
CGO_ENABLED: 0

strategy:
matrix:
go-version: [ 1.17, 1.18, 1.x ]
os: [ubuntu-latest, macos-latest, windows-latest]

steps:
# https://github.com/marketplace/actions/setup-go-environment
- name: Set up Go ${{ matrix.go-version }}
uses: actions/setup-go@v2
with:
go-version: ${{ matrix.go-version }}

# https://github.com/marketplace/actions/checkout
- name: Checkout code
uses: actions/checkout@v2

# https://github.com/marketplace/actions/cache
- name: Cache Go modules
uses: actions/cache@v2
with:
path: |
~/go/pkg/mod # Module download cache
~/.cache/go-build # Build cache (Linux)
~/Library/Caches/go-build # Build cache (Mac)
'%LocalAppData%\go-build' # Build cache (Windows)
key: ${{ runner.os }}-${{ matrix.go-version }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-${{ matrix.go-version }}-go-
- name: Test
run: go test -v -cover ./...

- name: Build
run: go build -v -ldflags "-s -w" -trimpath ./cmd/execinquery/
50 changes: 50 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Main

on:
push:
branches:
- master
pull_request:

jobs:

main:
name: Main Process
runs-on: ubuntu-latest
env:
GO_VERSION: 1.17
CGO_ENABLED: 0

steps:

# https://github.com/marketplace/actions/setup-go-environment
- name: Set up Go ${{ env.GO_VERSION }}
uses: actions/setup-go@v2
with:
go-version: ${{ env.GO_VERSION }}

# https://github.com/marketplace/actions/checkout
- name: Check out code
uses: actions/checkout@v2
with:
fetch-depth: 0

# https://github.com/marketplace/actions/cache
- name: Cache Go modules
uses: actions/cache@v2
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Check and get dependencies
run: |
go mod tidy
git diff --exit-code go.mod
git diff --exit-code go.sum
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
with:
version: v1.46.0
44 changes: 0 additions & 44 deletions .github/workflows/test-and-lint.yml

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
execinquery
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ func main() {
```

```console
go vet -vettool=(which execinquery) ./...
go vet -vettool=$(which execinquery) ./...

# command-line-arguments
./a.go:16:11: It's better to use Execute method instead of Query method to execute `UPDATE` query
./a.go:16:11: Use Exec instead of Query to execute `UPDATE` query
```

## CI
Expand All @@ -68,7 +68,7 @@ go vet -vettool=(which execinquery) ./...
run: go vet -vettool=`which execinquery` ./...
```
### License
### License
MIT license.
Expand Down
109 changes: 68 additions & 41 deletions execinquery.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package execinquery

import (
"go/ast"
"regexp"
"strings"
"unicode"

"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/inspect"
Expand All @@ -16,13 +16,25 @@ const doc = "execinquery is a linter about query string checker in Query functio
var Analyzer = &analysis.Analyzer{
Name: "execinquery",
Doc: doc,
Run: run,
Run: newLinter().run,
Requires: []*analysis.Analyzer{
inspect.Analyzer,
},
}

func run(pass *analysis.Pass) (interface{}, error) {
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{
Expand All @@ -32,10 +44,6 @@ func run(pass *analysis.Pass) (interface{}, error) {
result.Preorder(nodeFilter, func(n ast.Node) {
switch n := n.(type) {
case *ast.CallExpr:
if len(n.Args) < 1 {
return
}

selector, ok := n.Fun.(*ast.SelectorExpr)
if !ok {
return
Expand All @@ -49,53 +57,72 @@ func run(pass *analysis.Pass) (interface{}, error) {
return
}

var i int
replacement := "Exec"
var i int // the index of the query argument
if strings.Contains(selector.Sel.Name, "Context") {
replacement = "ExecContext"
i = 1
}

var s string
switch arg := n.Args[i].(type) {
case *ast.BasicLit:
s = strings.Replace(arg.Value, "\"", "", -1)

case *ast.Ident:

switch arg2 := arg.Obj.Decl.(type) {
case *ast.AssignStmt:
for _, stmt := range arg2.Rhs {
basicLit, ok := stmt.(*ast.BasicLit)
if !ok {
continue
}

s = strings.Replace(basicLit.Value, "\"", "", -1)
}
case *ast.ValueSpec:
basicLit, ok := arg2.Values[0].(*ast.BasicLit)
if !ok {
return
}

s = strings.TrimLeftFunc(basicLit.Value, func(r rune) bool {
return !unicode.IsLetter(r) && !unicode.IsNumber(r)
})
s = strings.Replace(s, "\"", "", -1)
}

default:
if len(n.Args) <= i {
return
}

if strings.HasPrefix(strings.ToLower(s), "select") {
query := l.getQueryString(n.Args[i])
if query == "" {
return
}

s = strings.ToTitle(strings.SplitN(s, " ", 2)[0])
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(), "It's better to use Execute method instead of %s method to execute `%s` query", selector.Sel.Name, s)
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:
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 ""
}
Loading

0 comments on commit 35ebba6

Please sign in to comment.