Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(runner): add suggested edit text from linter in display issue text #2135

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pkg/golinters/goanalysis/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 7 additions & 4 deletions pkg/golinters/goanalysis/runners.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
27 changes: 27 additions & 0 deletions pkg/printers/codeclimate.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"strings"

"github.com/golangci/golangci-lint/pkg/logutils"
"github.com/golangci/golangci-lint/pkg/result"
Expand All @@ -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 {
Expand Down Expand Up @@ -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
}
Expand All @@ -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
}
54 changes: 46 additions & 8 deletions pkg/printers/html.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,25 @@ const templateContent = `<!doctype html>
}
}

class SuggestedEdit extends React.Component {
render() {
if (this.props.data.SuggestedFix && this.props.data.SuggestedFix.length) {
return (
<div>
<div>
<strong>Suggested Edits</strong>
</div>
<div className="highlight">
<Highlight code={this.props.data.SuggestedFix}/>
</div>
</div>
)
}

return null
}
}

class Issue extends React.Component {
render() {
return (
Expand All @@ -77,6 +96,7 @@ const templateContent = `<!doctype html>
<div className="highlight">
<Highlight code={this.props.data.Code}/>
</div>
<SuggestedEdit data={this.props.data}/>
</div>
);
}
Expand Down Expand Up @@ -117,10 +137,11 @@ const templateContent = `<!doctype html>
</html>`

type htmlIssue struct {
Title string
Pos string
Linter string
Code string
Title string
Pos string
SuggestedFix string
Linter string
Code string
}

type HTML struct{}
Expand All @@ -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"),
})
}

Expand All @@ -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
}
27 changes: 25 additions & 2 deletions pkg/printers/junitxml.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package printers
import (
"context"
"encoding/xml"
"fmt"
"strings"

"github.com/golangci/golangci-lint/pkg/logutils"
Expand Down Expand Up @@ -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 {
Expand All @@ -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,
},
}

Expand All @@ -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 ""
}
18 changes: 18 additions & 0 deletions pkg/printers/tab.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"io"
"strings"
"text/tabwriter"

"github.com/fatih/color"
Expand Down Expand Up @@ -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 {
Expand All @@ -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)
}
17 changes: 17 additions & 0 deletions pkg/printers/text.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
43 changes: 41 additions & 2 deletions pkg/result/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"go/token"

"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/packages"
)

Expand All @@ -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
Copy link
Author

@ifaisalalam ifaisalalam Jul 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using custom SuggestedFix struct because analysis.SuggestedFix.TextEdits[i].NewText is type []byte which is not serialized properly to JSON/XML.


Severity string

Expand Down