diff --git a/spancheck.go b/spancheck.go index 6b8b9fd..49e5817 100644 --- a/spancheck.go +++ b/spancheck.go @@ -360,10 +360,12 @@ func usesCall( startSpanMatchers []spanStartMatcher, depth int, ) bool { - if depth > 1 { // for perf reasons, do not dive too deep thru func literals, just one level deep check. + if depth > 1 { // for perf reasons, do not dive too deep thru func literals, just two levels deep. return false } + cfgs := pass.ResultOf[ctrlflow.Analyzer].(*ctrlflow.CFGs) + found, reAssigned := false, false for _, subStmt := range stmts { stack := []ast.Node{} @@ -371,7 +373,6 @@ func usesCall( switch n := n.(type) { case *ast.FuncLit: if len(stack) > 0 { - cfgs := pass.ResultOf[ctrlflow.Analyzer].(*ctrlflow.CFGs) g := cfgs.FuncLit(n) if g != nil && len(g.Blocks) > 0 { return usesCall(pass, g.Blocks[0].Nodes, sv, selName, ignoreCheckSig, startSpanMatchers, depth+1) @@ -387,6 +388,32 @@ func usesCall( return false } } + case *ast.DeferStmt: + if n.Call == nil { + break + } + + f, ok := n.Call.Fun.(*ast.FuncLit) + if !ok { + break + } + + if g := cfgs.FuncLit(f); g != nil && len(g.Blocks) > 0 { + for _, b := range g.Blocks { + if usesCall( + pass, + b.Nodes, + sv, + selName, + ignoreCheckSig, + startSpanMatchers, + depth+1, + ) { + found = true + return false + } + } + } case nil: if len(stack) > 0 { stack = stack[:len(stack)-1] // pop diff --git a/testdata/disableerrorchecks/disable_error_checks.go b/testdata/disableerrorchecks/disable_error_checks.go index 0fd19f4..43854f9 100644 --- a/testdata/disableerrorchecks/disable_error_checks.go +++ b/testdata/disableerrorchecks/disable_error_checks.go @@ -56,3 +56,15 @@ func _() error { } func recordErr(span trace.Span, err error) {} + +// https://github.com/jjti/go-spancheck/issues/24 +func _() (err error) { + _, span := otel.Tracer("foo").Start(context.Background(), "bar") + defer func() { + recordErr(span, err) + + span.End() + }() + + return errors.New("test") +} diff --git a/testdata/enableall/enable_all.go b/testdata/enableall/enable_all.go index 5ce6480..e87ad51 100644 --- a/testdata/enableall/enable_all.go +++ b/testdata/enableall/enable_all.go @@ -249,3 +249,41 @@ func _() error { // return errors.New("test") // } + +// https://github.com/jjti/go-spancheck/issues/24 +func _() (err error) { + _, span := otel.Tracer("foo").Start(context.Background(), "bar") + defer func() { + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, "test") + } + + span.End() + }() + + return errors.New("test") +} + +func _() (err error) { + _, span := otel.Tracer("foo").Start(context.Background(), "bar") // want "span.SetStatus is not called on all paths" + defer func() { + if true { + span.End() + } + span.RecordError(err) + }() + + return errors.New("test") // want "return can be reached without calling span.SetStatus" +} + +func _() (err error) { + _, span := otel.Tracer("foo").Start(context.Background(), "bar") + defer func() { + span.RecordError(err) + span.SetStatus(codes.Error, "test") + span.End() + }() + + return errors.New("test") +}