Skip to content

Commit 2b35963

Browse files
authored
lint: Enable errcheck, fix failures (#1345)
This enables linting with errcheck on the repository. Exclusions were added for functions that are known to never fail, e.g. all Write methods on Zap's buffer.Buffer. In attempting to enable exclusions for these functions, I discovered and fixed a typo in the golangci.yml. <details> <summary>Complete list of issues fixed</summary> ``` encoder_test.go:44:18: Error return value is not checked (errcheck) encoder_test.go:55:18: Error return value is not checked (errcheck) error.go:64:19: Error return value of `arr.AppendObject` is not checked (errcheck) http_handler.go:83:13: Error return value of `enc.Encode` is not checked (errcheck) http_handler.go:88:14: Error return value of `enc.Encode` is not checked (errcheck) http_handler.go:92:13: Error return value of `enc.Encode` is not checked (errcheck) http_handler.go:95:13: Error return value of `enc.Encode` is not checked (errcheck) http_handler_test.go:170:24: Error return value of `res.Body.Close` is not checked (errcheck) logger.go:377:24: Error return value of `log.errorOutput.Sync` is not checked (errcheck) sink.go:69:17: Error return value of `sr.RegisterSink` is not checked (errcheck) stacktrace_ext_test.go:178:13: Error return value of `os.MkdirAll` is not checked (errcheck) writer.go:65:11: Error return value of `c.Close` is not checked (errcheck) writer_test.go:94:11: Error return value of `os.Remove` is not checked (errcheck) writer_test.go:258:9: Error return value of `w.Write` is not checked (errcheck) zapcore/buffered_write_syncer_bench_test.go:43:15: Error return value of `w.Stop` is not checked (errcheck) zapcore/buffered_write_syncer_bench_test.go:47:12: Error return value of `w.Write` is not checked (errcheck) zapcore/buffered_write_syncer_test.go:104:11: Error return value of `ws.Write` is not checked (errcheck) zapcore/core.go:107:9: Error return value of `c.Sync` is not checked (errcheck) zapcore/core_test.go:151:13: Error return value of `core.Write` is not checked (errcheck) zapcore/encoder_test.go:288:17: Error return value of `enc.AddArray` is not checked (errcheck) zapcore/encoder_test.go:316:17: Error return value of `enc.AddArray` is not checked (errcheck) zapcore/encoder_test.go:723:14: Error return value of `mem.AddArray` is not checked (errcheck) zapcore/entry.go:245:23: Error return value of `ce.ErrorOutput.Sync` is not checked (errcheck) zapcore/entry.go:257:22: Error return value of `ce.ErrorOutput.Sync` is not checked (errcheck) zapcore/error.go:101:19: Error return value of `arr.AppendObject` is not checked (errcheck) zapcore/json_encoder_bench_test.go:34:16: Error return value of `enc.AddObject` is not checked (errcheck) zapcore/json_encoder_bench_test.go:98:16: Error return value of `json.Marshal` is not checked (errcheck) zapcore/json_encoder_impl_test.go:252:16: Error return value of `e.AddObject` is not checked (errcheck) zapcore/json_encoder_impl_test.go:260:16: Error return value of `e.AddObject` is not checked (errcheck) zapcore/json_encoder_impl_test.go:268:16: Error return value of `e.AddObject` is not checked (errcheck) zapcore/json_encoder_impl_test.go:292:13: Error return value of `e.AddArray` is not checked (errcheck) zapcore/json_encoder_impl_test.go:423:21: Error return value of `arr.AppendObject` is not checked (errcheck) zapcore/json_encoder_impl_test.go:431:21: Error return value of `arr.AppendObject` is not checked (errcheck) zapcore/json_encoder_impl_test.go:533:20: Error return value of `arr.AppendObject` is not checked (errcheck) zapcore/level_test.go:162:16: Error return value of `l.MarshalText` is not checked (errcheck) zapcore/memory_encoder_test.go:221:16: Error return value of `e.AddObject` is not checked (errcheck) zapcore/memory_encoder_test.go:235:16: Error return value of `e.AddObject` is not checked (errcheck) zapcore/memory_encoder_test.go:288:54: Error return value of `e.AppendReflected` is not checked (errcheck) zapcore/memory_encoder_test.go:294:18: Error return value of `e.AppendArray` is not checked (errcheck) zapcore/memory_encoder_test.go:305:18: Error return value of `e.AppendArray` is not checked (errcheck) zapcore/memory_encoder_test.go:306:24: Error return value of `inner.AppendObject` is not checked (errcheck) zapcore/memory_encoder_test.go:321:18: Error return value of `e.AppendArray` is not checked (errcheck) zapcore/memory_encoder_test.go:322:24: Error return value of `inner.AppendObject` is not checked (errcheck) zapcore/tee_test.go:123:13: Error return value of `tee.Write` is not checked (errcheck) zapcore/write_syncer_bench_test.go:40:12: Error return value of `w.Write` is not checked (errcheck) zapcore/write_syncer_bench_test.go:54:12: Error return value of `w.Write` is not checked (errcheck) zapcore/write_syncer_bench_test.go:67:15: Error return value of `w.Stop` is not checked (errcheck) zapcore/write_syncer_bench_test.go:71:12: Error return value of `w.Write` is not checked (errcheck) zapcore/write_syncer_bench_test.go:86:12: Error return value of `w.Write` is not checked (errcheck) zapcore/write_syncer_test.go:73:9: Error return value of `ws.Sync` is not checked (errcheck) zaptest/observer/observer_test.go:176:15: Error return value of `logger.Write` is not checked (errcheck) ``` </details>
1 parent 9a36792 commit 2b35963

29 files changed

+309
-71
lines changed

.golangci.yml

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ linters:
99
disable-all: true
1010
enable:
1111
# golangci-lint defaults:
12-
# - errcheck TODO: enable errcheck
12+
- errcheck
1313
- gosimple
1414
- govet
1515
- ineffassign
@@ -21,7 +21,7 @@ linters:
2121
- nolintlint # lints nolint directives
2222
- revive
2323

24-
linter-settings:
24+
linters-settings:
2525
govet:
2626
# These govet checks are disabled by default, but they're useful.
2727
enable:
@@ -30,6 +30,24 @@ linter-settings:
3030
- sortslice
3131
- unusedwrite
3232

33+
errcheck:
34+
exclude-functions:
35+
# These methods can not fail.
36+
# They operate on an in-memory buffer.
37+
- (*go.uber.org/zap/buffer.Buffer).Write
38+
- (*go.uber.org/zap/buffer.Buffer).WriteByte
39+
- (*go.uber.org/zap/buffer.Buffer).WriteString
40+
41+
- (*go.uber.org/zap/zapio.Writer).Close
42+
- (*go.uber.org/zap/zapio.Writer).Sync
43+
- (*go.uber.org/zap/zapio.Writer).Write
44+
# Write to zapio.Writer cannot fail,
45+
# so io.WriteString on it cannot fail.
46+
- io.WriteString(*go.uber.org/zap/zapio.Writer)
47+
48+
# Writing a plain string to a fmt.State cannot fail.
49+
- io.WriteString(fmt.State)
50+
3351
issues:
3452
# Print all issues reported by all linters.
3553
max-issues-per-linter: 0
@@ -51,3 +69,9 @@ issues:
5169
# for foo() { }
5270
- linters: [revive]
5371
text: 'empty-block: this block is empty, you can remove it'
72+
73+
# Ignore logger.Sync() errcheck failures in example_test.go
74+
# since those are intended to be uncomplicated examples.
75+
- linters: [errcheck]
76+
path: example_test.go
77+
text: 'Error return value of `logger.Sync` is not checked'

benchmarks/scenario_bench_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@ func BenchmarkDisabledWithoutFields(b *testing.B) {
104104
}
105105
})
106106
})
107-
108107
}
109108

