From 34d4b45b4a7de1c61ccfc137b9f5a636478df538 Mon Sep 17 00:00:00 2001 From: Faisal Alam Date: Wed, 28 Jul 2021 20:17:55 +0530 Subject: [PATCH 1/4] fix(runner): add suggested edit text from linter in display issue text The tool did not print suggested edits by the linter when displaying the issues. If there is suggested edits by the linter, it should be displayed along with the issue. Closes #2134 --- pkg/golinters/goanalysis/issue.go | 1 + pkg/golinters/goanalysis/runners.go | 11 +++++--- pkg/printers/tab.go | 18 ++++++++++++ pkg/printers/text.go | 17 ++++++++++++ pkg/result/issue.go | 43 +++++++++++++++++++++++++++-- 5 files changed, 84 insertions(+), 6 deletions(-) diff --git a/pkg/golinters/goanalysis/issue.go b/pkg/golinters/goanalysis/issue.go index f331a3ab9f1a..34adc7bac4fa 100644 --- a/pkg/golinters/goanalysis/issue.go +++ b/pkg/golinters/goanalysis/issue.go @@ -23,6 +23,7 @@ func NewIssue(i *result.Issue, pass *analysis.Pass) Issue { type EncodingIssue struct { FromLinter string Text string + SuggestedFixes []result.SuggestedFix Pos token.Position LineRange *result.Range Replacement *result.Replacement diff --git a/pkg/golinters/goanalysis/runners.go b/pkg/golinters/goanalysis/runners.go index 7e4cf902e79c..bbb1410d16a9 100644 --- a/pkg/golinters/goanalysis/runners.go +++ b/pkg/golinters/goanalysis/runners.go @@ -98,10 +98,11 @@ func buildIssues(diags []Diagnostic, linterNameBuilder func(diag *Diagnostic) st } issues = append(issues, result.Issue{ - FromLinter: linterName, - Text: text, - Pos: diag.Position, - Pkg: diag.Pkg, + FromLinter: linterName, + Text: text, + SuggestedFixes: result.BuildSuggestedFixes(diag.SuggestedFixes), + Pos: diag.Position, + Pkg: diag.Pkg, }) if len(diag.Related) > 0 { @@ -150,6 +151,7 @@ func saveIssuesToCache(allPkgs []*packages.Package, pkgsFromCache map[*packages. encodedIssues = append(encodedIssues, EncodingIssue{ FromLinter: i.FromLinter, Text: i.Text, + SuggestedFixes: i.SuggestedFixes, Pos: i.Pos, LineRange: i.LineRange, Replacement: i.Replacement, @@ -221,6 +223,7 @@ func loadIssuesFromCache(pkgs []*packages.Package, lintCtx *linter.Context, issues = append(issues, result.Issue{ FromLinter: i.FromLinter, Text: i.Text, + SuggestedFixes: i.SuggestedFixes, Pos: i.Pos, LineRange: i.LineRange, Replacement: i.Replacement, diff --git a/pkg/printers/tab.go b/pkg/printers/tab.go index d3cdce673dd8..d789d69cf32d 100644 --- a/pkg/printers/tab.go +++ b/pkg/printers/tab.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "io" + "strings" "text/tabwriter" "github.com/fatih/color" @@ -34,6 +35,7 @@ func (p *Tab) Print(ctx context.Context, issues []result.Issue) error { for i := range issues { p.printIssue(&issues[i], w) + p.printSuggestedEdits(&issues[i], w) } if err := w.Flush(); err != nil { @@ -56,3 +58,19 @@ func (p Tab) printIssue(i *result.Issue, w io.Writer) { fmt.Fprintf(w, "%s\t%s\n", pos, text) } + +func (p Tab) printSuggestedEdits(i *result.Issue, w io.Writer) { + var text string + if len(i.SuggestedFixes) > 0 { + for _, fix := range i.SuggestedFixes { + text += p.SprintfColored(color.FgRed, "%s\n", strings.TrimSpace(fix.Message)) + var suggestedEdits []string + for _, textEdit := range fix.TextEdits { + suggestedEdits = append(suggestedEdits, strings.TrimSpace(textEdit.NewText)) + } + text += strings.Join(suggestedEdits, "\n") + "\n" + } + } + + fmt.Fprintln(w, text) +} diff --git a/pkg/printers/text.go b/pkg/printers/text.go index 1814528884c4..cf1a7e111bb2 100644 --- a/pkg/printers/text.go +++ b/pkg/printers/text.go @@ -47,6 +47,7 @@ func (p *Text) Print(ctx context.Context, issues []result.Issue) error { p.printSourceCode(&issues[i]) p.printUnderLinePointer(&issues[i]) + p.printSuggestedEdits(&issues[i]) } return nil @@ -89,3 +90,19 @@ func (p Text) printUnderLinePointer(i *result.Issue) { fmt.Fprintf(logutils.StdOut, "%s%s\n", string(prefixRunes), p.SprintfColored(color.FgYellow, "^")) } + +func (p Text) printSuggestedEdits(i *result.Issue) { + var text string + if len(i.SuggestedFixes) > 0 { + for _, fix := range i.SuggestedFixes { + text += p.SprintfColored(color.FgRed, "%s\n", strings.TrimSpace(fix.Message)) + var suggestedEdits []string + for _, textEdit := range fix.TextEdits { + suggestedEdits = append(suggestedEdits, strings.TrimSpace(textEdit.NewText)) + } + text += strings.Join(suggestedEdits, "\n") + "\n" + } + } + + fmt.Fprintln(logutils.StdOut, text) +} diff --git a/pkg/result/issue.go b/pkg/result/issue.go index 707a2b17cd95..9e6332ad24a1 100644 --- a/pkg/result/issue.go +++ b/pkg/result/issue.go @@ -5,6 +5,7 @@ import ( "fmt" "go/token" + "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/packages" ) @@ -24,9 +25,47 @@ type InlineFix struct { NewString string } +type SuggestedFix struct { + // A description for this suggested fix to be shown to a user deciding + // whether to accept it. + Message string + TextEdits []TextEdit +} + +type TextEdit struct { + Pos token.Pos + End token.Pos + NewText string +} + +func BuildSuggestedFixes(fixes []analysis.SuggestedFix) []SuggestedFix { + if len(fixes) == 0 { + return nil + } + + suggestedFixes := make([]SuggestedFix, 0, len(fixes)) + for _, fix := range fixes { + textEdits := make([]TextEdit, 0, len(fix.TextEdits)) + for _, edit := range fix.TextEdits { + textEdits = append(textEdits, TextEdit{ + Pos: edit.Pos, + End: edit.End, + NewText: string(edit.NewText), + }) + } + suggestedFixes = append(suggestedFixes, SuggestedFix{ + Message: fix.Message, + TextEdits: textEdits, + }) + } + + return suggestedFixes +} + type Issue struct { - FromLinter string - Text string + FromLinter string + Text string + SuggestedFixes []SuggestedFix Severity string From f992976600806a1afc6fac5c605726ce77d8d608 Mon Sep 17 00:00:00 2001 From: Faisal Alam Date: Thu, 29 Jul 2021 15:02:23 +0530 Subject: [PATCH 2/4] fix(html-printer): display suggested edit in output --- pkg/printers/html.go | 54 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 8 deletions(-) diff --git a/pkg/printers/html.go b/pkg/printers/html.go index 65ab753bd512..e4f18eee2c59 100644 --- a/pkg/printers/html.go +++ b/pkg/printers/html.go @@ -59,6 +59,25 @@ const templateContent = ` } } + class SuggestedEdit extends React.Component { + render() { + if (this.props.data.SuggestedFix && this.props.data.SuggestedFix.length) { + return ( +
+
+ Suggested Edits +
+
+ +
+
+ ) + } + + return null + } + } + class Issue extends React.Component { render() { return ( @@ -77,6 +96,7 @@ const templateContent = `
+ ); } @@ -117,10 +137,11 @@ const templateContent = ` ` type htmlIssue struct { - Title string - Pos string - Linter string - Code string + Title string + Pos string + SuggestedFix string + Linter string + Code string } type HTML struct{} @@ -139,10 +160,11 @@ func (h HTML) Print(_ context.Context, issues []result.Issue) error { } htmlIssues = append(htmlIssues, htmlIssue{ - Title: strings.TrimSpace(issues[i].Text), - Pos: pos, - Linter: issues[i].FromLinter, - Code: strings.Join(issues[i].SourceLines, "\n"), + Title: strings.TrimSpace(issues[i].Text), + Pos: pos, + SuggestedFix: h.getSuggestedFix(&issues[i]), + Linter: issues[i].FromLinter, + Code: strings.Join(issues[i].SourceLines, "\n"), }) } @@ -153,3 +175,19 @@ func (h HTML) Print(_ context.Context, issues []result.Issue) error { return t.Execute(logutils.StdOut, struct{ Issues []htmlIssue }{Issues: htmlIssues}) } + +func (h HTML) getSuggestedFix(i *result.Issue) string { + var text string + if len(i.SuggestedFixes) > 0 { + for _, fix := range i.SuggestedFixes { + text += fmt.Sprintf("%s\n", strings.TrimSpace(fix.Message)) + var suggestedEdits []string + for _, textEdit := range fix.TextEdits { + suggestedEdits = append(suggestedEdits, strings.TrimSpace(textEdit.NewText)) + } + text += strings.Join(suggestedEdits, "\n") + "\n" + } + } + + return text +} From abc3f26748559390ecfd7caeae90707c262a1679 Mon Sep 17 00:00:00 2001 From: Faisal Alam Date: Thu, 29 Jul 2021 16:06:39 +0530 Subject: [PATCH 3/4] fix(junitxml-printer): display suggested edit in output --- pkg/printers/junitxml.go | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/pkg/printers/junitxml.go b/pkg/printers/junitxml.go index 9277cd66f2fe..44aa50d6a92b 100644 --- a/pkg/printers/junitxml.go +++ b/pkg/printers/junitxml.go @@ -3,6 +3,7 @@ package printers import ( "context" "encoding/xml" + "fmt" "strings" "github.com/golangci/golangci-lint/pkg/logutils" @@ -41,7 +42,7 @@ func NewJunitXML() *JunitXML { return &JunitXML{} } -func (JunitXML) Print(ctx context.Context, issues []result.Issue) error { +func (j JunitXML) Print(ctx context.Context, issues []result.Issue) error { suites := make(map[string]testSuiteXML) // use a map to group by file for ind := range issues { @@ -52,12 +53,14 @@ func (JunitXML) Print(ctx context.Context, issues []result.Issue) error { testSuite.Tests++ testSuite.Failures++ + content := strings.Join(i.SourceLines, "\n") + content += j.getSuggestedFix(&issues[ind]) tc := testCaseXML{ Name: i.FromLinter, ClassName: i.Pos.String(), Failure: failureXML{ Message: i.Text, - Content: strings.Join(i.SourceLines, "\n"), + Content: content, }, } @@ -77,3 +80,23 @@ func (JunitXML) Print(ctx context.Context, issues []result.Issue) error { } return nil } + +func (j JunitXML) getSuggestedFix(i *result.Issue) string { + var text string + if len(i.SuggestedFixes) > 0 { + for _, fix := range i.SuggestedFixes { + text += fmt.Sprintf("%s\n", strings.TrimSpace(fix.Message)) + var suggestedEdits []string + for _, textEdit := range fix.TextEdits { + suggestedEdits = append(suggestedEdits, strings.TrimSpace(textEdit.NewText)) + } + text += strings.Join(suggestedEdits, "\n") + "\n" + } + } + + if text != "" { + return fmt.Sprintf("\n\n%s", text) + } + + return "" +} From 8a08c5e0f3700ef0939343e0c09cccd94e7481be Mon Sep 17 00:00:00 2001 From: Faisal Alam Date: Thu, 29 Jul 2021 16:50:24 +0530 Subject: [PATCH 4/4] fix(codeclimate-printer): display suggested edit in output --- pkg/printers/codeclimate.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/pkg/printers/codeclimate.go b/pkg/printers/codeclimate.go index 35a22ce99a72..afeca12334a2 100644 --- a/pkg/printers/codeclimate.go +++ b/pkg/printers/codeclimate.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "strings" "github.com/golangci/golangci-lint/pkg/logutils" "github.com/golangci/golangci-lint/pkg/result" @@ -13,6 +14,7 @@ import ( // It is just enough to support GitLab CI Code Quality - https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html type CodeClimateIssue struct { Description string `json:"description"` + Content string `json:"content,omitempty"` Severity string `json:"severity,omitempty"` Fingerprint string `json:"fingerprint"` Location struct { @@ -40,6 +42,11 @@ func (p CodeClimate) Print(ctx context.Context, issues []result.Issue) error { codeClimateIssue.Location.Lines.Begin = issue.Pos.Line codeClimateIssue.Fingerprint = issue.Fingerprint() + content := p.buildContentString(&issues[i]) + if content != "" { + codeClimateIssue.Content = content + } + if issue.Severity != "" { codeClimateIssue.Severity = issue.Severity } @@ -55,3 +62,23 @@ func (p CodeClimate) Print(ctx context.Context, issues []result.Issue) error { fmt.Fprint(logutils.StdOut, string(outputJSON)) return nil } + +func (p CodeClimate) buildContentString(issue *result.Issue) string { + if len(issue.SuggestedFixes) == 0 { + return "" + } + + var text string + for _, fix := range issue.SuggestedFixes { + text += fmt.Sprintf("%s\n", strings.TrimSpace(fix.Message)) + var suggestedEdits []string + for _, textEdit := range fix.TextEdits { + suggestedEdits = append(suggestedEdits, strings.TrimSpace(textEdit.NewText)) + } + if len(suggestedEdits) > 0 { + text += "```\n" + strings.Join(suggestedEdits, "\n") + "\n" + "```\n" + } + } + + return text +}