From a4c276be6e5de1a021675140df457c49e473d260 Mon Sep 17 00:00:00 2001 From: Vladimir Moskva Date: Tue, 25 Apr 2023 12:14:39 +0200 Subject: [PATCH] Fix false positive for uninitialized variables (#1159) --- warn/warn_control_flow.go | 14 +++++++++++++- warn/warn_control_flow_test.go | 11 +++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/warn/warn_control_flow.go b/warn/warn_control_flow.go index 7566515de..19206a2ac 100644 --- a/warn/warn_control_flow.go +++ b/warn/warn_control_flow.go @@ -712,7 +712,7 @@ func collectLocalVariables(stmts []build.Expr) []*build.Ident { return variables } -// searchUninitializedVariables takes a list of statements (e.g. body of a block statement) +// findUninitializedVariables takes a list of statements (e.g. body of a block statement) // and a map of previously initialized statements, and calls `callback` on all idents that are not // initialized. An ident is considered initialized if it's initialized by every possible execution // path (before or by `stmts`). @@ -761,8 +761,20 @@ func findUninitializedVariables(stmts []build.Expr, previouslyInitialized map[st } } + walkBlockList := map[build.Expr]bool{} build.WalkInterruptable(expr, func(expr build.Expr, stack []build.Expr) (err error) { + if walkBlockList[expr] { + return &build.StopTraversalError{} + } switch expr := expr.(type) { + case *build.DefStmt: + // The header of the DefStmt may contain uninitialized variables (e.g. + // default values of parameters) and should be traversed. + // Its body shouldn't be traversed because it has another scope and will + // be analyzed by another call of `findUninitializedVariables`. + for _, stmt := range expr.Body { + walkBlockList[stmt] = true + } case *build.Comprehension, *build.LambdaExpr: // Comprehension and Lambda nodes are special, they have their own scope // with variables that are only defined inside. diff --git a/warn/warn_control_flow_test.go b/warn/warn_control_flow_test.go index 49e6baa8a..dc7da123f 100644 --- a/warn/warn_control_flow_test.go +++ b/warn/warn_control_flow_test.go @@ -1270,6 +1270,17 @@ def foo(): y = [baz.get(bar) for bar in bars] x = lambda bar: baz.get(bar) +`, + []string{}, + scopeEverywhere) + + checkFindings(t, "uninitialized", ` +def foo(): + def bar(x): + print(x) + + for x, y in z: + bar(x) `, []string{}, scopeEverywhere)