diff --git a/Gopkg.lock b/Gopkg.lock index 1398094f..a37d7aee 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -146,7 +146,7 @@ [[projects]] branch = "master" - digest = "1:7d1f12e47120a4e50790f423446888ef01fcc1a3c17fcf9d5f1be7774cfd963c" + digest = "1:bfaf14a1dd31e57f9b433739af5f0411558d9ba90566c7342c02da9a48ea8e75" name = "github.com/golangci/govet" packages = [ ".", @@ -154,7 +154,7 @@ "lib/whitelist", ] pruneopts = "UT" - revision = "e3b43b6cb1916e0000f244f26ee752733e544429" + revision = "44ddbe260190d79165f4150b828650780405d801" [[projects]] branch = "master" diff --git a/pkg/golinters/gas.go b/pkg/golinters/gas.go index 4f78f6b4..31321ca9 100644 --- a/pkg/golinters/gas.go +++ b/pkg/golinters/gas.go @@ -39,7 +39,7 @@ func (lint Gas) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issu res := make([]result.Issue, 0, len(issues)) for _, i := range issues { - text := fmt.Sprintf("%s: %s", i.RuleID, i.What) // TODO: use severity and confidence + text := fmt.Sprintf("%s: %s", i.RuleID, markIdentifiers(i.What)) // TODO: use severity and confidence var r *result.Range line, err := strconv.Atoi(i.Line) if err != nil { diff --git a/pkg/golinters/gofmt.go b/pkg/golinters/gofmt.go index 58d954bb..3a5dd314 100644 --- a/pkg/golinters/gofmt.go +++ b/pkg/golinters/gofmt.go @@ -83,9 +83,9 @@ func (g Gofmt) extractIssuesFromPatch(patch string, log logutils.Log) ([]result. } } - text := "File is not gofmt-ed with -s" + text := "File is not `gofmt`-ed with `-s`" if g.UseGoimports { - text = "File is not goimports-ed" + text = "File is not `goimports`-ed" } i := result.Issue{ FromLinter: g.Name(), diff --git a/pkg/golinters/golint.go b/pkg/golinters/golint.go index a230db54..170337c4 100644 --- a/pkg/golinters/golint.go +++ b/pkg/golinters/golint.go @@ -60,7 +60,7 @@ func (g Golint) lintPkg(minConfidence float64, files []*ast.File, fset *token.Fi if p.Confidence >= minConfidence { issues = append(issues, result.Issue{ Pos: p.Position, - Text: p.Text, + Text: markIdentifiers(p.Text), FromLinter: g.Name(), }) // TODO: use p.Link and p.Category diff --git a/pkg/golinters/govet.go b/pkg/golinters/govet.go index 257cf1a4..e8d83564 100644 --- a/pkg/golinters/govet.go +++ b/pkg/golinters/govet.go @@ -53,7 +53,7 @@ func (g Govet) Run(ctx context.Context, lintCtx *linter.Context) ([]result.Issue for _, i := range govetIssues { res = append(res, result.Issue{ Pos: i.Pos, - Text: i.Message, + Text: markIdentifiers(i.Message), FromLinter: g.Name(), }) } diff --git a/pkg/golinters/interfacer.go b/pkg/golinters/interfacer.go index f3652e49..7395706f 100644 --- a/pkg/golinters/interfacer.go +++ b/pkg/golinters/interfacer.go @@ -37,7 +37,7 @@ func (lint Interfacer) Run(ctx context.Context, lintCtx *linter.Context) ([]resu pos := lintCtx.SSAProgram.Fset.Position(i.Pos()) res = append(res, result.Issue{ Pos: pos, - Text: i.Message(), + Text: markIdentifiers(i.Message()), FromLinter: lint.Name(), }) } diff --git a/pkg/golinters/lll.go b/pkg/golinters/lll.go index 0a8ad046..09b1f926 100644 --- a/pkg/golinters/lll.go +++ b/pkg/golinters/lll.go @@ -43,7 +43,6 @@ func (lint Lll) getIssuesForFile(filename string, maxLineLen int, tabSpaces stri Pos: token.Position{ Filename: filename, Line: lineNumber, - Column: 1, }, Text: fmt.Sprintf("line is %d characters", lineLen), FromLinter: lint.Name(), diff --git a/pkg/golinters/megacheck.go b/pkg/golinters/megacheck.go index bd3e081c..17245c32 100644 --- a/pkg/golinters/megacheck.go +++ b/pkg/golinters/megacheck.go @@ -112,7 +112,7 @@ func (m Megacheck) Run(ctx context.Context, lintCtx *linter.Context) ([]result.I for _, i := range issues { res = append(res, result.Issue{ Pos: i.Position, - Text: i.Text, + Text: markIdentifiers(i.Text), FromLinter: m.Name(), }) } diff --git a/pkg/golinters/typecheck.go b/pkg/golinters/typecheck.go index c6be2bbd..18ed6b7a 100644 --- a/pkg/golinters/typecheck.go +++ b/pkg/golinters/typecheck.go @@ -61,7 +61,7 @@ func (lint TypeCheck) parseError(srcErr error) (*result.Issue, error) { Line: line, Column: column, }, - Text: message, + Text: markIdentifiers(message), FromLinter: lint.Name(), }, nil } diff --git a/pkg/golinters/unparam.go b/pkg/golinters/unparam.go index 9b4f9d89..472c3361 100644 --- a/pkg/golinters/unparam.go +++ b/pkg/golinters/unparam.go @@ -36,7 +36,7 @@ func (lint Unparam) Run(ctx context.Context, lintCtx *linter.Context) ([]result. for _, i := range unparamIssues { res = append(res, result.Issue{ Pos: lintCtx.Program.Fset.Position(i.Pos()), - Text: i.Message(), + Text: markIdentifiers(i.Message()), FromLinter: lint.Name(), }) } diff --git a/pkg/golinters/utils.go b/pkg/golinters/utils.go index f3bf3b57..be20adb0 100644 --- a/pkg/golinters/utils.go +++ b/pkg/golinters/utils.go @@ -4,7 +4,9 @@ import ( "fmt" "go/ast" "go/token" + "regexp" "strings" + "sync" "github.com/golangci/golangci-lint/pkg/lint/linter" "github.com/golangci/golangci-lint/pkg/packages" @@ -28,6 +30,65 @@ func formatCodeBlock(code string, _ *config.Config) string { return fmt.Sprintf("```\n%s\n```", code) } +type replacePattern struct { + re string + repl string +} + +type replaceRegexp struct { + re *regexp.Regexp + repl string +} + +var replaceRegexps []replaceRegexp +var replaceRegexpsOnce sync.Once + +var replacePatterns = []replacePattern{ + // unparam + {`^(\S+) - (\S+) is unused$`, "`${1}` - `${2}` is unused"}, + {`^(\S+) - (\S+) always receives (\S+) \((.*)\)$`, "`${1}` - `${2}` always receives `${3}` (`${4}`)"}, + {`^(\S+) - (\S+) always receives (.*)$`, "`${1}` - `${2}` always receives `${3}`"}, + + // interfacer + {`^(\S+) can be (\S+)$`, "`${1}` can be `${2}`"}, + + // govet + {`^(\S+) arg list ends with redundant newline$`, "`${1}` arg list ends with redundant newline"}, + {`^(\S+) composite literal uses unkeyed fields$`, "`${1}` composite literal uses unkeyed fields"}, + + // gas + {`^Blacklisted import (\S+): weak cryptographic primitive$`, + "Blacklisted import `${1}`: weak cryptographic primitive"}, + {`^TLS InsecureSkipVerify set true.$`, "TLS `InsecureSkipVerify` set true."}, + + // megacheck + {`^this value of (\S+) is never used$`, "this value of `${1}` is never used"}, + {`^should use time.Since instead of time.Now().Sub$`, + "should use `time.Since` instead of `time.Now().Sub`"}, + {`^(func|const|field|type) (\S+) is unused$`, "${1} `${2}` is unused"}, +} + +func markIdentifiers(s string) string { + replaceRegexpsOnce.Do(func() { + for _, p := range replacePatterns { + r := replaceRegexp{ + re: regexp.MustCompile(p.re), + repl: p.repl, + } + replaceRegexps = append(replaceRegexps, r) + } + }) + + for _, rr := range replaceRegexps { + rs := rr.re.ReplaceAllString(s, rr.repl) + if rs != s { + return rs + } + } + + return s +} + func getASTFilesForPkg(ctx *linter.Context, pkg *packages.Package) ([]*ast.File, *token.FileSet, error) { filenames := pkg.Files(ctx.Cfg.Run.AnalyzeTests) files := make([]*ast.File, 0, len(filenames)) diff --git a/pkg/printers/text.go b/pkg/printers/text.go index 08023382..30cd282c 100644 --- a/pkg/printers/text.go +++ b/pkg/printers/text.go @@ -69,6 +69,7 @@ func (p Text) printSourceCode(i *result.Issue) { } func (p Text) printUnderLinePointer(i *result.Issue) { + // if column == 0 it means column is unknown (e.g. for gas) if len(i.SourceLines) != 1 || i.Pos.Column == 0 { return } diff --git a/test/testdata/gas.go b/test/testdata/gas.go index f446fbd5..3f781120 100644 --- a/test/testdata/gas.go +++ b/test/testdata/gas.go @@ -2,7 +2,7 @@ package testdata import ( - "crypto/md5" // ERROR "G501: Blacklisted import crypto/md5: weak cryptographic primitive" + "crypto/md5" // ERROR "G501: Blacklisted import `crypto/md5`: weak cryptographic primitive" "log" ) diff --git a/test/testdata/gofmt.go b/test/testdata/gofmt.go index 382ccc36..c1ccd2fe 100644 --- a/test/testdata/gofmt.go +++ b/test/testdata/gofmt.go @@ -5,5 +5,5 @@ import "fmt" func GofmtNotSimplified() { var x []string - fmt.Print(x[1:len(x)]) // ERROR "File is not gofmt-ed with -s" + fmt.Print(x[1:len(x)]) // ERROR "File is not `gofmt`-ed with `-s`" } diff --git a/test/testdata/goimports.go b/test/testdata/goimports.go index e4c92eb3..0f7a15bb 100644 --- a/test/testdata/goimports.go +++ b/test/testdata/goimports.go @@ -2,7 +2,7 @@ package testdata import ( - "fmt" // ERROR "File is not goimports-ed" + "fmt" // ERROR "File is not `goimports`-ed" "github.com/golangci/golangci-lint/pkg/config" ) diff --git a/test/testdata/govet.go b/test/testdata/govet.go index e8c210e4..d0c1bb11 100644 --- a/test/testdata/govet.go +++ b/test/testdata/govet.go @@ -7,7 +7,7 @@ import ( ) func Govet() error { - return &os.PathError{"first", "path", os.ErrNotExist} // ERROR "os.PathError composite literal uses unkeyed fields" + return &os.PathError{"first", "path", os.ErrNotExist} // ERROR "`os.PathError` composite literal uses unkeyed fields" } func GovetShadow(f io.Reader, buf []byte) (err error) { diff --git a/test/testdata/interfacer.go b/test/testdata/interfacer.go index e1a13346..c55ca7d7 100644 --- a/test/testdata/interfacer.go +++ b/test/testdata/interfacer.go @@ -3,6 +3,6 @@ package testdata import "io" -func InterfacerCheck(f io.ReadCloser) { // ERROR "f can be io.Closer" +func InterfacerCheck(f io.ReadCloser) { // ERROR "`f` can be `io.Closer`" f.Close() } diff --git a/test/testdata/unparam.go b/test/testdata/unparam.go index f2a8795b..a59ab995 100644 --- a/test/testdata/unparam.go +++ b/test/testdata/unparam.go @@ -1,7 +1,7 @@ // args: -Eunparam package testdata -func unparamUnused(a, b uint) uint { // ERROR "unparamUnused - b is unused" +func unparamUnused(a, b uint) uint { // ERROR "`unparamUnused` - `b` is unused" a++ return a } diff --git a/vendor/github.com/golangci/govet/composite.go b/vendor/github.com/golangci/govet/composite.go index ed0ea643..feab049a 100644 --- a/vendor/github.com/golangci/govet/composite.go +++ b/vendor/github.com/golangci/govet/composite.go @@ -73,19 +73,13 @@ func checkUnkeyedLiteral(f *File, node ast.Node) { return } - f.Badf(cl.Pos(), "%s composite literal uses unkeyed fields", typeName) + f.Badf(cl.Pos(), "%s composite literal uses unkeyed fields", + types.TypeString(typ, func(pkg *types.Package) string { + return pkg.Name() + })) } func isLocalType(f *File, typ types.Type) bool { - structNameParts := strings.Split(typ.String(), ".") - if len(structNameParts) >= 2 { - structName := structNameParts[len(structNameParts)-1] - firstLetter := string(structName[0]) - if firstLetter == strings.ToLower(firstLetter) { - return true - } - } - switch x := typ.(type) { case *types.Struct: // struct literals are local types @@ -94,7 +88,7 @@ func isLocalType(f *File, typ types.Type) bool { return isLocalType(f, x.Elem()) case *types.Named: // names in package foo are local to foo_test too - return strings.TrimSuffix(x.Obj().Pkg().Path(), "_test") == strings.TrimSuffix(f.pkg.path, "_test") + return strings.TrimSuffix(x.Obj().Pkg().Path(), "_test") == strings.TrimSuffix(f.pkg.typesPkg.Path(), "_test") } return false }