Coder Social home page Coder Social logo

Comments (2)

willmurphyscode avatar willmurphyscode commented on June 14, 2024

Here's a quick implementation that could be wired up to golangci-lint. It hard-codes the name of the function that must defer for now, but that could be passed via config.

package analyzer

import (
	"go/ast"
	"golang.org/x/tools/go/analysis"
	"golang.org/x/tools/go/analysis/passes/inspect"
	"golang.org/x/tools/go/ast/inspector"
)

func run(pass *analysis.Pass) (any, error) {
	insp := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
	nodeFilter := []ast.Node{
		(*ast.ExprStmt)(nil),
		(*ast.DeferStmt)(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 node will have type *ast.DeferStmt)
		switch t := node.(type) {
		case *ast.ExprStmt:
			if !isExprStmtAllowed(t, pass) {
				pass.Reportf(t.Pos(), "internal.CloseAndLogError must be called in defer")
			}
		}
	})
	return nil, nil
}

func isExprStmtAllowed(e *ast.ExprStmt, pass *analysis.Pass) bool {
	call, ok := e.X.(*ast.CallExpr)
	if !ok {
		return true
	}
	sel, ok := call.Fun.(*ast.SelectorExpr)
	if !ok {
		return true
	}

	obj := pass.TypesInfo.Uses[sel.Sel]
	if obj == nil {
		return true
	}
	pkg := obj.Pkg()
	if pkg == nil {
		return true
	}

	if pkg.Path() == "github.com/anchore/syft/internal" && sel.Sel.Name == "CloseAndLogError" {
		return false
	}

	return true
}

func NewAnalyzer() *analysis.Analyzer {
	analyzer := analysis.Analyzer{
		Name:     "mustdefer",
		Doc:      "functions whose doc comemnt includes `*mustdefer*` must be invoked with the defer keyword",
		Run:      run,
		Requires: []*analysis.Analyzer{inspect.Analyzer},
	}
	return &analyzer
}

With a main function like this:

func main() {
	singlechecker.Main(analyzer.NewAnalyzer())
}

This is invoked like this:

$ ../investigations/golangci-custom-lint/mustdefer/main ./...
/Users/willmurphy/work/syft-clean/syft/linux/identify_release.go:74:4: internal.CloseAndLogError must be called in defer
/Users/willmurphy/work/syft-clean/syft/pkg/cataloger/generic/cataloger.go:132:3: internal.CloseAndLogError must be called in defer
/Users/willmurphy/work/syft-clean/syft/pkg/cataloger/golang/parse_go_binary.go:70:2: internal.CloseAndLogError must be called in defer
/Users/willmurphy/work/syft-clean/syft/pkg/cataloger/java/graalvm_native_image_cataloger.go:595:3: internal.CloseAndLogError must be called in defer

from syft.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.