Skip to content

Commit

Permalink
feat: add suggested fixes support
Browse files Browse the repository at this point in the history
  • Loading branch information
ldez committed Dec 27, 2024
1 parent a7b3ee4 commit cdc3627
Show file tree
Hide file tree
Showing 21 changed files with 5,506 additions and 82 deletions.
35 changes: 23 additions & 12 deletions pkg/analyzer/analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package analyzer

import (
"flag"
"fmt"
"go/ast"
"go/token"
"strings"
Expand Down Expand Up @@ -364,55 +365,55 @@ func checkHTTPMethod(pass *analysis.Pass, basicLit *ast.BasicLit) {
key := strings.ToUpper(currentVal)

if newVal, ok := mapping.HTTPMethod[key]; ok {
report(pass, basicLit.Pos(), currentVal, newVal)
report(pass, basicLit, currentVal, newVal)
}
}

func checkHTTPStatusCode(pass *analysis.Pass, basicLit *ast.BasicLit) {
currentVal := getBasicLitValue(basicLit)

if newVal, ok := mapping.HTTPStatusCode[currentVal]; ok {
report(pass, basicLit.Pos(), currentVal, newVal)
report(pass, basicLit, currentVal, newVal)
}
}

func checkTimeWeekday(pass *analysis.Pass, basicLit *ast.BasicLit) {
currentVal := getBasicLitValue(basicLit)

if newVal, ok := mapping.TimeWeekday[currentVal]; ok {
report(pass, basicLit.Pos(), currentVal, newVal)
report(pass, basicLit, currentVal, newVal)
}
}

func checkTimeMonth(pass *analysis.Pass, basicLit *ast.BasicLit) {
currentVal := getBasicLitValue(basicLit)

if newVal, ok := mapping.TimeMonth[currentVal]; ok {
report(pass, basicLit.Pos(), currentVal, newVal)
report(pass, basicLit, currentVal, newVal)
}
}

func checkTimeLayout(pass *analysis.Pass, basicLit *ast.BasicLit) {
currentVal := getBasicLitValue(basicLit)

if newVal, ok := mapping.TimeLayout[currentVal]; ok {
report(pass, basicLit.Pos(), currentVal, newVal)
report(pass, basicLit, currentVal, newVal)
}
}

func checkCryptoHash(pass *analysis.Pass, basicLit *ast.BasicLit) {
currentVal := getBasicLitValue(basicLit)

if newVal, ok := mapping.CryptoHash[currentVal]; ok {
report(pass, basicLit.Pos(), currentVal, newVal)
report(pass, basicLit, currentVal, newVal)
}
}

func checkRPCDefaultPath(pass *analysis.Pass, basicLit *ast.BasicLit) {
currentVal := getBasicLitValue(basicLit)

if newVal, ok := mapping.RPCDefaultPath[currentVal]; ok {
report(pass, basicLit.Pos(), currentVal, newVal)
report(pass, basicLit, currentVal, newVal)
}
}

Expand All @@ -422,23 +423,23 @@ func checkSQLIsolationLevel(pass *analysis.Pass, basicLit *ast.BasicLit) {
currentVal := getBasicLitValue(basicLit)

if newVal, ok := mapping.SQLIsolationLevel[currentVal]; ok {
report(pass, basicLit.Pos(), currentVal, newVal)
report(pass, basicLit, currentVal, newVal)
}
}

func checkTLSSignatureScheme(pass *analysis.Pass, basicLit *ast.BasicLit) {
currentVal := getBasicLitValue(basicLit)

if newVal, ok := mapping.TLSSignatureScheme[currentVal]; ok {
report(pass, basicLit.Pos(), currentVal, newVal)
report(pass, basicLit, currentVal, newVal)
}
}

func checkConstantKind(pass *analysis.Pass, basicLit *ast.BasicLit) {
currentVal := getBasicLitValue(basicLit)

if newVal, ok := mapping.ConstantKind[currentVal]; ok {
report(pass, basicLit.Pos(), currentVal, newVal)
report(pass, basicLit, currentVal, newVal)
}
}

Expand Down Expand Up @@ -514,6 +515,16 @@ func getBasicLitValue(basicLit *ast.BasicLit) string {
return val.String()
}

func report(pass *analysis.Pass, pos token.Pos, currentVal, newVal string) {
pass.Reportf(pos, "%q can be replaced by %s", currentVal, newVal)
func report(pass *analysis.Pass, rg analysis.Range, currentVal, newVal string) {
pass.Report(analysis.Diagnostic{
Pos: rg.Pos(),
Message: fmt.Sprintf("%q can be replaced by %s", currentVal, newVal),
SuggestedFixes: []analysis.SuggestedFix{{
TextEdits: []analysis.TextEdit{{
Pos: rg.Pos(),
End: rg.End(),
NewText: []byte(newVal),
}},
}},
})
}
28 changes: 17 additions & 11 deletions pkg/analyzer/analyzer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,6 @@ var flags = []string{
analyzer.ConstantKindFlag,
}

var pkgs = []string{
"a/crypto",
"a/http",
"a/rpc",
"a/time",
"a/sql",
"a/tls",
"a/constant",
}

func TestUseStdlibVars(t *testing.T) {
a := analyzer.New()

Expand All @@ -38,5 +28,21 @@ func TestUseStdlibVars(t *testing.T) {
}
}

analysistest.Run(t, analysistest.TestData(), a, pkgs...)
testCases := []struct {
dir string
}{
{dir: "a/crypto"},
{dir: "a/http"},
{dir: "a/rpc"},
{dir: "a/time"},
{dir: "a/sql"},
{dir: "a/tls"},
{dir: "a/constant"},
}

for _, test := range testCases {
t.Run(test.dir, func(t *testing.T) {
analysistest.RunWithSuggestedFixes(t, analysistest.TestData(), a, test.dir)
})
}
}
138 changes: 79 additions & 59 deletions pkg/analyzer/internal/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"go/format"
"log"
"os"
"path/filepath"
"regexp"
"strings"
"text/template"
Expand All @@ -30,82 +31,95 @@ func main() {
)

operations := []struct {
mapping map[string]string
packageName string
templateName string
fileName string
mapping map[string]string
packageName string
templateName string
goldenTemplateName string
fileName string
}{
{
mapping: mapping.CryptoHash,
packageName: "crypto_test",
templateName: "test-template.go.tmpl",
fileName: "pkg/analyzer/testdata/src/a/crypto/crypto.go",
mapping: mapping.CryptoHash,
packageName: "crypto_test",
templateName: "test-template.go.tmpl",
goldenTemplateName: "test-template.go.golden.tmpl",
fileName: "pkg/analyzer/testdata/src/a/crypto/crypto.go",
},
{
mapping: mapping.HTTPMethod,
packageName: "http_test",
templateName: "test-httpmethod.go.tmpl",
fileName: "pkg/analyzer/testdata/src/a/http/method.go",
mapping: mapping.RPCDefaultPath,
packageName: "rpc_test",
templateName: "test-template.go.tmpl",
goldenTemplateName: "test-template.go.golden.tmpl",
fileName: "pkg/analyzer/testdata/src/a/rpc/rpc.go",
},
{
mapping: mapping.HTTPStatusCode,
packageName: "http_test",
templateName: "test-httpstatus.go.tmpl",
fileName: "pkg/analyzer/testdata/src/a/http/status.go",
mapping: mapping.TimeWeekday,
packageName: "time_test",
templateName: "test-template.go.tmpl",
goldenTemplateName: "test-template.go.golden.tmpl",
fileName: "pkg/analyzer/testdata/src/a/time/weekday.go",
},
{
mapping: mapping.RPCDefaultPath,
packageName: "rpc_test",
templateName: "test-template.go.tmpl",
fileName: "pkg/analyzer/testdata/src/a/rpc/rpc.go",
mapping: mapping.TimeMonth,
packageName: "time_test",
templateName: "test-template.go.tmpl",
goldenTemplateName: "test-template.go.golden.tmpl",
fileName: "pkg/analyzer/testdata/src/a/time/month.go",
},
{
mapping: mapping.TimeWeekday,
packageName: "time_test",
templateName: "test-template.go.tmpl",
fileName: "pkg/analyzer/testdata/src/a/time/weekday.go",
mapping: mapping.TimeLayout,
packageName: "time_test",
templateName: "test-template.go.tmpl",
goldenTemplateName: "test-template.go.golden.tmpl",
fileName: "pkg/analyzer/testdata/src/a/time/layout.go",
},
{
mapping: mapping.TimeMonth,
packageName: "time_test",
templateName: "test-template.go.tmpl",
fileName: "pkg/analyzer/testdata/src/a/time/month.go",
mapping: mapping.SQLIsolationLevel,
packageName: "sql_test",
templateName: "test-template.go.tmpl",
goldenTemplateName: "test-template.go.golden.tmpl",
fileName: "pkg/analyzer/testdata/src/a/sql/isolationlevel.go",
},
{
mapping: mapping.TimeLayout,
packageName: "time_test",
templateName: "test-template.go.tmpl",
fileName: "pkg/analyzer/testdata/src/a/time/layout.go",
mapping: mapping.TLSSignatureScheme,
packageName: "tls_test",
templateName: "test-template.go.tmpl",
goldenTemplateName: "test-template.go.golden.tmpl",
fileName: "pkg/analyzer/testdata/src/a/tls/signaturescheme.go",
},
{
mapping: mapping.HTTPMethod,
packageName: "http_test",
templateName: "test-issue32.go.tmpl",
fileName: "pkg/analyzer/testdata/src/a/http/issue32.go",
mapping: mapping.ConstantKind,
packageName: "constant_test",
templateName: "test-template.go.tmpl",
goldenTemplateName: "test-template.go.golden.tmpl",
fileName: "pkg/analyzer/testdata/src/a/constant/kind.go",
},
{
mapping: mapping.SQLIsolationLevel,
packageName: "sql_test",
templateName: "test-template.go.tmpl",
fileName: "pkg/analyzer/testdata/src/a/sql/isolationlevel.go",
mapping: mapping.HTTPMethod,
packageName: "http_test",
templateName: "test-httpmethod.go.tmpl",
goldenTemplateName: "test-httpmethod.go.golden.tmpl",
fileName: "pkg/analyzer/testdata/src/a/http/method.go",
},
{
mapping: mapping.TLSSignatureScheme,
packageName: "tls_test",
templateName: "test-template.go.tmpl",
fileName: "pkg/analyzer/testdata/src/a/tls/signaturescheme.go",
mapping: mapping.HTTPStatusCode,
packageName: "http_test",
templateName: "test-httpstatus.go.tmpl",
goldenTemplateName: "test-httpstatus.go.golden.tmpl",
fileName: "pkg/analyzer/testdata/src/a/http/status.go",
},
{
mapping: mapping.ConstantKind,
packageName: "constant_test",
templateName: "test-template.go.tmpl",
fileName: "pkg/analyzer/testdata/src/a/constant/kind.go",
mapping: mapping.HTTPMethod,
packageName: "http_test",
templateName: "test-issue32.go.tmpl",
goldenTemplateName: "test-issue32.go.golden.tmpl",
fileName: "pkg/analyzer/testdata/src/a/http/issue32.go",
},
{
mapping: mapping.HTTPStatusCode,
packageName: "http_test",
templateName: "test-issue89.go.tmpl",
fileName: "pkg/analyzer/testdata/src/a/http/issue89.go",
mapping: mapping.HTTPStatusCode,
packageName: "http_test",
templateName: "test-issue89.go.tmpl",
goldenTemplateName: "test-issue89.go.golden.tmpl",
fileName: "pkg/analyzer/testdata/src/a/http/issue89.go",
},
}

Expand All @@ -118,6 +132,12 @@ func main() {
if err := execute(t, operation.templateName, data, operation.fileName); err != nil {
log.Fatal(err)
}

if operation.goldenTemplateName != "" {
if err := execute(t, operation.goldenTemplateName, data, operation.fileName+".golden"); err != nil {
log.Fatal(err)
}
}
}
}

Expand All @@ -128,14 +148,14 @@ func execute(t *template.Template, templateName string, data any, fileName strin
return err
}

sourceData, err := format.Source(builder.Bytes())
if err != nil {
return err
}
if filepath.Ext(fileName) == ".go" {
sourceData, err := format.Source(builder.Bytes())
if err != nil {
return err
}

if err := os.WriteFile(fileName, sourceData, os.ModePerm); err != nil {
return err
return os.WriteFile(fileName, sourceData, os.ModePerm)
}

return nil
return os.WriteFile(fileName, builder.Bytes(), os.ModePerm)
}
Loading

0 comments on commit cdc3627

Please sign in to comment.