mirror of
https://github.com/mgechev/revive.git
synced 2024-11-21 17:16:40 +02:00
formatters return output (#921)
Some of the formatters were writing directly to stdout instead of returning the output. That made them more difficult to use them with revivelib. This PR updates those formatters to write to a buffer and return the resulting string.
This commit is contained in:
parent
25ae73a67a
commit
cb72bd880d
@ -1,6 +1,7 @@
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/mgechev/revive/lint"
|
||||
@ -19,8 +20,9 @@ func (*Default) Name() string {
|
||||
|
||||
// Format formats the failures gotten from the lint.
|
||||
func (*Default) Format(failures <-chan lint.Failure, _ lint.Config) (string, error) {
|
||||
var buf bytes.Buffer
|
||||
for failure := range failures {
|
||||
fmt.Printf("%v: %s\n", failure.Position.Start, failure.Failure)
|
||||
fmt.Fprintf(&buf, "%v: %s\n", failure.Position.Start, failure.Failure)
|
||||
}
|
||||
return "", nil
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
165
formatter/formatter_test.go
Normal file
165
formatter/formatter_test.go
Normal file
@ -0,0 +1,165 @@
|
||||
package formatter_test
|
||||
|
||||
import (
|
||||
"go/token"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mgechev/revive/formatter"
|
||||
"github.com/mgechev/revive/lint"
|
||||
)
|
||||
|
||||
func TestFormatter(t *testing.T) {
|
||||
lintFailure := lint.Failure{
|
||||
Failure: "test failure",
|
||||
RuleName: "rule",
|
||||
Category: "cat",
|
||||
Position: lint.FailurePosition{
|
||||
Start: token.Position{
|
||||
Filename: "test.go",
|
||||
Line: 2,
|
||||
Column: 5,
|
||||
},
|
||||
End: token.Position{
|
||||
Filename: "test.go",
|
||||
Line: 2,
|
||||
Column: 10,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, td := range []struct {
|
||||
formatter lint.Formatter
|
||||
want string
|
||||
}{
|
||||
{
|
||||
formatter: &formatter.Checkstyle{},
|
||||
want: `
|
||||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<checkstyle version="5.0">
|
||||
<file name="test.go">
|
||||
<error line="2" column="5" message="test failure (confidence 0)" severity="warning" source="revive/rule"/>
|
||||
</file>
|
||||
</checkstyle>
|
||||
`,
|
||||
},
|
||||
{
|
||||
formatter: &formatter.Default{},
|
||||
want: `test.go:2:5: test failure`,
|
||||
},
|
||||
{
|
||||
formatter: &formatter.Friendly{},
|
||||
want: `
|
||||
⚠ https://revive.run/r#rule test failure
|
||||
test.go:2:5
|
||||
|
||||
⚠ 1 problem (0 errors, 1 warning)
|
||||
|
||||
Warnings:
|
||||
1 rule
|
||||
`,
|
||||
},
|
||||
{
|
||||
formatter: &formatter.JSON{},
|
||||
want: `[{"Severity":"warning","Failure":"test failure","RuleName":"rule","Category":"cat","Position":{"Start":{"Filename":"test.go","Offset":0,"Line":2,"Column":5},"End":{"Filename":"test.go","Offset":0,"Line":2,"Column":10}},"Confidence":0,"ReplacementLine":""}]`,
|
||||
},
|
||||
{
|
||||
formatter: &formatter.NDJSON{},
|
||||
want: `{"Severity":"warning","Failure":"test failure","RuleName":"rule","Category":"cat","Position":{"Start":{"Filename":"test.go","Offset":0,"Line":2,"Column":5},"End":{"Filename":"test.go","Offset":0,"Line":2,"Column":10}},"Confidence":0,"ReplacementLine":""}`,
|
||||
},
|
||||
{
|
||||
formatter: &formatter.Plain{},
|
||||
want: `test.go:2:5: test failure https://revive.run/r#rule`,
|
||||
},
|
||||
{
|
||||
formatter: &formatter.Sarif{},
|
||||
want: `
|
||||
{
|
||||
"runs": [
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"locations": [
|
||||
{
|
||||
"physicalLocation": {
|
||||
"artifactLocation": {
|
||||
"uri": "test.go"
|
||||
},
|
||||
"region": {
|
||||
"startColumn": 5,
|
||||
"startLine": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"message": {
|
||||
"text": "test failure"
|
||||
},
|
||||
"ruleId": "rule"
|
||||
}
|
||||
],
|
||||
"tool": {
|
||||
"driver": {
|
||||
"informationUri": "https://revive.run",
|
||||
"name": "revive"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"version": "2.1.0"
|
||||
}
|
||||
`,
|
||||
},
|
||||
{
|
||||
formatter: &formatter.Stylish{},
|
||||
want: `
|
||||
test.go
|
||||
(2, 5) https://revive.run/r#rule test failure
|
||||
|
||||
|
||||
✖ 1 problem (0 errors) (1 warnings)
|
||||
`,
|
||||
},
|
||||
{
|
||||
formatter: &formatter.Unix{},
|
||||
want: `test.go:2:5: [rule] test failure`,
|
||||
},
|
||||
} {
|
||||
t.Run(td.formatter.Name(), func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
realStdout := os.Stdout
|
||||
fakeStdout, err := os.Create(dir + "/fakeStdout")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
os.Stdout = fakeStdout
|
||||
defer func() {
|
||||
os.Stdout = realStdout
|
||||
}()
|
||||
failures := make(chan lint.Failure, 10)
|
||||
failures <- lintFailure
|
||||
close(failures)
|
||||
output, err := td.formatter.Format(failures, lint.Config{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
os.Stdout = realStdout
|
||||
err = fakeStdout.Close()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
stdout, err := os.ReadFile(fakeStdout.Name())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(stdout) > 0 {
|
||||
t.Errorf("formatter wrote to stdout: %q", stdout)
|
||||
}
|
||||
got := strings.TrimSpace(output)
|
||||
want := strings.TrimSpace(td.want)
|
||||
if got != want {
|
||||
t.Errorf("got %q, want %q", got, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package formatter
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
|
||||
"github.com/fatih/color"
|
||||
@ -31,13 +32,14 @@ func (*Friendly) Name() string {
|
||||
|
||||
// Format formats the failures gotten from the lint.
|
||||
func (f *Friendly) Format(failures <-chan lint.Failure, config lint.Config) (string, error) {
|
||||
var buf bytes.Buffer
|
||||
errorMap := map[string]int{}
|
||||
warningMap := map[string]int{}
|
||||
totalErrors := 0
|
||||
totalWarnings := 0
|
||||
for failure := range failures {
|
||||
sev := severity(config, failure)
|
||||
f.printFriendlyFailure(failure, sev)
|
||||
f.printFriendlyFailure(&buf, failure, sev)
|
||||
if sev == lint.SeverityWarning {
|
||||
warningMap[failure.RuleName]++
|
||||
totalWarnings++
|
||||
@ -47,29 +49,29 @@ func (f *Friendly) Format(failures <-chan lint.Failure, config lint.Config) (str
|
||||
totalErrors++
|
||||
}
|
||||
}
|
||||
f.printSummary(totalErrors, totalWarnings)
|
||||
f.printStatistics(color.RedString("Errors:"), errorMap)
|
||||
f.printStatistics(color.YellowString("Warnings:"), warningMap)
|
||||
return "", nil
|
||||
f.printSummary(&buf, totalErrors, totalWarnings)
|
||||
f.printStatistics(&buf, color.RedString("Errors:"), errorMap)
|
||||
f.printStatistics(&buf, color.YellowString("Warnings:"), warningMap)
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func (f *Friendly) printFriendlyFailure(failure lint.Failure, severity lint.Severity) {
|
||||
f.printHeaderRow(failure, severity)
|
||||
f.printFilePosition(failure)
|
||||
fmt.Println()
|
||||
fmt.Println()
|
||||
func (f *Friendly) printFriendlyFailure(w io.Writer, failure lint.Failure, severity lint.Severity) {
|
||||
f.printHeaderRow(w, failure, severity)
|
||||
f.printFilePosition(w, failure)
|
||||
fmt.Fprintln(w)
|
||||
fmt.Fprintln(w)
|
||||
}
|
||||
|
||||
func (f *Friendly) printHeaderRow(failure lint.Failure, severity lint.Severity) {
|
||||
func (f *Friendly) printHeaderRow(w io.Writer, failure lint.Failure, severity lint.Severity) {
|
||||
emoji := getWarningEmoji()
|
||||
if severity == lint.SeverityError {
|
||||
emoji = getErrorEmoji()
|
||||
}
|
||||
fmt.Print(f.table([][]string{{emoji, "https://revive.run/r#" + failure.RuleName, color.GreenString(failure.Failure)}}))
|
||||
fmt.Fprint(w, f.table([][]string{{emoji, "https://revive.run/r#" + failure.RuleName, color.GreenString(failure.Failure)}}))
|
||||
}
|
||||
|
||||
func (*Friendly) printFilePosition(failure lint.Failure) {
|
||||
fmt.Printf(" %s:%d:%d", failure.GetFilename(), failure.Position.Start.Line, failure.Position.Start.Column)
|
||||
func (*Friendly) printFilePosition(w io.Writer, failure lint.Failure) {
|
||||
fmt.Fprintf(w, " %s:%d:%d", failure.GetFilename(), failure.Position.Start.Line, failure.Position.Start.Column)
|
||||
}
|
||||
|
||||
type statEntry struct {
|
||||
@ -77,7 +79,7 @@ type statEntry struct {
|
||||
failures int
|
||||
}
|
||||
|
||||
func (*Friendly) printSummary(errors, warnings int) {
|
||||
func (*Friendly) printSummary(w io.Writer, errors, warnings int) {
|
||||
emoji := getWarningEmoji()
|
||||
if errors > 0 {
|
||||
emoji = getErrorEmoji()
|
||||
@ -96,18 +98,18 @@ func (*Friendly) printSummary(errors, warnings int) {
|
||||
}
|
||||
str := fmt.Sprintf("%d %s (%d %s, %d %s)", errors+warnings, problemsLabel, errors, errorsLabel, warnings, warningsLabel)
|
||||
if errors > 0 {
|
||||
fmt.Printf("%s %s\n", emoji, color.RedString(str))
|
||||
fmt.Println()
|
||||
fmt.Fprintf(w, "%s %s\n", emoji, color.RedString(str))
|
||||
fmt.Fprintln(w)
|
||||
return
|
||||
}
|
||||
if warnings > 0 {
|
||||
fmt.Printf("%s %s\n", emoji, color.YellowString(str))
|
||||
fmt.Println()
|
||||
fmt.Fprintf(w, "%s %s\n", emoji, color.YellowString(str))
|
||||
fmt.Fprintln(w)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Friendly) printStatistics(header string, stats map[string]int) {
|
||||
func (f *Friendly) printStatistics(w io.Writer, header string, stats map[string]int) {
|
||||
if len(stats) == 0 {
|
||||
return
|
||||
}
|
||||
@ -122,8 +124,8 @@ func (f *Friendly) printStatistics(header string, stats map[string]int) {
|
||||
for _, entry := range data {
|
||||
formatted = append(formatted, []string{color.GreenString(fmt.Sprintf("%d", entry.failures)), entry.name})
|
||||
}
|
||||
fmt.Println(header)
|
||||
fmt.Println(f.table(formatted))
|
||||
fmt.Fprintln(w, header)
|
||||
fmt.Fprintln(w, f.table(formatted))
|
||||
}
|
||||
|
||||
func (*Friendly) table(rows [][]string) string {
|
||||
|
@ -1,8 +1,8 @@
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
"github.com/mgechev/revive/lint"
|
||||
)
|
||||
@ -20,7 +20,8 @@ func (*NDJSON) Name() string {
|
||||
|
||||
// Format formats the failures gotten from the lint.
|
||||
func (*NDJSON) Format(failures <-chan lint.Failure, config lint.Config) (string, error) {
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
var buf bytes.Buffer
|
||||
enc := json.NewEncoder(&buf)
|
||||
for failure := range failures {
|
||||
obj := jsonObject{}
|
||||
obj.Severity = severity(config, failure)
|
||||
@ -30,5 +31,5 @@ func (*NDJSON) Format(failures <-chan lint.Failure, config lint.Config) (string,
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/mgechev/revive/lint"
|
||||
@ -19,8 +20,9 @@ func (*Plain) Name() string {
|
||||
|
||||
// Format formats the failures gotten from the lint.
|
||||
func (*Plain) Format(failures <-chan lint.Failure, _ lint.Config) (string, error) {
|
||||
var buf bytes.Buffer
|
||||
for failure := range failures {
|
||||
fmt.Printf("%v: %s %s\n", failure.Position.Start, failure.Failure, "https://revive.run/r#"+failure.RuleName)
|
||||
fmt.Fprintf(&buf, "%v: %s %s\n", failure.Position.Start, failure.Failure, "https://revive.run/r#"+failure.RuleName)
|
||||
}
|
||||
return "", nil
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/mgechev/revive/lint"
|
||||
@ -8,7 +9,8 @@ import (
|
||||
|
||||
// Unix is an implementation of the Formatter interface
|
||||
// which formats the errors to a simple line based error format
|
||||
// main.go:24:9: [errorf] should replace errors.New(fmt.Sprintf(...)) with fmt.Errorf(...)
|
||||
//
|
||||
// main.go:24:9: [errorf] should replace errors.New(fmt.Sprintf(...)) with fmt.Errorf(...)
|
||||
type Unix struct {
|
||||
Metadata lint.FormatterMetadata
|
||||
}
|
||||
@ -20,8 +22,9 @@ func (*Unix) Name() string {
|
||||
|
||||
// Format formats the failures gotten from the lint.
|
||||
func (*Unix) Format(failures <-chan lint.Failure, _ lint.Config) (string, error) {
|
||||
var buf bytes.Buffer
|
||||
for failure := range failures {
|
||||
fmt.Printf("%v: [%s] %s\n", failure.Position.Start, failure.RuleName, failure.Failure)
|
||||
fmt.Fprintf(&buf, "%v: [%s] %s\n", failure.Position.Start, failure.RuleName, failure.Failure)
|
||||
}
|
||||
return "", nil
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user