diff options
author | Patrick Steinhardt <psteinhardt@gitlab.com> | 2023-03-02 10:53:13 +0300 |
---|---|---|
committer | Patrick Steinhardt <psteinhardt@gitlab.com> | 2023-03-02 11:41:21 +0300 |
commit | d7a5f22faf841819669d29ccf4e431fc3b2d9b76 (patch) | |
tree | a1002bae46450a9974609ee9ec3995f5c3d8d384 /tools/golangci-lint/gitaly/matcher.go | |
parent | c7695bd902060be80b3499ffd2bd9e86d89c4f4f (diff) |
tools: Fix incompatible dependencies for new Gitaly linter
In d1c74493a (Add golangci-lint custom linter infrastructure,
2023-02-15), we have added a Gitaly-specific custom linter that is
plugged into golangci-lint. For the time being, we agreed to just make
it use the same `go.mod` as the main module, mostly because we wanted
`make test` to also execute its tests.
This is causing problems already, though. The Renovate bot has decided
to immediately try and upgrade the new dependency on golang.org/x/tools
to the most recent version. But this causes CI failures now because
golangci-lint requires all plugins to always use the same version of
that package as it uses itself.
Fix this by moving the Gitaly-specific linting infrastructure into a
subpackage of `tools/golangci-lint` and make it reuse the `go.mod` file.
This ensures that they always use compatible versions and that the main
production-level `go.mod` file is not tied to a specific dependency
version.
Diffstat (limited to 'tools/golangci-lint/gitaly/matcher.go')
-rw-r--r-- | tools/golangci-lint/gitaly/matcher.go | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/tools/golangci-lint/gitaly/matcher.go b/tools/golangci-lint/gitaly/matcher.go new file mode 100644 index 000000000..9b180a726 --- /dev/null +++ b/tools/golangci-lint/gitaly/matcher.go @@ -0,0 +1,88 @@ +package main + +import ( + "go/ast" + "go/types" + "regexp" + + "golang.org/x/tools/go/analysis" +) + +// Matcher implements some helper methods to filter relevant AST nodes for linter checks. It depends +// on the TypeInfo of analysis.Pass object passed in the analyzer. +type Matcher struct { + typesInfo *types.Info +} + +// NewMatcher creates a new Matcher object from the input analysis pass. +func NewMatcher(pass *analysis.Pass) *Matcher { + return &Matcher{ + typesInfo: pass.TypesInfo, + } +} + +var funcNamePattern = regexp.MustCompile(`^\(?([^\\)].*)\)?\.(.*)$`) + +// MatchFunction returns true if the input call expression matches any of the list of input rules. +// A rule is a human-friend full name of a function. Some examples: +// - A public package function: +// "fmt.Errorf" +// - Match all package functions: +// "fmt.*" +// - A public function of a dependent package: +// "gitlab.com/gitlab-org/gitaly/v15/internal/structerr.NewInternal", +// - A function of a struct inside a package: +// "(*gitlab.com/gitlab-org/gitaly/v15/internal/structerr.Error).Unwrap", +// +// This Matcher doesn't support interface match (yet). +func (m *Matcher) MatchFunction(call *ast.CallExpr, rules []string) bool { + name := m.functionName(call) + if name == "" { + return false + } + for _, rule := range rules { + if m.matchRule(name, rule) { + return true + } + } + return false +} + +func (m *Matcher) matchRule(name, rule string) bool { + nameMatches := funcNamePattern.FindStringSubmatch(name) + if len(nameMatches) == 0 { + return false + } + + ruleMatches := funcNamePattern.FindStringSubmatch(rule) + if len(ruleMatches) == 0 { + return false + } + + if nameMatches[1] != ruleMatches[1] { + return false + } + + return ruleMatches[2] == "*" || nameMatches[2] == ruleMatches[2] +} + +func (m *Matcher) functionName(call *ast.CallExpr) string { + fn, ok := m.getFunction(call) + if !ok { + return "" + } + + return fn.FullName() +} + +func (m *Matcher) getFunction(call *ast.CallExpr) (*types.Func, bool) { + sel, ok := call.Fun.(*ast.SelectorExpr) + if !ok { + return nil, false + } + fn, ok := m.typesInfo.ObjectOf(sel.Sel).(*types.Func) + if !ok { + return nil, false + } + return fn, true +} |