Skip to content

Only run data flow analysis on Stack variables in ReplaceStackWithDeque - #1030

Merged
timtebeek merged 1 commit into
mainfrom
tim/fix-stackoverflow-replacestackwithdeque
Aug 26, 2026
Merged

Only run data flow analysis on Stack variables in ReplaceStackWithDeque#1030
timtebeek merged 1 commit into
mainfrom
tim/fix-stackoverflow-replacestackwithdeque

Conversation

@timtebeek

@timtebeek timtebeek commented Aug 26, 2026

Copy link
Copy Markdown
Member

What's changed

ReplaceStackWithDeque.visitVariable ran FindLocalFlowPaths for every initialized variable in any file that mentions java.util.Stack, and only then applied ChangeType to the initializer — a no-op for variables that aren't a Stack. Swapping the order (apply ChangeType first, bail out when it leaves the initializer untouched) is equivalent and confines the analysis to the variables the recipe can actually rewrite.

Why

This is how the crash surfaced. org.openrewrite.staticanalysis.ReplaceStackWithDeque has been failing with java.lang.StackOverflowError on spring-projects/spring-data-mongodb, file spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoExampleMapper.java, recurring since at least 2026-08-17 in the "Java best practices" and "Common static analysis issues" flagship runs on dev (ALL, Open Source, Netflix + Spring + Apache, Netflix + Spring). Confirmed in run 20260825041417-SSuzc; absent on 2026-08-26, so it is input/attribution dependent and a clean run isn't evidence of a fix.

The SourcesFileErrors data table for that run gives ~1000 frames alternating between exactly two call sites:

java.lang.StackOverflowError
  org.openrewrite.Cursor.getParentTreeCursor(Cursor.java:253)
  org.openrewrite.analysis.trait.expr.VarAccessBase.viewOf(VarAccess.java:221)
  ...
  org.openrewrite.analysis.dataflow.DataFlowNode.of(DataFlowNode.java:63)
  org.openrewrite.analysis.dataflow.analysis.ForwardFlow.computeVariableAssignment(ForwardFlow.java:458)
  org.openrewrite.analysis.dataflow.analysis.ForwardFlow.computeVariableAssignment(ForwardFlow.java:495)
  org.openrewrite.analysis.dataflow.analysis.ForwardFlow.computeVariableAssignment(ForwardFlow.java:466)
  org.openrewrite.analysis.dataflow.analysis.ForwardFlow.computeVariableAssignment(ForwardFlow.java:495)
  org.openrewrite.analysis.dataflow.analysis.ForwardFlow.computeVariableAssignment(ForwardFlow.java:466)
  ...

The added test reproduces the production stack trace exactly (502 frames at :466, 501 at :495) on main, and passes here. The Stack in it only satisfies the recipe's UsesType precondition — the overflow comes from the unrelated String a, which is also what happens in MongoExampleMapper.

Scope

…Deque`

`visitVariable` ran `FindLocalFlowPaths` for every initialized variable in
any file that mentions `java.util.Stack`, then applied `ChangeType` to the
initializer -- a no-op for variables that are not a `Stack`. Applying
`ChangeType` first and bailing out when it leaves the initializer untouched
is equivalent, and confines the analysis to the variables the recipe can
actually rewrite.

That also avoids openrewrite/rewrite-analysis#113, which is how this
surfaced: `ForwardFlow.computeVariableAssignment` recurses between its
"argument to select" and "select to argument" steps without a visited set,
so an expression like `Optional.ofNullable(a).orElse(opt.get())` -- where
both the select and an argument are themselves calls, and the outer method
is in both the `Argument[k] -> ReturnValue` and `Argument[this] ->
ReturnValue` model groups -- overflows the stack. The `Stack` in the
regression test only satisfies the recipe's `UsesType` precondition; the
overflow came from an unrelated variable, as it did on
`spring-data-mongodb`'s `MongoExampleMapper`.

The engine-level cycle remains, so a `Stack` whose flow reaches such an
expression can still overflow; that needs the upstream fix.
@github-project-automation github-project-automation Bot moved this to In Progress in OpenRewrite Aug 26, 2026
@timtebeek
timtebeek merged commit e5883e5 into main Aug 26, 2026
1 check passed
@timtebeek
timtebeek deleted the tim/fix-stackoverflow-replacestackwithdeque branch August 26, 2026 11:38
@github-project-automation github-project-automation Bot moved this from In Progress to Done in OpenRewrite Aug 26, 2026
@JLLeitschuh

Copy link
Copy Markdown
Contributor

Why is this a problem specifically for stacks? This seems like a more general problem, or like we are missing more general recursion detection that would break a recursive check

@JLLeitschuh

Copy link
Copy Markdown
Contributor

This doesn't seem like the right way of solving this problem. This seems like a more general recursion problem where we aren't checking the graph being built correctly.

Can we reconsider how we're fixing this and build something more resilient?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

2 participants