110109
func BenchmarkDisabledAccumulatedContext(b *testing.B) {
@@ -183,7 +182,6 @@ func BenchmarkDisabledAccumulatedContext(b *testing.B) {
183182
}
184183
})
185184
})
186-
187185
}
188186

189187
func BenchmarkDisabledAddingFields(b *testing.B) {
@@ -253,7 +251,6 @@ func BenchmarkDisabledAddingFields(b *testing.B) {
253251
}
254252
})
255253
})
256-
257254
}
258255

259256
func BenchmarkWithoutFields(b *testing.B) {
@@ -323,7 +320,9 @@ func BenchmarkWithoutFields(b *testing.B) {
323320
b.ResetTimer()
324321
b.RunParallel(func(pb *testing.PB) {
325322
for pb.Next() {
326-
logger.Log(getMessage(0), getMessage(1))
323+
if err := logger.Log(getMessage(0), getMessage(1)); err != nil {
324+
b.Fatal(err)
325+
}
327326
}
328327
})
329328
})
@@ -401,7 +400,6 @@ func BenchmarkWithoutFields(b *testing.B) {
401400
}
402401
})
403402
})
404-
405403
}
406404

407405
func BenchmarkAccumulatedContext(b *testing.B) {
@@ -471,7 +469,9 @@ func BenchmarkAccumulatedContext(b *testing.B) {
471469
b.ResetTimer()
472470
b.RunParallel(func(pb *testing.PB) {
473471
for pb.Next() {
474-
logger.Log(getMessage(0), getMessage(1))
472+
if err := logger.Log(getMessage(0), getMessage(1)); err != nil {
473+
b.Fatal(err)
474+
}
475475
}
476476
})
477477
})
@@ -531,7 +531,6 @@ func BenchmarkAccumulatedContext(b *testing.B) {
531531
}
532532
})
533533
})
534-
535534
}
536535

