From 7b5689f42873d3a13921b2c008359c599451ceac Mon Sep 17 00:00:00 2001 From: Quang-Minh Nguyen Date: Wed, 21 Jun 2023 15:30:21 +0700 Subject: Implement a linter to discourage future usage of Unavailable code The prior commit fixes inelligible usages of Unavailable status codes. To prevent this situation from happening in the future, this commit implements a linter that it warns occurrences where Unavailable code is used. Engineers can ignore it by adding a comment, but at least this practice catches their eyes. --- tools/golangci-lint/gitaly/lint.go | 4 ++ .../src/unavailable_code/unavailable_code_test.go | 21 ++++++++++ tools/golangci-lint/gitaly/unavailable_code.go | 48 ++++++++++++++++++++++ .../golangci-lint/gitaly/unavailable_code_test.go | 22 ++++++++++ 4 files changed, 95 insertions(+) create mode 100644 tools/golangci-lint/gitaly/testdata/src/unavailable_code/unavailable_code_test.go create mode 100644 tools/golangci-lint/gitaly/unavailable_code.go create mode 100644 tools/golangci-lint/gitaly/unavailable_code_test.go (limited to 'tools') diff --git a/tools/golangci-lint/gitaly/lint.go b/tools/golangci-lint/gitaly/lint.go index 1d76a9400..6840b15ac 100644 --- a/tools/golangci-lint/gitaly/lint.go +++ b/tools/golangci-lint/gitaly/lint.go @@ -23,6 +23,10 @@ func (p *analyzerPlugin) GetAnalyzers() []*analysis.Analyzer { "included-functions", ), }), + newUnavailableCodeAnalyzer(&unavailableCodeAnalyzerSettings{IncludedFunctions: p.configStringSlicesAt( + unavailableCodeAnalyzerName, + "included-functions", + )}), } } diff --git a/tools/golangci-lint/gitaly/testdata/src/unavailable_code/unavailable_code_test.go b/tools/golangci-lint/gitaly/testdata/src/unavailable_code/unavailable_code_test.go new file mode 100644 index 000000000..5493e3f7a --- /dev/null +++ b/tools/golangci-lint/gitaly/testdata/src/unavailable_code/unavailable_code_test.go @@ -0,0 +1,21 @@ +package unavailable_code + +import ( + "fmt" +) + +func NewUnavailable(msg string) error { + return fmt.Errorf("unavailable: %s", msg) +} + +func NewAborted(msg string) error { + return fmt.Errorf("aborted: %s", msg) +} + +func errorWrapOkay() { + _ = NewAborted("hello world") +} + +func errorWrapNotOkay() { + _ = NewUnavailable("hello world") // please avoid using the Unavailable status code: https://gitlab.com/gitlab-org/gitaly/-/blob/master/STYLE.md?plain=0#unavailable-code +} diff --git a/tools/golangci-lint/gitaly/unavailable_code.go b/tools/golangci-lint/gitaly/unavailable_code.go new file mode 100644 index 000000000..9546b1871 --- /dev/null +++ b/tools/golangci-lint/gitaly/unavailable_code.go @@ -0,0 +1,48 @@ +package main + +import ( + "go/ast" + + "golang.org/x/tools/go/analysis" +) + +const unavailableCodeAnalyzerName = "unavailable_code" + +type unavailableCodeAnalyzerSettings struct { + IncludedFunctions []string `mapstructure:"included-functions"` +} + +// newErrorWrapAnalyzer warns if Unavailable status code is used. Unavailable status code is reserved to signal server's +// unavailability. It should be used by some specific components. gRPC handlers should typically avoid this type of +// error. +// For more information: +// https://gitlab.com/gitlab-org/gitaly/-/blob/master/STYLE.md?plain=0#unavailable-code +func newUnavailableCodeAnalyzer(settings *unavailableCodeAnalyzerSettings) *analysis.Analyzer { + return &analysis.Analyzer{ + Name: unavailableCodeAnalyzerName, + Doc: `discourage the usage of Unavailable status code`, + Run: runUnavailableCodeAnalyzer(settings.IncludedFunctions), + } +} + +func runUnavailableCodeAnalyzer(rules []string) func(*analysis.Pass) (interface{}, error) { + return func(pass *analysis.Pass) (interface{}, error) { + matcher := NewMatcher(pass) + for _, file := range pass.Files { + ast.Inspect(file, func(n ast.Node) bool { + if call, ok := n.(*ast.CallExpr); ok { + if matcher.MatchFunction(call, rules) { + pass.Report(analysis.Diagnostic{ + Pos: call.Pos(), + End: call.End(), + Message: "please avoid using the Unavailable status code: https://gitlab.com/gitlab-org/gitaly/-/blob/master/STYLE.md?plain=0#unavailable-code", + SuggestedFixes: nil, + }) + } + } + return true + }) + } + return nil, nil + } +} diff --git a/tools/golangci-lint/gitaly/unavailable_code_test.go b/tools/golangci-lint/gitaly/unavailable_code_test.go new file mode 100644 index 000000000..39d97e788 --- /dev/null +++ b/tools/golangci-lint/gitaly/unavailable_code_test.go @@ -0,0 +1,22 @@ +package main + +import ( + "os" + "path/filepath" + "testing" + + "golang.org/x/tools/go/analysis/analysistest" +) + +func TestUnavailableCode(t *testing.T) { + wd, err := os.Getwd() + if err != nil { + t.Fatalf("Failed to get wd: %s", err) + } + + testdata := filepath.Join(wd, "testdata") + analyzer := newUnavailableCodeAnalyzer(&unavailableCodeAnalyzerSettings{IncludedFunctions: []string{ + "unavailable_code.NewUnavailable", + }}) + analysistest.Run(t, testdata, analyzer, "unavailable_code") +} -- cgit v1.2.3