From 5d901479ad8e8c7179fc0b6984ea87a30134dcd3 Mon Sep 17 00:00:00 2001 From: Matthieu MOREL Date: Mon, 18 Nov 2024 20:26:47 +0100 Subject: [PATCH] chore: refactor Diagnostic declaration Signed-off-by: Matthieu MOREL --- analyzer/analyzer.go | 177 ++++++++++++++++++++++------------------- analyzer/diagnostic.go | 26 ++++++ 2 files changed, 119 insertions(+), 84 deletions(-) create mode 100644 analyzer/diagnostic.go diff --git a/analyzer/analyzer.go b/analyzer/analyzer.go index 543b4bd..1dac951 100644 --- a/analyzer/analyzer.go +++ b/analyzer/analyzer.go @@ -16,6 +16,15 @@ import ( "golang.org/x/tools/go/analysis" ) +const ( + intConversionChecker string = "int-conversion" + errErrorChecker string = "err-error" + errorfChecker string = "errorf" + sprintf1Checker string = "sprintf1" + fiximportsChecker string = "fiximports" + strconcatChecker string = "strconcat" +) + type perfSprint struct { intConv bool errError bool @@ -44,12 +53,12 @@ func New() *analysis.Analyzer { Run: n.run, Requires: []*analysis.Analyzer{inspect.Analyzer}, } - r.Flags.BoolVar(&n.intConv, "int-conversion", true, "optimizes even if it requires an int or uint type cast") - r.Flags.BoolVar(&n.errError, "err-error", false, "optimizes into err.Error() even if it is only equivalent for non-nil errors") - r.Flags.BoolVar(&n.errorf, "errorf", true, "optimizes fmt.Errorf") - r.Flags.BoolVar(&n.sprintf1, "sprintf1", true, "optimizes fmt.Sprintf with only one argument") - r.Flags.BoolVar(&n.fiximports, "fiximports", true, "fix needed imports from other fixes") - r.Flags.BoolVar(&n.strconcat, "strconcat", true, "optimizes into strings concatenation") + r.Flags.BoolVar(&n.intConv, intConversionChecker, true, "optimizes even if it requires an int or uint type cast") + r.Flags.BoolVar(&n.errError, errErrorChecker, false, "optimizes into err.Error() even if it is only equivalent for non-nil errors") + r.Flags.BoolVar(&n.errorf, errorfChecker, true, "optimizes fmt.Errorf") + r.Flags.BoolVar(&n.sprintf1, sprintf1Checker, true, "optimizes fmt.Sprintf with only one argument") + r.Flags.BoolVar(&n.fiximports, fiximportsChecker, true, "fix needed imports from other fixes") + r.Flags.BoolVar(&n.strconcat, strconcatChecker, true, "optimizes into strings concatenation") return r } @@ -171,11 +180,11 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) { removedFmtUsages[fname]++ if fn == "fmt.Errorf" { neededPackages[fname]["errors"] = true - d = &analysis.Diagnostic{ - Pos: call.Pos(), - End: call.End(), - Message: fn + " can be replaced with errors.New", - SuggestedFixes: []analysis.SuggestedFix{ + d = newAnalysisDiagnostic( + errorfChecker, + call, + fn+" can be replaced with errors.New", + []analysis.SuggestedFix{ { Message: "Use errors.New", TextEdits: []analysis.TextEdit{{ @@ -185,13 +194,13 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) { }}, }, }, - } + ) } else { - d = &analysis.Diagnostic{ - Pos: call.Pos(), - End: call.End(), - Message: fn + " can be replaced with just using the string", - SuggestedFixes: []analysis.SuggestedFix{ + d = newAnalysisDiagnostic( + "", // TODO: precise checker + call, + fn+" can be replaced with just using the string", + []analysis.SuggestedFix{ { Message: "Just use string value", TextEdits: []analysis.TextEdit{{ @@ -201,7 +210,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) { }}, }, }, - } + ) } case types.Implements(valueType, errIface) && oneOf(verb, "%v", "%s") && n.errError: // known false positive if this error is nil @@ -209,11 +218,11 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) { errMethodCall := formatNode(pass.Fset, value) + ".Error()" fname := pass.Fset.File(call.Pos()).Name() removedFmtUsages[fname]++ - d = &analysis.Diagnostic{ - Pos: call.Pos(), - End: call.End(), - Message: fn + " can be replaced with " + errMethodCall, - SuggestedFixes: []analysis.SuggestedFix{ + d = newAnalysisDiagnostic( + errErrorChecker, + call, + fn+" can be replaced with "+errMethodCall, + []analysis.SuggestedFix{ { Message: "Use " + errMethodCall, TextEdits: []analysis.TextEdit{{ @@ -223,7 +232,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) { }}, }, }, - } + ) case isBasicType(valueType, types.Bool) && oneOf(verb, "%v", "%t"): fname := pass.Fset.File(call.Pos()).Name() @@ -233,11 +242,11 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) { neededPackages[fname] = make(map[string]bool) } neededPackages[fname]["strconv"] = true - d = &analysis.Diagnostic{ - Pos: call.Pos(), - End: call.End(), - Message: fn + " can be replaced with faster strconv.FormatBool", - SuggestedFixes: []analysis.SuggestedFix{ + d = newAnalysisDiagnostic( + "", // TODO: precise checker + call, + fn+" can be replaced with faster strconv.FormatBool", + []analysis.SuggestedFix{ { Message: "Use strconv.FormatBool", TextEdits: []analysis.TextEdit{{ @@ -247,7 +256,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) { }}, }, }, - } + ) case isArray && isBasicType(a.Elem(), types.Uint8) && oneOf(verb, "%x"): if _, ok := value.(*ast.Ident); !ok { @@ -262,11 +271,11 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) { neededPackages[fname] = make(map[string]bool) } neededPackages[fname]["encoding/hex"] = true - d = &analysis.Diagnostic{ - Pos: call.Pos(), - End: call.End(), - Message: fn + " can be replaced with faster hex.EncodeToString", - SuggestedFixes: []analysis.SuggestedFix{ + d = newAnalysisDiagnostic( + "", // TODO: precise checker + call, + fn+" can be replaced with faster hex.EncodeToString", + []analysis.SuggestedFix{ { Message: "Use hex.EncodeToString", TextEdits: []analysis.TextEdit{ @@ -283,7 +292,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) { }, }, }, - } + ) case isSlice && isBasicType(s.Elem(), types.Uint8) && oneOf(verb, "%x"): fname := pass.Fset.File(call.Pos()).Name() removedFmtUsages[fname]++ @@ -292,11 +301,11 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) { neededPackages[fname] = make(map[string]bool) } neededPackages[fname]["encoding/hex"] = true - d = &analysis.Diagnostic{ - Pos: call.Pos(), - End: call.End(), - Message: fn + " can be replaced with faster hex.EncodeToString", - SuggestedFixes: []analysis.SuggestedFix{ + d = newAnalysisDiagnostic( + "", // TODO: precise checker + call, + fn+" can be replaced with faster hex.EncodeToString", + []analysis.SuggestedFix{ { Message: "Use hex.EncodeToString", TextEdits: []analysis.TextEdit{{ @@ -306,7 +315,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) { }}, }, }, - } + ) case isBasicType(valueType, types.Int8, types.Int16, types.Int32) && oneOf(verb, "%v", "%d") && n.intConv: fname := pass.Fset.File(call.Pos()).Name() @@ -316,11 +325,11 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) { neededPackages[fname] = make(map[string]bool) } neededPackages[fname]["strconv"] = true - d = &analysis.Diagnostic{ - Pos: call.Pos(), - End: call.End(), - Message: fn + " can be replaced with faster strconv.Itoa", - SuggestedFixes: []analysis.SuggestedFix{ + d = newAnalysisDiagnostic( + intConversionChecker, + call, + fn+" can be replaced with faster strconv.Itoa", + []analysis.SuggestedFix{ { Message: "Use strconv.Itoa", TextEdits: []analysis.TextEdit{ @@ -337,7 +346,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) { }, }, }, - } + ) case isBasicType(valueType, types.Int) && oneOf(verb, "%v", "%d"): fname := pass.Fset.File(call.Pos()).Name() removedFmtUsages[fname]++ @@ -346,11 +355,11 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) { neededPackages[fname] = make(map[string]bool) } neededPackages[fname]["strconv"] = true - d = &analysis.Diagnostic{ - Pos: call.Pos(), - End: call.End(), - Message: fn + " can be replaced with faster strconv.Itoa", - SuggestedFixes: []analysis.SuggestedFix{ + d = newAnalysisDiagnostic( + "", // TODO: precise checker + call, + fn+" can be replaced with faster strconv.Itoa", + []analysis.SuggestedFix{ { Message: "Use strconv.Itoa", TextEdits: []analysis.TextEdit{{ @@ -360,7 +369,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) { }}, }, }, - } + ) case isBasicType(valueType, types.Int64) && oneOf(verb, "%v", "%d"): fname := pass.Fset.File(call.Pos()).Name() removedFmtUsages[fname]++ @@ -369,11 +378,11 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) { neededPackages[fname] = make(map[string]bool) } neededPackages[fname]["strconv"] = true - d = &analysis.Diagnostic{ - Pos: call.Pos(), - End: call.End(), - Message: fn + " can be replaced with faster strconv.FormatInt", - SuggestedFixes: []analysis.SuggestedFix{ + d = newAnalysisDiagnostic( + "", // TODO: precise checker + call, + fn+" can be replaced with faster strconv.FormatInt", + []analysis.SuggestedFix{ { Message: "Use strconv.FormatInt", TextEdits: []analysis.TextEdit{ @@ -390,7 +399,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) { }, }, }, - } + ) case isBasicType(valueType, types.Uint8, types.Uint16, types.Uint32, types.Uint) && oneOf(verb, "%v", "%d", "%x") && n.intConv: base := []byte("), 10") @@ -404,11 +413,11 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) { neededPackages[fname] = make(map[string]bool) } neededPackages[fname]["strconv"] = true - d = &analysis.Diagnostic{ - Pos: call.Pos(), - End: call.End(), - Message: fn + " can be replaced with faster strconv.FormatUint", - SuggestedFixes: []analysis.SuggestedFix{ + d = newAnalysisDiagnostic( + intConversionChecker, + call, + fn+" can be replaced with faster strconv.FormatUint", + []analysis.SuggestedFix{ { Message: "Use strconv.FormatUint", TextEdits: []analysis.TextEdit{ @@ -425,7 +434,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) { }, }, }, - } + ) case isBasicType(valueType, types.Uint64) && oneOf(verb, "%v", "%d", "%x"): base := []byte(", 10") if verb == "%x" { @@ -438,11 +447,11 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) { neededPackages[fname] = make(map[string]bool) } neededPackages[fname]["strconv"] = true - d = &analysis.Diagnostic{ - Pos: call.Pos(), - End: call.End(), - Message: fn + " can be replaced with faster strconv.FormatUint", - SuggestedFixes: []analysis.SuggestedFix{ + d = newAnalysisDiagnostic( + "", // TODO: precise checker + call, + fn+" can be replaced with faster strconv.FormatUint", + []analysis.SuggestedFix{ { Message: "Use strconv.FormatUint", TextEdits: []analysis.TextEdit{ @@ -459,7 +468,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) { }, }, }, - } + ) case isBasicType(valueType, types.String) && fn == "fmt.Sprintf" && isConcatable(verb): var fix string if strings.HasSuffix(verb, "%s") { @@ -473,11 +482,11 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) { } fname := pass.Fset.File(call.Pos()).Name() removedFmtUsages[fname]++ - d = &analysis.Diagnostic{ - Pos: call.Pos(), - End: call.End(), - Message: fn + " can be replaced with string concatenation", - SuggestedFixes: []analysis.SuggestedFix{ + d = newAnalysisDiagnostic( + strconcatChecker, + call, + fn+" can be replaced with string concatenation", + []analysis.SuggestedFix{ { Message: "Use string concatenation", TextEdits: []analysis.TextEdit{{ @@ -487,7 +496,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) { }}, }, }, - } + ) } if d != nil { @@ -553,11 +562,11 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) { for _, k := range keys { fix = fix + "\n\t\"" + k + `"` } - pass.Report(analysis.Diagnostic{ - Pos: gd.Pos(), - End: gd.End(), - Message: "Fix imports", - SuggestedFixes: []analysis.SuggestedFix{ + pass.Report(*newAnalysisDiagnostic( + fiximportsChecker, + gd, + "Fix imports", + []analysis.SuggestedFix{ { Message: "Fix imports", TextEdits: []analysis.TextEdit{{ @@ -566,7 +575,7 @@ func (n *perfSprint) run(pass *analysis.Pass) (interface{}, error) { NewText: []byte(fix), }}, }, - }}) + })) } }) } diff --git a/analyzer/diagnostic.go b/analyzer/diagnostic.go new file mode 100644 index 0000000..0d05f67 --- /dev/null +++ b/analyzer/diagnostic.go @@ -0,0 +1,26 @@ +package analyzer + +import ( + "golang.org/x/tools/go/analysis" +) + +func newAnalysisDiagnostic( + checker string, + analysisRange analysis.Range, + message string, + suggestedFixes []analysis.SuggestedFix, +) *analysis.Diagnostic { + d := analysis.Diagnostic{ + Pos: analysisRange.Pos(), + End: analysisRange.End(), + Message: message, + SuggestedFixes: suggestedFixes, + } + if checker != "" { + d.Category = checker + d.Message = checker + ": " + message + } else { + d.Message = message + } + return &d +}