diff options
| author | Teddy Wing | 2023-05-18 21:31:58 +0200 | 
|---|---|---|
| committer | Teddy Wing | 2023-05-18 21:31:58 +0200 | 
| commit | 5fd2ef0221f0737127f524554a872e118b178416 (patch) | |
| tree | f7ed26650c98c83f93846177f8da22093bde7144 | |
| parent | b8fde7555016618d1a9a4625c24af537e09de103 (diff) | |
| download | godefererr-5fd2ef0221f0737127f524554a872e118b178416.tar.bz2 | |
Ideas for an analyser for returning errors from defer
Still working out how to traverse the AST to be able to see the objects
I need.
| -rw-r--r-- | defererr.go | 61 | ||||
| -rw-r--r-- | defererr_test.go | 14 | ||||
| -rw-r--r-- | go.mod | 10 | ||||
| -rw-r--r-- | go.sum | 7 | ||||
| -rw-r--r-- | testdata/signature.go | 18 | 
5 files changed, 110 insertions, 0 deletions
| diff --git a/defererr.go b/defererr.go new file mode 100644 index 0000000..d512cbf --- /dev/null +++ b/defererr.go @@ -0,0 +1,61 @@ +// TODO: doc +package defererr + +import ( +	"fmt" +	"go/ast" + +	"golang.org/x/tools/go/analysis" +) + +var Analyzer = &analysis.Analyzer{ +	Name: "defererr", +	Doc:  "reports issues returning errors from defer", +	Run:  run, +} + +func run(pass *analysis.Pass) (interface{}, error) { +	// TODO: Find defer closure +	// Does it set error defined in outer scope? +	// Does outer scope declare error variable in signature? +	// Is err variable returned after closure? + +	for _, file := range pass.Files { +		ast.Inspect( +			file, +			func(node ast.Node) bool { +				deferStmt, ok := node.(*ast.DeferStmt) +				if !ok { +					return true +				} + +				// Look for a function literal after the `defer` statement. +				funcLit, ok := deferStmt.Call.Fun.(*ast.FuncLit) +				if !ok { +					return true +				} + +				funcScope := pass.TypesInfo.Scopes[funcLit.Type] + +				// Try to find the function where the defer is defined. Note, defer can be defined in an inner block. +				funcType, ok := funcScope.Parent().(*ast.FuncType) +				if !ok { +					return true +				} +				fmt.Printf("func: %#v\n", funcType) + +				if funcLit.Type.Results == nil { +					return true +				} + +				for _, returnVal := range funcLit.Type.Results.List { +					fmt.Printf("returnVal: %#v\n", returnVal) +				} + +				return true +			}, +		) +	} + +	return nil, nil +} diff --git a/defererr_test.go b/defererr_test.go new file mode 100644 index 0000000..a94b3b9 --- /dev/null +++ b/defererr_test.go @@ -0,0 +1,14 @@ +package defererr_test + +import ( +	"testing" + +	"golang.org/x/tools/go/analysis/analysistest" +	"gopkg.teddywing.com/defererr" +) + +func Test(t *testing.T) { +	testdata := analysistest.TestData() + +	analysistest.Run(t, testdata, defererr.Analyzer, ".") +} @@ -0,0 +1,10 @@ +module gopkg.teddywing.com/defererr + +go 1.20 + +require golang.org/x/tools v0.9.1 + +require ( +	golang.org/x/mod v0.10.0 // indirect +	golang.org/x/sys v0.8.0 // indirect +) @@ -0,0 +1,7 @@ +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= +golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= diff --git a/testdata/signature.go b/testdata/signature.go new file mode 100644 index 0000000..17cfd0b --- /dev/null +++ b/testdata/signature.go @@ -0,0 +1,18 @@ +package main + +import "errors" + +func declareErrInSignature() error { // want "return signature should be '(err error)'" +	var err error // Should use variable declared in signature + +	err = nil +	if err != nil { +		return err +	} + +	defer func() { +		err = errors.New("defer error") +	}() + +	return nil // want "does not return 'err'" +} | 
