From b00c492b0b4f5fef1d5e1df50c3bbf36f02050eb Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Thu, 2 May 2024 11:50:35 -0400 Subject: [PATCH] wire up settings Signed-off-by: Will Murphy --- test/linters/ensuredefer/analyzer.go | 108 +++++++++++++++++++-------- 1 file changed, 77 insertions(+), 31 deletions(-) diff --git a/test/linters/ensuredefer/analyzer.go b/test/linters/ensuredefer/analyzer.go index ea79372f2..c535b16f6 100644 --- a/test/linters/ensuredefer/analyzer.go +++ b/test/linters/ensuredefer/analyzer.go @@ -1,7 +1,9 @@ package ensuredefer import ( + "fmt" "go/ast" + "strings" "github.com/golangci/plugin-module-register/register" "golang.org/x/tools/go/analysis" @@ -10,29 +12,63 @@ import ( ) func init() { - register.Plugin("ensuredefer", func(_ any) (register.LinterPlugin, error) { - return &analyzerPlugin{}, nil - }) -} + register.Plugin("ensuredefer", func(conf any) (register.LinterPlugin, error) { + m, ok := conf.(map[string]any) + if !ok { + return nil, fmt.Errorf("expected map[string]any but got %+v (%T)", conf, conf) + } -func run(pass *analysis.Pass) (any, error) { - insp := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) - nodeFilter := []ast.Node{ - (*ast.ExprStmt)(nil), - } - insp.Preorder(nodeFilter, func(node ast.Node) { - // if we have a *ast.ExprStmt that calls internal.CloseAndLogError, report a problem. - // (if the function is correctly called in a defer statement, the block will have - if t, ok := node.(*ast.ExprStmt); ok { - if !isExprStmtAllowed(t, pass) { - pass.Reportf(t.Pos(), "internal.CloseAndLogError must be called in defer") + var toCheck []mustDeferSymbol + symbols := m["symbols"] + if symbols != nil { + s, ok := symbols.([]any) + if !ok { + return nil, fmt.Errorf("expected slice but got %v (%T)", symbols, symbols) + } + for _, maybeSym := range s { + sym, ok := maybeSym.(string) + if !ok { + return nil, fmt.Errorf("expect slice of string but found %v (%T)", maybeSym, maybeSym) + } + + splitAt := strings.LastIndex(sym, ".") + if splitAt == -1 { + return nil, fmt.Errorf("symbols must have form example.com/some/package.SomeMethod, but got %s", sym) + } + toCheck = append(toCheck, mustDeferSymbol{ + pkg: sym[:splitAt], + funName: sym[splitAt+1:], + }) } } + + return &analyzerPlugin{ + symbols: toCheck, + }, nil }) - return nil, nil } -func isExprStmtAllowed(e *ast.ExprStmt, pass *analysis.Pass) bool { +func makeRun(toCheck map[string]mustDeferSymbol) func(pass *analysis.Pass) (any, error) { + return func(pass *analysis.Pass) (any, error) { + insp := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) + nodeFilter := []ast.Node{ + (*ast.ExprStmt)(nil), + } + insp.Preorder(nodeFilter, func(node ast.Node) { + // if we have a *ast.ExprStmt that calls internal.CloseAndLogError, report a problem. + // (if the function is correctly called in a defer statement, the statement will have type + // *ast.DeferStmt.) + if t, ok := node.(*ast.ExprStmt); ok { + if !isExprStmtAllowed(t, pass, toCheck) { + pass.Reportf(t.Pos(), "internal.CloseAndLogError must be called in defer") + } + } + }) + return nil, nil + } +} + +func isExprStmtAllowed(e *ast.ExprStmt, pass *analysis.Pass, toCheck map[string]mustDeferSymbol) bool { call, ok := e.X.(*ast.CallExpr) if !ok { return true @@ -41,6 +77,10 @@ func isExprStmtAllowed(e *ast.ExprStmt, pass *analysis.Pass) bool { if !ok { return true } + candidate, ok := toCheck[sel.Sel.Name] + if !ok { + return true + } obj := pass.TypesInfo.Uses[sel.Sel] if obj == nil { @@ -51,34 +91,40 @@ func isExprStmtAllowed(e *ast.ExprStmt, pass *analysis.Pass) bool { return true } - if pkg.Path() == "github.com/anchore/syft/internal" && sel.Sel.Name == "CloseAndLogError" { + if pkg.Path() == candidate.pkg && sel.Sel.Name == candidate.funName { return false } return true } -func NewAnalyzer() *analysis.Analyzer { - analyzer := analysis.Analyzer{ - Name: "ensuredefer", - Doc: "enforce that specified functions are called in defer statements", - Run: run, - Requires: []*analysis.Analyzer{inspect.Analyzer}, - } - return &analyzer +type mustDeferSymbol struct { + pkg string + funName string } -var analyzerInstance = NewAnalyzer() - -type analyzerPlugin struct{} +type analyzerPlugin struct { + symbols []mustDeferSymbol +} func (p *analyzerPlugin) BuildAnalyzers() ([]*analysis.Analyzer, error) { + analyzer := &analysis.Analyzer{ + Name: "ensuredefer", + Doc: "enforce that specified functions are called in defer statements", + Requires: []*analysis.Analyzer{inspect.Analyzer}, + } + + callsToCheck := make(map[string]mustDeferSymbol) + for _, s := range p.symbols { + callsToCheck[s.funName] = s + } + analyzer.Run = makeRun(callsToCheck) + return []*analysis.Analyzer{ - analyzerInstance, + analyzer, }, nil } func (p *analyzerPlugin) GetLoadMode() string { - //TODO: what does this do return register.LoadModeSyntax }