wire up settings

Signed-off-by: Will Murphy <will.murphy@anchore.com>
This commit is contained in:
Will Murphy 2024-05-02 11:50:35 -04:00
parent 34f9e6fec9
commit b00c492b0b

View File

@ -1,7 +1,9 @@
package ensuredefer package ensuredefer
import ( import (
"fmt"
"go/ast" "go/ast"
"strings"
"github.com/golangci/plugin-module-register/register" "github.com/golangci/plugin-module-register/register"
"golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis"
@ -10,29 +12,63 @@ import (
) )
func init() { func init() {
register.Plugin("ensuredefer", func(_ any) (register.LinterPlugin, error) { register.Plugin("ensuredefer", func(conf any) (register.LinterPlugin, error) {
return &analyzerPlugin{}, nil 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) { var toCheck []mustDeferSymbol
insp := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) symbols := m["symbols"]
nodeFilter := []ast.Node{ if symbols != nil {
(*ast.ExprStmt)(nil), s, ok := symbols.([]any)
} if !ok {
insp.Preorder(nodeFilter, func(node ast.Node) { return nil, fmt.Errorf("expected slice but got %v (%T)", symbols, symbols)
// 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 for _, maybeSym := range s {
if t, ok := node.(*ast.ExprStmt); ok { sym, ok := maybeSym.(string)
if !isExprStmtAllowed(t, pass) { if !ok {
pass.Reportf(t.Pos(), "internal.CloseAndLogError must be called in defer") 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) call, ok := e.X.(*ast.CallExpr)
if !ok { if !ok {
return true return true
@ -41,6 +77,10 @@ func isExprStmtAllowed(e *ast.ExprStmt, pass *analysis.Pass) bool {
if !ok { if !ok {
return true return true
} }
candidate, ok := toCheck[sel.Sel.Name]
if !ok {
return true
}
obj := pass.TypesInfo.Uses[sel.Sel] obj := pass.TypesInfo.Uses[sel.Sel]
if obj == nil { if obj == nil {
@ -51,34 +91,40 @@ func isExprStmtAllowed(e *ast.ExprStmt, pass *analysis.Pass) bool {
return true 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 false
} }
return true return true
} }
func NewAnalyzer() *analysis.Analyzer { type mustDeferSymbol struct {
analyzer := analysis.Analyzer{ pkg string
Name: "ensuredefer", funName string
Doc: "enforce that specified functions are called in defer statements",
Run: run,
Requires: []*analysis.Analyzer{inspect.Analyzer},
}
return &analyzer
} }
var analyzerInstance = NewAnalyzer() type analyzerPlugin struct {
symbols []mustDeferSymbol
type analyzerPlugin struct{} }
func (p *analyzerPlugin) BuildAnalyzers() ([]*analysis.Analyzer, error) { 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{ return []*analysis.Analyzer{
analyzerInstance, analyzer,
}, nil }, nil
} }
func (p *analyzerPlugin) GetLoadMode() string { func (p *analyzerPlugin) GetLoadMode() string {
//TODO: what does this do
return register.LoadModeSyntax return register.LoadModeSyntax
} }