diff --git a/go/analysis/passes/defers/doc.go b/go/analysis/passes/defers/doc.go index ec9f7664062..60ad3c2cac9 100644 --- a/go/analysis/passes/defers/doc.go +++ b/go/analysis/passes/defers/doc.go @@ -21,5 +21,5 @@ // // The correct code is: // -// defer func() { recordLatency(time.Since(start)) }()` +// defer func() { recordLatency(time.Since(start)) }() package defers diff --git a/go/analysis/unitchecker/vet_std_test.go b/go/analysis/unitchecker/vet_std_test.go index feea1a21e25..e0fb41c77ed 100644 --- a/go/analysis/unitchecker/vet_std_test.go +++ b/go/analysis/unitchecker/vet_std_test.go @@ -19,6 +19,7 @@ import ( "golang.org/x/tools/go/analysis/passes/cgocall" "golang.org/x/tools/go/analysis/passes/composite" "golang.org/x/tools/go/analysis/passes/copylock" + "golang.org/x/tools/go/analysis/passes/defers" "golang.org/x/tools/go/analysis/passes/directive" "golang.org/x/tools/go/analysis/passes/errorsas" "golang.org/x/tools/go/analysis/passes/framepointer" @@ -54,6 +55,7 @@ func vet() { cgocall.Analyzer, composite.Analyzer, copylock.Analyzer, + defers.Analyzer, directive.Analyzer, errorsas.Analyzer, framepointer.Analyzer, @@ -68,8 +70,8 @@ func vet() { stdmethods.Analyzer, stringintconv.Analyzer, structtag.Analyzer, - tests.Analyzer, testinggoroutine.Analyzer, + tests.Analyzer, timeformat.Analyzer, unmarshal.Analyzer, unreachable.Analyzer, diff --git a/gopls/doc/analyzers.md b/gopls/doc/analyzers.md index 8f470e25d25..48c98e0cb39 100644 --- a/gopls/doc/analyzers.md +++ b/gopls/doc/analyzers.md @@ -108,6 +108,26 @@ errors is discouraged. **Enabled by default.** +## **defer** + +report common mistakes in defer statements + +The defer analyzer reports a diagnostic when a defer statement would +result in a non-deferred call to time.Since, as experience has shown +that this is nearly always a mistake. + +For example: + + start := time.Now() + ... + defer recordLatency(time.Since(start)) // error: call to time.Since is not deferred + +The correct code is: + + defer func() { recordLatency(time.Since(start)) }() + +**Enabled by default.** + ## **deprecated** check for use of deprecated identifiers diff --git a/gopls/doc/generate_test.go b/gopls/doc/generate_test.go index 99f366c19a2..44e6041721d 100644 --- a/gopls/doc/generate_test.go +++ b/gopls/doc/generate_test.go @@ -23,6 +23,6 @@ func TestGenerated(t *testing.T) { t.Fatal(err) } if !ok { - t.Error("documentation needs updating. run: `go run doc/generate.go` from the gopls module.") + t.Error("documentation needs updating. Run: cd gopls && go generate ./doc") } } diff --git a/gopls/internal/lsp/source/api_json.go b/gopls/internal/lsp/source/api_json.go index 153cb7d6e9a..97f6384ab82 100644 --- a/gopls/internal/lsp/source/api_json.go +++ b/gopls/internal/lsp/source/api_json.go @@ -268,6 +268,11 @@ var GeneratedAPIJSON = &APIJSON{ Doc: "check for calls of reflect.DeepEqual on error values\n\nThe deepequalerrors checker looks for calls of the form:\n\n reflect.DeepEqual(err1, err2)\n\nwhere err1 and err2 are errors. Using reflect.DeepEqual to compare\nerrors is discouraged.", Default: "true", }, + { + Name: "\"defer\"", + Doc: "report common mistakes in defer statements\n\nThe defer analyzer reports a diagnostic when a defer statement would\nresult in a non-deferred call to time.Since, as experience has shown\nthat this is nearly always a mistake.\n\nFor example:\n\n\tstart := time.Now()\n\t...\n\tdefer recordLatency(time.Since(start)) // error: call to time.Since is not deferred\n\nThe correct code is:\n\n\tdefer func() { recordLatency(time.Since(start)) }()", + Default: "true", + }, { Name: "\"deprecated\"", Doc: "check for use of deprecated identifiers\n\nThe deprecated analyzer looks for deprecated symbols and package imports.\n\nSee https://go.dev/wiki/Deprecated to learn about Go's convention\nfor documenting and signaling deprecated identifiers.", @@ -966,6 +971,11 @@ var GeneratedAPIJSON = &APIJSON{ URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/deepequalerrors", Default: true, }, + { + Name: "defer", + Doc: "report common mistakes in defer statements\n\nThe defer analyzer reports a diagnostic when a defer statement would\nresult in a non-deferred call to time.Since, as experience has shown\nthat this is nearly always a mistake.\n\nFor example:\n\n\tstart := time.Now()\n\t...\n\tdefer recordLatency(time.Since(start)) // error: call to time.Since is not deferred\n\nThe correct code is:\n\n\tdefer func() { recordLatency(time.Since(start)) }()", + Default: true, + }, { Name: "deprecated", Doc: "check for use of deprecated identifiers\n\nThe deprecated analyzer looks for deprecated symbols and package imports.\n\nSee https://go.dev/wiki/Deprecated to learn about Go's convention\nfor documenting and signaling deprecated identifiers.", diff --git a/gopls/internal/lsp/source/options.go b/gopls/internal/lsp/source/options.go index 4c3301457a7..c2e3223e6c1 100644 --- a/gopls/internal/lsp/source/options.go +++ b/gopls/internal/lsp/source/options.go @@ -26,6 +26,7 @@ import ( "golang.org/x/tools/go/analysis/passes/composite" "golang.org/x/tools/go/analysis/passes/copylock" "golang.org/x/tools/go/analysis/passes/deepequalerrors" + "golang.org/x/tools/go/analysis/passes/defers" "golang.org/x/tools/go/analysis/passes/directive" "golang.org/x/tools/go/analysis/passes/errorsas" "golang.org/x/tools/go/analysis/passes/fieldalignment" @@ -1544,6 +1545,7 @@ func defaultAnalyzers() map[string]*Analyzer { cgocall.Analyzer.Name: {Analyzer: cgocall.Analyzer, Enabled: true}, composite.Analyzer.Name: {Analyzer: composite.Analyzer, Enabled: true}, copylock.Analyzer.Name: {Analyzer: copylock.Analyzer, Enabled: true}, + defers.Analyzer.Name: {Analyzer: defers.Analyzer, Enabled: true}, deprecated.Analyzer.Name: {Analyzer: deprecated.Analyzer, Enabled: true, Severity: protocol.SeverityHint, Tag: []protocol.DiagnosticTag{protocol.Deprecated}}, directive.Analyzer.Name: {Analyzer: directive.Analyzer, Enabled: true}, errorsas.Analyzer.Name: {Analyzer: errorsas.Analyzer, Enabled: true},