From 5f1096cf808dba485b9186ed2640946008cd6dff Mon Sep 17 00:00:00 2001 From: Isaev Denis Date: Sun, 19 Jan 2020 16:46:51 +0300 Subject: [PATCH] add goprintffuncname linter (#850) --- README.md | 3 + go.mod | 1 + go.sum | 2 + pkg/golinters/goprintffuncname.go | 17 +++++ pkg/lint/lintersdb/manager.go | 3 + test/testdata/goprintffuncname.go | 5 ++ .../pkg/analyzer/analyzer.go | 74 +++++++++++++++++++ vendor/modules.txt | 3 + 8 files changed, 108 insertions(+) create mode 100644 pkg/golinters/goprintffuncname.go create mode 100644 test/testdata/goprintffuncname.go create mode 100644 vendor/github.com/jirfag/go-printf-func-name/pkg/analyzer/analyzer.go diff --git a/README.md b/README.md index cfaff146..b4ebeb54 100644 --- a/README.md +++ b/README.md @@ -221,6 +221,7 @@ gofmt: Gofmt checks whether code was gofmt-ed. By default this tool runs with -s goimports: Goimports does everything that gofmt does. Additionally it checks unused imports [fast: true, auto-fix: true] golint: Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes [fast: true, auto-fix: false] gomnd: An analyzer to detect magic numbers. [fast: true, auto-fix: false] +goprintffuncname: Checks that printf-like functions are named with `f` at the end [fast: true, auto-fix: false] gosec (gas): Inspects source code for security problems [fast: true, auto-fix: false] interfacer: Linter that suggests narrower interface types [fast: true, auto-fix: false] lll: Reports long lines [fast: true, auto-fix: false] @@ -485,6 +486,7 @@ golangci-lint help linters - [whitespace](https://github.com/ultraware/whitespace) - Tool for detection of leading and trailing whitespace - [wsl](https://github.com/bombsimon/wsl) - Whitespace Linter - Forces you to use empty lines! - [gomnd](https://github.com/tommy-muehle/go-mnd) - An analyzer to detect magic numbers. +- [goprintffuncname](https://github.com/jirfag/go-printf-func-name) - Checks that printf-like functions are named with `f` at the end ## Configuration @@ -1232,6 +1234,7 @@ Thanks to developers and authors of used linters: - [matoous](https://github.com/matoous) - [ultraware](https://github.com/ultraware) - [bombsimon](https://github.com/bombsimon) +- [jirfag](https://github.com/jirfag) - [tommy-muehle](https://github.com/tommy-muehle) ## Changelog diff --git a/go.mod b/go.mod index 5a71ed9d..47061e16 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21 github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0 github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 + github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3 github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a github.com/matoous/godox v0.0.0-20190911065817-5d6d842e92eb // v1.0 github.com/mattn/go-colorable v0.1.4 diff --git a/go.sum b/go.sum index 80d7e17c..8ea1d70a 100644 --- a/go.sum +++ b/go.sum @@ -130,6 +130,8 @@ github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3 h1:jNYPNLe3d8smommaoQlK7LOA5ESyUJJ+Wf79ZtA7Vp4= +github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0= github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a h1:GmsqmapfzSJkm28dhRoHz2tLRbJmqhU86IPgBtN3mmk= github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a/go.mod h1:xRskid8CManxVta/ALEhJha/pweKBaVG6fWgc0yH25s= github.com/jmoiron/sqlx v1.2.1-0.20190826204134-d7d95172beb5 h1:lrdPtrORjGv1HbbEvKWDUAy97mPpFm4B8hp77tcCUJY= diff --git a/pkg/golinters/goprintffuncname.go b/pkg/golinters/goprintffuncname.go new file mode 100644 index 00000000..c5516dc7 --- /dev/null +++ b/pkg/golinters/goprintffuncname.go @@ -0,0 +1,17 @@ +package golinters + +import ( + "github.com/jirfag/go-printf-func-name/pkg/analyzer" + "golang.org/x/tools/go/analysis" + + "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" +) + +func NewGoPrintfFuncName() *goanalysis.Linter { + return goanalysis.NewLinter( + "goprintffuncname", + "Checks that printf-like functions are named with `f` at the end", + []*analysis.Analyzer{analyzer.Analyzer}, + nil, + ).WithLoadMode(goanalysis.LoadModeSyntax) +} diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index 38c4cbab..f35265f5 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -241,6 +241,9 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { linter.NewConfig(golinters.NewWSL()). WithPresets(linter.PresetStyle). WithURL("https://github.com/bombsimon/wsl"), + linter.NewConfig(golinters.NewGoPrintfFuncName()). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/jirfag/go-printf-func-name"), linter.NewConfig(golinters.NewGoMND()). WithPresets(linter.PresetStyle). WithURL("https://github.com/tommy-muehle/go-mnd"), diff --git a/test/testdata/goprintffuncname.go b/test/testdata/goprintffuncname.go new file mode 100644 index 00000000..100977c6 --- /dev/null +++ b/test/testdata/goprintffuncname.go @@ -0,0 +1,5 @@ +//args: -Egoprintffuncname +package testdata + +func PrintfLikeFuncWithBadName(format string, args ...interface{}) { // ERROR "printf-like formatting function 'PrintfLikeFuncWithBadName' should be named 'PrintfLikeFuncWithBadNamef'" +} diff --git a/vendor/github.com/jirfag/go-printf-func-name/pkg/analyzer/analyzer.go b/vendor/github.com/jirfag/go-printf-func-name/pkg/analyzer/analyzer.go new file mode 100644 index 00000000..7937dd43 --- /dev/null +++ b/vendor/github.com/jirfag/go-printf-func-name/pkg/analyzer/analyzer.go @@ -0,0 +1,74 @@ +package analyzer + +import ( + "go/ast" + "strings" + + "golang.org/x/tools/go/analysis/passes/inspect" + "golang.org/x/tools/go/ast/inspector" + + "golang.org/x/tools/go/analysis" +) + +var Analyzer = &analysis.Analyzer{ + Name: "goprintffuncname", + Doc: "Checks that printf-like functions are named with `f` at the end.", + Run: run, + Requires: []*analysis.Analyzer{inspect.Analyzer}, +} + +func run(pass *analysis.Pass) (interface{}, error) { + inspector := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + nodeFilter := []ast.Node{ + (*ast.FuncDecl)(nil), + } + + inspector.Preorder(nodeFilter, func(node ast.Node) { + funcDecl := node.(*ast.FuncDecl) + + if res := funcDecl.Type.Results; res != nil && len(res.List) != 0 { + return + } + + params := funcDecl.Type.Params.List + if len(params) < 2 { // [0] must be format (string), [1] must be args (...interface{}) + return + } + + formatParamType, ok := params[len(params)-2].Type.(*ast.Ident) + if !ok { // first param type isn't identificator so it can't be of type "string" + return + } + + if formatParamType.Name != "string" { // first param (format) type is not string + return + } + + if formatParamNames := params[len(params)-2].Names; len(formatParamNames) == 0 || formatParamNames[len(formatParamNames)-1].Name != "format" { + return + } + + argsParamType, ok := params[len(params)-1].Type.(*ast.Ellipsis) + if !ok { // args are not ellipsis (...args) + return + } + + elementType, ok := argsParamType.Elt.(*ast.InterfaceType) + if !ok { // args are not of interface type, but we need interface{} + return + } + + if elementType.Methods != nil && len(elementType.Methods.List) != 0 { + return // has >= 1 method in interface, but we need an empty interface "interface{}" + } + + if strings.HasSuffix(funcDecl.Name.Name, "f") { + return + } + + pass.Reportf(node.Pos(), "printf-like formatting function '%s' should be named '%sf'", + funcDecl.Name.Name, funcDecl.Name.Name) + }) + + return nil, nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt index a97d0de5..68b75f1e 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -99,6 +99,8 @@ github.com/hashicorp/hcl/json/scanner github.com/hashicorp/hcl/json/token # github.com/inconshreveable/mousetrap v1.0.0 github.com/inconshreveable/mousetrap +# github.com/jirfag/go-printf-func-name v0.0.0-20191110105641-45db9963cdd3 +github.com/jirfag/go-printf-func-name/pkg/analyzer # github.com/jingyugao/rowserrcheck v0.0.0-20191204022205-72ab7603b68a github.com/jingyugao/rowserrcheck/passes/rowserr # github.com/kisielk/gotool v1.0.0 @@ -194,6 +196,7 @@ golang.org/x/sys/windows golang.org/x/text/transform golang.org/x/text/unicode/norm golang.org/x/text/width +# golang.org/x/tools v0.0.0-20191108193012-7d206e10da11 => github.com/golangci/tools v0.0.0-20190915081525-6aa350649b1c # golang.org/x/tools v0.0.0-20200102140908-9497f49d5709 => github.com/golangci/tools v0.0.0-20190915081525-6aa350649b1c golang.org/x/tools/go/analysis golang.org/x/tools/go/analysis/passes/asmdecl