Skip to content

Commit ddf271f

Browse files
committed
Refactor Analyzer, extend README, some linting issues
1 parent 8da7c48 commit ddf271f

File tree

5 files changed

+77
-21
lines changed

5 files changed

+77
-21
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ on:
99
jobs:
1010

1111
ci:
12-
name: Main Process
12+
name: ci
1313
runs-on: ubuntu-latest
1414
env:
1515
GO_VERSION: 1.16

README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,52 @@
44

55
Checks types passed to the json encoding functions. Reports unsupported types and reports occations, where the check for the returned error can be omited.
66

7+
Consider this [http.Handler](https://pkg.go.dev/net/http#Handler):
8+
9+
```Go
10+
func JSONHelloWorld(w http.ResponseWriter, r *http.Request) {
11+
response := struct {
12+
Message string
13+
Code int
14+
}{
15+
Message: "Hello World",
16+
Code: 200,
17+
}
18+
19+
body, err := json.Marshal(response)
20+
if err != nil {
21+
panic(err) // unreachable, because json encoding of a struct with just a string and an int will never return an error.
22+
}
23+
24+
w.Write(body)
25+
}
26+
```
27+
28+
Because the `panic` is not possible to happen, one might refactor the code like this:
29+
30+
```Go
31+
func JSONHelloWorld(w http.ResponseWriter, r *http.Request) {
32+
response := struct {
33+
Message string
34+
Code int
35+
}{
36+
Message: "Hello World",
37+
Code: 200,
38+
}
39+
40+
body, _ := json.Marshal(response)
41+
42+
w.Write(body)
43+
}
44+
```
45+
46+
This is ok, as long as the struct is not altered in such a way, that could potentially lead
47+
to `json.Marshal` returning an error.
48+
49+
`errchkjson` allows you to lint your code such that the above error returned from `json.Marshal`
50+
can be omitted while still staying save, because as soon as an unsafe type is added to the
51+
response type, the linter will warn you.
52+
753
## Installation
854

955
Download `errchkjson` from the [releases](https://github.com/breml/errchkjson/releases) or get the latest version from source with:

cmd/errchkjson/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ import (
77
)
88

99
func main() {
10-
singlechecker.Main(errchkjson.Analyzer)
10+
singlechecker.Main(errchkjson.NewAnalyzer())
1111
}

errchkjson.go

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package errchkjson
44

55
import (
6+
"flag"
67
"fmt"
78
"go/ast"
89
"go/token"
@@ -13,27 +14,35 @@ import (
1314
"golang.org/x/tools/go/types/typeutil"
1415
)
1516

16-
var Analyzer = &analysis.Analyzer{
17-
Name: "errchkjson",
18-
Doc: "Checks types passed to the json encoding functions. Reports unsupported types and reports occations, where the check for the returned error can be omited.",
19-
Run: run,
17+
type errchkjson struct {
18+
omitSafe bool // -omit-safe flag
2019
}
2120

22-
var omitSafe bool // -omit-safe flag
21+
// NewAnalyzer returns a new errchkjson analyzer.
22+
func NewAnalyzer() *analysis.Analyzer {
23+
errchkjson := &errchkjson{}
2324

24-
func init() {
25-
Analyzer.Flags.BoolVar(&omitSafe, "omit-safe", false, "if omit-safe is true, checking of safe returns is omitted")
25+
a := &analysis.Analyzer{
26+
Name: "errchkjson",
27+
Doc: "Checks types passed to the json encoding functions. Reports unsupported types and reports occations, where the check for the returned error can be omitted.",
28+
Run: errchkjson.run,
29+
}
30+
31+
a.Flags.Init("errchkjson", flag.ExitOnError)
32+
a.Flags.BoolVar(&errchkjson.omitSafe, "omit-safe", false, "if omit-safe is true, checking of safe returns is omitted")
33+
34+
return a
2635
}
2736

28-
func run(pass *analysis.Pass) (interface{}, error) {
37+
func (e *errchkjson) run(pass *analysis.Pass) (interface{}, error) {
2938
for _, file := range pass.Files {
3039
ast.Inspect(file, func(n ast.Node) bool {
3140
if n == nil {
3241
return true
3342
}
3443

3544
ce, ok := n.(*ast.CallExpr)
36-
if ok && omitSafe {
45+
if ok && e.omitSafe {
3746
fn, _ := typeutil.Callee(pass.TypesInfo, ce).(*types.Func)
3847
if fn == nil {
3948
return true
@@ -64,9 +73,9 @@ func run(pass *analysis.Pass) (interface{}, error) {
6473

6574
switch fn.FullName() {
6675
case "encoding/json.Marshal", "encoding/json.MarshalIndent":
67-
handleJSONMarshal(pass, n, fn.FullName(), 1)
76+
e.handleJSONMarshal(pass, n, fn.FullName(), 1)
6877
case "(*encoding/json.Encoder).Encode":
69-
handleJSONMarshal(pass, n, fn.FullName(), 0)
78+
e.handleJSONMarshal(pass, n, fn.FullName(), 0)
7079
default:
7180
return true
7281
}
@@ -77,7 +86,7 @@ func run(pass *analysis.Pass) (interface{}, error) {
7786
return nil, nil
7887
}
7988

80-
func handleJSONMarshal(pass *analysis.Pass, n ast.Node, fnName string, errPos int) {
89+
func (e *errchkjson) handleJSONMarshal(pass *analysis.Pass, n ast.Node, fnName string, errPos int) {
8190
as := n.(*ast.AssignStmt)
8291
ce := as.Rhs[0].(*ast.CallExpr)
8392

@@ -111,11 +120,11 @@ func handleJSONMarshal(pass *analysis.Pass, n ast.Node, fnName string, errPos in
111120
pass.Reportf(n.Pos(), "Error return value of `%s` is not checked: %v", fnName, err)
112121
}
113122
}
114-
if err == nil && !blankIdentifier && !omitSafe {
123+
if err == nil && !blankIdentifier && !e.omitSafe {
115124
pass.Reportf(n.Pos(), "Error return value of `%s` is checked but passed argument is safe", fnName)
116125
}
117126
// Report an error, if err for json.Marshal is not checked and save types are omitted
118-
if err == nil && blankIdentifier && omitSafe {
127+
if err == nil && blankIdentifier && e.omitSafe {
119128
pass.Reportf(n.Pos(), "Error return value of `%s` is not checked", fnName)
120129
}
121130
}

errchkjson_test.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@ import (
1313

1414
func TestDefault(t *testing.T) {
1515
testdata := analysistest.TestData()
16-
analysistest.Run(t, testdata, errchkjson.Analyzer, "standard")
16+
analysistest.Run(t, testdata, errchkjson.NewAnalyzer(), "standard")
1717
}
1818

19-
func TestNoSafeFlag(t *testing.T) {
20-
err := errchkjson.Analyzer.Flags.Set("omit-safe", "true")
19+
func TestOmitSafeFlag(t *testing.T) {
20+
errchkjson := errchkjson.NewAnalyzer()
21+
err := errchkjson.Flags.Set("omit-safe", "true")
2122
if err != nil {
22-
t.Fatalf("error setting 'no-safe' command line flag: %v", err)
23+
t.Fatalf("error setting 'omit-safe' command line flag: %v", err)
2324
}
2425
testdata := analysistest.TestData()
25-
analysistest.Run(t, testdata, errchkjson.Analyzer, "nosafe")
26+
analysistest.Run(t, testdata, errchkjson, "nosafe")
2627
}

0 commit comments

Comments
 (0)