Skip to content

Commit 5d407d7

Browse files
committed
implement linter
1 parent c54986f commit 5d407d7

File tree

7 files changed

+153
-0
lines changed

7 files changed

+153
-0
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,16 @@
11
# go-printf-func-name
2+
3+
The Go linter `go-printf-func-name` checks that printf-like functions are named with `f` at the end.
4+
5+
For example, `myLog` should be named `myLogf` by Go convention:
6+
7+
```go
8+
package main
9+
10+
import "log"
11+
12+
func myLog(format string, args ...interface{}) {
13+
const prefix = "[my] "
14+
log.Printf(prefix + format, args...)
15+
}
16+
```

cmd/go-printf-func-name/main.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package main
2+
3+
import (
4+
"github.com/jirfag/go-printf-func-name/pkg/analyzer"
5+
"golang.org/x/tools/go/analysis/singlechecker"
6+
)
7+
8+
func main() {
9+
singlechecker.Main(analyzer.Analyzer)
10+
}

go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module github.com/jirfag/go-printf-func-name
2+
3+
go 1.13
4+
5+
require golang.org/x/tools v0.0.0-20191108193012-7d206e10da11

go.sum

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
2+
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
3+
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
4+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
5+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
6+
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11 h1:Yq9t9jnGoR+dBuitxdo9l6Q7xh/zOyNnYUtDKaQ3x0E=
7+
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
8+
golang.org/x/tools v0.0.0-20191109212701-97ad0ed33101 h1:LCmXVkvpQCDj724eX6irUTPCJP5GelFHxqGSWL2D1R0=
9+
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

pkg/analyzer/analyzer.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package analyzer
2+
3+
import (
4+
"go/ast"
5+
"strings"
6+
7+
"golang.org/x/tools/go/analysis/passes/inspect"
8+
"golang.org/x/tools/go/ast/inspector"
9+
10+
"golang.org/x/tools/go/analysis"
11+
)
12+
13+
var Analyzer = &analysis.Analyzer{
14+
Name: "go_printf_func_name",
15+
Doc: "Checks that printf-like functions are named with `f` at the end.",
16+
Run: run,
17+
Requires: []*analysis.Analyzer{inspect.Analyzer},
18+
}
19+
20+
func run(pass *analysis.Pass) (interface{}, error) {
21+
inspector := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
22+
nodeFilter := []ast.Node{
23+
(*ast.FuncDecl)(nil),
24+
}
25+
26+
inspector.Preorder(nodeFilter, func(node ast.Node) {
27+
funcDecl := node.(*ast.FuncDecl)
28+
29+
params := funcDecl.Type.Params.List
30+
if len(params) < 2 { // [0] must be format (string), [1] must be args (...interface{})
31+
return
32+
}
33+
34+
formatParamType, ok := params[len(params)-2].Type.(*ast.Ident)
35+
if !ok { // first param type isn't identificator so it can't be of type "string"
36+
return
37+
}
38+
39+
if formatParamType.Name != "string" { // first param (format) type is not string
40+
return
41+
}
42+
43+
argsParamType, ok := params[len(params)-1].Type.(*ast.Ellipsis)
44+
if !ok { // args are not ellipsis (...args)
45+
return
46+
}
47+
48+
elementType, ok := argsParamType.Elt.(*ast.InterfaceType)
49+
if !ok { // args are not of interface type, but we need interface{}
50+
return
51+
}
52+
53+
if elementType.Methods != nil && len(elementType.Methods.List) != 0 {
54+
return // has >= 1 method in interface, but we need an empty interface "interface{}"
55+
}
56+
57+
if strings.HasSuffix(funcDecl.Name.Name, "f") {
58+
return
59+
}
60+
61+
pass.Reportf(node.Pos(), "printf-like formatting function '%s' should be named '%sf'\n",
62+
funcDecl.Name.Name, funcDecl.Name.Name)
63+
})
64+
65+
return nil, nil
66+
}

pkg/analyzer/analyzer_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package analyzer_test
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
8+
"github.com/jirfag/go-printf-func-name/pkg/analyzer"
9+
"golang.org/x/tools/go/analysis/analysistest"
10+
)
11+
12+
func TestAll(t *testing.T) {
13+
wd, err := os.Getwd()
14+
if err != nil {
15+
t.Fatalf("Failed to get wd: %s", err)
16+
}
17+
18+
testdata := filepath.Join(filepath.Dir(filepath.Dir(wd)), "testdata")
19+
analysistest.Run(t, testdata, analyzer.Analyzer, "p")
20+
}

testdata/src/p/p.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package p
2+
3+
func notPrintfFuncAtAll() {}
4+
5+
func funcWithEllipsis(args ...interface{}) {}
6+
7+
func printfLikeButWithStrings(format string, args ...string) {}
8+
9+
func printfLikeButWithBadFormat(format int, args ...interface{}) {}
10+
11+
func secondArgIsNotEllipsis(arg1 string, arg2 int) {}
12+
13+
func printfLikeButWithExtraInterfaceMethods(format string, args ...interface {
14+
String() string
15+
}) {
16+
}
17+
18+
func prinfLikeFuncf(format string, args ...interface{}) {}
19+
20+
func prinfLikeFunc(format string, args ...interface{}) {} // want "printf-like formatting function"
21+
22+
func prinfLikeFuncWithExtraArgs1(extraArg, format string, args ...interface{}) {} // want "printf-like formatting function"
23+
24+
func prinfLikeFuncWithExtraArgs2(extraArg int, format string, args ...interface{}) {} // want "printf-like formatting function"
25+
26+
func prinfLikeFuncWithReturnValue(format string, args ...interface{}) string { // want "printf-like formatting function"
27+
return ""
28+
}

0 commit comments

Comments
 (0)