537536
func BenchmarkAddingFields(b *testing.B) {
@@ -592,7 +591,9 @@ func BenchmarkAddingFields(b *testing.B) {
592591
b.ResetTimer()
593592
b.RunParallel(func(pb *testing.PB) {
594593
for pb.Next() {
595-
logger.Log(fakeSugarFields()...)
594+
if err := logger.Log(fakeSugarFields()...); err != nil {
595+
b.Fatal(err)
596+
}
596597
}
597598
})
598599
})
@@ -643,5 +644,4 @@ func BenchmarkAddingFields(b *testing.B) {
643644
}
644645
})
645646
})
646-
647647
}

encoder_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func TestRegisterEncoder(t *testing.T) {
4141

4242
func TestDuplicateRegisterEncoder(t *testing.T) {
4343
testEncoders(func() {
44-
RegisterEncoder("foo", newNilEncoder)
44+
assert.NoError(t, RegisterEncoder("foo", newNilEncoder), "expected to be able to register the encoder foo")
4545
assert.Error(t, RegisterEncoder("foo", newNilEncoder), "expected an error when registering an encoder with the same name twice")
4646
})
4747
}
@@ -52,7 +52,7 @@ func TestRegisterEncoderNoName(t *testing.T) {
5252

5353
func TestNewEncoder(t *testing.T) {
5454
testEncoders(func() {
55-
RegisterEncoder("foo", newNilEncoder)
55+
assert.NoError(t, RegisterEncoder("foo", newNilEncoder), "expected to be able to register the encoder foo")
5656
encoder, err := newEncoder("foo", zapcore.EncoderConfig{})
5757
assert.NoError(t, err, "could not create an encoder for the registered name foo")
5858
assert.Nil(t, encoder, "the encoder from newNilEncoder is not nil")

error.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,12 @@ func (errs errArray) MarshalLogArray(arr zapcore.ArrayEncoder) error {
6161
// allocating, pool the wrapper type.
6262
elem := _errArrayElemPool.Get()
6363
elem.error = errs[i]
64-
arr.AppendObject(elem)
64+
err := arr.AppendObject(elem)
6565
elem.error = nil
6666
_errArrayElemPool.Put(elem)
67+
if err != nil {
68+
return err
69+
}
6770
}
6871
return nil
6972
}

error_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,39 @@ func TestErrorsArraysHandleRichErrors(t *testing.T) {
9595
require.True(t, ok, "Expected serialized error to be a map, got %T.", serialized)
9696
assert.Equal(t, "egad", errMap["error"], "Unexpected standard error string.")
9797
}
98+
99+
func TestErrArrayBrokenEncoder(t *testing.T) {
100+
t.Parallel()
101+
102+
failWith := errors.New("great sadness")
103+
err := (brokenArrayObjectEncoder{
104+
Err: failWith,
105+
ObjectEncoder: zapcore.NewMapObjectEncoder(),
106+
}).AddArray("errors", errArray{
107+
errors.New("foo"),
108+
errors.New("bar"),
109+
})
110+
require.Error(t, err, "Expected error from broken encoder.")
111+
assert.ErrorIs(t, err, failWith, "Unexpected error.")
112+
}
113+
114+
// brokenArrayObjectEncoder is an ObjectEncoder
115+
// that builds a broken ArrayEncoder.
116+
type brokenArrayObjectEncoder struct {
117+
zapcore.ObjectEncoder
118+
zapcore.ArrayEncoder
119+
120+
Err error // error to return
121+
}
122+
123+
func (enc brokenArrayObjectEncoder) AddArray(key string, marshaler zapcore.ArrayMarshaler) error {
124+
return enc.ObjectEncoder.AddArray(key,
125+
zapcore.ArrayMarshalerFunc(func(ae zapcore.ArrayEncoder) error {
126+
enc.ArrayEncoder = ae
127+
return marshaler.MarshalLogArray(enc)
128+
}))
129+
}
130+
131+
func (enc brokenArrayObjectEncoder) AppendObject(zapcore.ObjectMarshaler) error {
132+
return enc.Err
133+
}

http_handler.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,13 @@ import (
6969
//
7070
// curl -X PUT localhost:8080/log/level -H "Content-Type: application/json" -d '{"level":"debug"}'
7171
func (lvl AtomicLevel) ServeHTTP(w http.ResponseWriter, r *http.Request) {
72+
if err := lvl.serveHTTP(w, r); err != nil {
73+
w.WriteHeader(http.StatusInternalServerError)
74+
fmt.Fprintf(w, "internal error: %v", err)
75+
}
76+
}
77+
78+
func (lvl AtomicLevel) serveHTTP(w http.ResponseWriter, r *http.Request) error {
7279
type errorResponse struct {
7380
Error string `json:"error"`
7481
}
@@ -80,19 +87,20 @@ func (lvl AtomicLevel) ServeHTTP(w http.ResponseWriter, r *http.Request) {
8087

8188
switch r.Method {
8289
case http.MethodGet:
83-
enc.Encode(payload{Level: lvl.Level()})
90+
return enc.Encode(payload{Level: lvl.Level()})
91+
8492
case http.MethodPut:
8593
requestedLvl, err := decodePutRequest(r.Header.Get("Content-Type"), r)
8694
if err != nil {
8795
w.WriteHeader(http.StatusBadRequest)
88-
enc.Encode(errorResponse{Error: err.Error()})
89-
return
96+
return enc.Encode(errorResponse{Error: err.Error()})
9097
}
9198
lvl.SetLevel(requestedLvl)
92-
enc.Encode(payload{Level: lvl.Level()})
99+
return enc.Encode(payload{Level: lvl.Level()})
100+
93101
default:
94102
w.WriteHeader(http.StatusMethodNotAllowed)
95-
enc.Encode(errorResponse{
103+
return enc.Encode(errorResponse{
96104
Error: "Only GET and PUT are supported.",
97105
})
98106
}
@@ -129,5 +137,4 @@ func decodePutJSON(body io.Reader) (zapcore.Level, error) {
129137
return 0, errors.New("must specify logging level")
130138
}
131139
return *pld.Level, nil
132-
133140
}

http_handler_test.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ package zap_test
2222

2323
import (
2424
"encoding/json"
25+
"errors"
2526
"net/http"
2627
"net/http/httptest"
2728
"strings"
@@ -167,7 +168,9 @@ func TestAtomicLevelServeHTTP(t *testing.T) {
167168

168169
res, err := http.DefaultClient.Do(req)
169170
require.NoError(t, err, "Error making %s request.", req.Method)
170-
defer res.Body.Close()
171+
defer func() {
172+
assert.NoError(t, res.Body.Close(), "Error closing response body.")
173+
}()
171174

172175
require.Equal(t, tt.expectedCode, res.StatusCode, "Unexpected status code.")
173176
if tt.expectedCode != http.StatusOK {
@@ -188,3 +191,27 @@ func TestAtomicLevelServeHTTP(t *testing.T) {
188191
})
189192
}
190193
}
194+
195+
func TestAtomicLevelServeHTTPBrokenWriter(t *testing.T) {
196+
t.Parallel()
197+
198+
lvl := zap.NewAtomicLevel()
199+
200+
request, err := http.NewRequest(http.MethodGet, "http://localhost:1234/log/level", nil)
201+
require.NoError(t, err, "Error constructing request.")
202+
203+
recorder := httptest.NewRecorder()
204+
lvl.ServeHTTP(&brokenHTTPResponseWriter{
205+
ResponseWriter: recorder,
206+
}, request)
207+
208+
assert.Equal(t, http.StatusInternalServerError, recorder.Code, "Unexpected status code.")
209+
}
210+
211+
type brokenHTTPResponseWriter struct {
212+
http.ResponseWriter
213+
}
214+
215+
func (w *brokenHTTPResponseWriter) Write([]byte) (int, error) {
216+
return 0, errors.New("great sadness")
217+
}

logger.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ func (log *Logger) check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry {
374374
if stack.Count() == 0 {
375375
if log.addCaller {
376376
fmt.Fprintf(log.errorOutput, "%v Logger.check error: failed to get caller\n", ent.Time.UTC())
377-
log.errorOutput.Sync()
377+
_ = log.errorOutput.Sync()
378378
}
379379
return ce
380380
}

sink.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ func newSinkRegistry() *sinkRegistry {
6666
factories: make(map[string]func(*url.URL) (Sink, error)),
6767
openFile: os.OpenFile,
6868
}
69-
sr.RegisterSink(schemeFile, sr.newFileSinkFromURL)
69+
// Infallible operation: the registry is empty, so we can't have a conflict.
70+
_ = sr.RegisterSink(schemeFile, sr.newFileSinkFromURL)
7071
return sr
7172
}
7273

@@ -154,7 +155,7 @@ func (sr *sinkRegistry) newFileSinkFromPath(path string) (Sink, error) {
154155
case "stderr":
155156
return nopCloserSink{os.Stderr}, nil
156157
}
157-
return sr.openFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
158+
return sr.openFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0o666)
158159
}
159160

160161
func normalizeScheme(s string) (string, error) {

stacktrace_ext_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ func TestStacktraceFiltersVendorZap(t *testing.T) {
9797

9898
testDir := filepath.Join(goPath, "src/go.uber.org/zap_test/")
9999
vendorDir := filepath.Join(testDir, "vendor")
100-
require.NoError(t, os.MkdirAll(testDir, 0777), "Failed to create source director")
100+
require.NoError(t, os.MkdirAll(testDir, 0o777), "Failed to create source director")
101101

102102
curFile := getSelfFilename(t)
103103
setupSymlink(t, curFile, filepath.Join(testDir, curFile))
@@ -175,7 +175,7 @@ func getSelfFilename(t *testing.T) string {
175175

176176
func setupSymlink(t *testing.T, src, dst string) {
177177
// Make sure the destination directory exists.
178-
os.MkdirAll(filepath.Dir(dst), 0777)
178+
require.NoError(t, os.MkdirAll(filepath.Dir(dst), 0o777))
179179

180180
// Get absolute path of the source for the symlink, otherwise we can create a symlink
181181
// that uses relative paths.

0 commit comments

Comments
 (0)