Skip to content

issue-1250 pattern matching for instanceof in switch case#1259

Merged
msridhar merged 18 commits intouber:masterfrom
dhruv-agr:master
Sep 1, 2025
Merged

issue-1250 pattern matching for instanceof in switch case#1259
msridhar merged 18 commits intouber:masterfrom
dhruv-agr:master

Conversation

@dhruv-agr
Copy link
Copy Markdown
Contributor

@dhruv-agr dhruv-agr commented Aug 25, 2025

Fixes #1250

The goal is to make visitCase aware of pattern variables. When it encounters a case with a pattern (like case BusinessExceptionUnauthenticated e), it should:

  1. Positively identify that this is a pattern matching case, not a simple constant case.
  2. In the "then" branch (the code path for the body of the case), it must record two facts:
    • The variable being switched on (bException) is non-null and has the type of the pattern.
    • The new pattern variable (e) is also non-null.
  3. Crucially, in the "else" branch (the code path for all subsequent case labels), it must not alter the existing nullness information for the variable being switched on. This is what is
    causing the bug right now - the information is being corrupted for the following default case.

Summary by CodeRabbit

  • New Features
    • Adds support for Java switch type-pattern bindings so variables bound by a matching pattern are treated as non-null in the matching branch.
  • Bug Fixes
    • Reduces false positives for pattern matching and switch expressions involving exceptions under nullness annotations.
  • Tests
    • Adds unit tests validating pattern-matching on Throwable/RuntimeException and switch-expression nullness behavior.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Aug 25, 2025

Walkthrough

Adds type-pattern binding handling to switch-case analysis in AccessPathNullnessPropagation: when a case label binds a pattern variable, the code resolves the bound LocalVariableNode and marks it NONNULL in the matching (then) branch. Also adds two JDK17 tests exercising pattern-match and switch behavior.

Changes

Cohort / File(s) Summary
Nullness propagation: switch type-pattern support
nullaway/src/main/java/com/uber/nullaway/dataflow/AccessPathNullnessPropagation.java
Detects type-pattern bindings in switch case labels (via CaseTree/CaseNode), resolves the binding identifier to a LocalVariableNode, and sets that variable to NONNULL in the then-branch while leaving the else-branch unchanged. Adds private helper getVariableNodeForTypePattern(...), new imports (CaseTree, Tree, VariableTree, TreeUtilsAfterJava11), and updates visitCase control flow to return conditional (then/else) stores for pattern cases.
JDK17 tests for pattern matching and switch
jdk-recent-unit-tests/src/test/java/com/uber/nullaway/jdk17/SwitchTests.java
Adds two tests (issue1250, patternMatchingInstanceOfNegation) that define nested exception classes and exercise pattern matching (binding a BusinessException) and a switch expression on the bound variable to validate pattern-binding handling.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant DF as DataflowAnalyzer
  participant CN as CaseNode
  participant CT as CaseTree
  participant VN as LocalVariableNode
  participant Store as Dataflow Store

  DF->>CN: visitCase(caseNode)
  DF->>CT: inspect case label for type-pattern binding
  alt type-pattern binding present
    CT-->>DF: binding identifier
    DF->>VN: resolve binding -> LocalVariableNode
    DF->>Store: create then-store (VN = NONNULL)
    DF->>Store: create else-store (unchanged)
    DF-->>CN: return conditional result with then/else stores
  else no type-pattern binding
    DF->>CN: fallback to existing operand/equality handling
    DF-->>CN: return previous-style result
  end
  note right of Store: Only the matching (then) branch gets NONNULL fact
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • yuxincs

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d82f008 and 78cd162.

📒 Files selected for processing (1)
  • jdk-recent-unit-tests/src/test/java/com/uber/nullaway/jdk17/SwitchTests.java (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-14T18:50:06.159Z
Learnt from: msridhar
PR: uber/NullAway#1245
File: guava-recent-unit-tests/src/test/java/com/uber/nullaway/guava/NullAwayGuavaParametricNullnessTests.java:101-102
Timestamp: 2025-08-14T18:50:06.159Z
Learning: In NullAway JSpecify tests, when JDK version requirements exist due to bytecode annotation reading capabilities, prefer failing tests over skipping them on unsupported versions to ensure CI catches regressions and enforces proper JDK version usage for developers.

Applied to files:

  • jdk-recent-unit-tests/src/test/java/com/uber/nullaway/jdk17/SwitchTests.java
📚 Learning: 2025-08-29T18:41:43.566Z
Learnt from: msridhar
PR: uber/NullAway#1259
File: jdk-recent-unit-tests/src/test/java/com/uber/nullaway/jdk17/SwitchTests.java:318-321
Timestamp: 2025-08-29T18:41:43.566Z
Learning: Classes annotated with NullMarked are analyzed by NullAway even if they are not in packages specified by the AnnotatedPackages configuration. The NullMarked annotation guarantees NullAway analysis.

Applied to files:

  • jdk-recent-unit-tests/src/test/java/com/uber/nullaway/jdk17/SwitchTests.java
🔇 Additional comments (1)
jdk-recent-unit-tests/src/test/java/com/uber/nullaway/jdk17/SwitchTests.java (1)

314-343: Good: exercises NONNULL for both bound pattern var and scrutinee in the matching case.

Dereferencing both e and bException in the pattern case, and using bException in default, solidly covers the refinements and the “subsequent labels/default must not corrupt scrutinee nullness” objective. Also fine to omit package due to @NullMarked (per prior learning).

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
Collaborator

@msridhar msridhar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks promising! A few comments.

Comment thread nullaway/src/test/java/com/uber/nullaway/jspecify/GenericsTests.java Outdated
@dhruv-agr dhruv-agr marked this pull request as ready for review August 29, 2025 07:35
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
nullaway/src/test/java/com/uber/nullaway/jspecify/GenericsTests.java (1)

2576-2595: Indent test source lines for readability

Please indent the code inside these tests. This was requested previously.

Also applies to: 2606-2623

🧹 Nitpick comments (2)
nullaway/src/test/java/com/uber/nullaway/jspecify/GenericsTests.java (1)

2591-2591: Exercise the bound pattern variable in the case body

Return e.getMessage() to assert the pattern-bound var is treated NONNULL in the matching branch.

Apply:

-            "case BusinessExceptionUnauthenticated e -> \"\"; ",
+            "case BusinessExceptionUnauthenticated e -> e.getMessage();",
nullaway/src/main/java/com/uber/nullaway/dataflow/AccessPathNullnessPropagation.java (1)

1010-1011: Javadoc nit: remove stray paragraph marker

The isolated <p> adds no content.

Apply:

-   * <p>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d0e9bbb and 491a117.

📒 Files selected for processing (2)
  • nullaway/src/main/java/com/uber/nullaway/dataflow/AccessPathNullnessPropagation.java (3 hunks)
  • nullaway/src/test/java/com/uber/nullaway/jspecify/GenericsTests.java (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-08-28T04:54:20.922Z
Learnt from: msridhar
PR: uber/NullAway#1248
File: nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java:847-857
Timestamp: 2025-08-28T04:54:20.922Z
Learning: In NullAway's GenericsChecks.java, NewClassTree support for explicit type argument substitution requires more extensive changes beyond just modifying the conditional in compareGenericTypeParameterNullabilityForCall. The maintainers prefer to handle NewClassTree support in a separate follow-up rather than expanding the scope of PRs focused on specific issues like super constructor calls.

Applied to files:

  • nullaway/src/main/java/com/uber/nullaway/dataflow/AccessPathNullnessPropagation.java
  • nullaway/src/test/java/com/uber/nullaway/jspecify/GenericsTests.java
📚 Learning: 2025-08-14T18:50:06.159Z
Learnt from: msridhar
PR: uber/NullAway#1245
File: guava-recent-unit-tests/src/test/java/com/uber/nullaway/guava/NullAwayGuavaParametricNullnessTests.java:101-102
Timestamp: 2025-08-14T18:50:06.159Z
Learning: In NullAway JSpecify tests, when JDK version requirements exist due to bytecode annotation reading capabilities, prefer failing tests over skipping them on unsupported versions to ensure CI catches regressions and enforces proper JDK version usage for developers.

Applied to files:

  • nullaway/src/test/java/com/uber/nullaway/jspecify/GenericsTests.java
🧬 Code graph analysis (1)
nullaway/src/main/java/com/uber/nullaway/dataflow/AccessPathNullnessPropagation.java (1)
nullaway/src/main/java/com/uber/nullaway/dataflow/AccessPath.java (1)
  • AccessPath (78-797)
🔇 Additional comments (1)
nullaway/src/main/java/com/uber/nullaway/dataflow/AccessPathNullnessPropagation.java (1)

33-38: Imports LGTM

New imports are appropriate for pattern-case handling and used below.

Also applies to: 137-137

Comment thread nullaway/src/test/java/com/uber/nullaway/jspecify/GenericsTests.java Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
jdk-recent-unit-tests/src/test/java/com/uber/nullaway/jdk17/SwitchTests.java (1)

364-369: Remove stray semicolon after method for consistency.

Harmless, but inconsistent with surrounding style.

-   };
+   }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 491a117 and 42694e3.

📒 Files selected for processing (1)
  • jdk-recent-unit-tests/src/test/java/com/uber/nullaway/jdk17/SwitchTests.java (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-14T18:50:06.159Z
Learnt from: msridhar
PR: uber/NullAway#1245
File: guava-recent-unit-tests/src/test/java/com/uber/nullaway/guava/NullAwayGuavaParametricNullnessTests.java:101-102
Timestamp: 2025-08-14T18:50:06.159Z
Learning: In NullAway JSpecify tests, when JDK version requirements exist due to bytecode annotation reading capabilities, prefer failing tests over skipping them on unsupported versions to ensure CI catches regressions and enforces proper JDK version usage for developers.

Applied to files:

  • jdk-recent-unit-tests/src/test/java/com/uber/nullaway/jdk17/SwitchTests.java

Comment thread jdk-recent-unit-tests/src/test/java/com/uber/nullaway/jdk17/SwitchTests.java Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented Aug 29, 2025

Codecov Report

❌ Patch coverage is 71.42857% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.12%. Comparing base (2de1134) to head (78cd162).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
...llaway/dataflow/AccessPathNullnessPropagation.java 71.42% 3 Missing and 5 partials ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master    #1259      +/-   ##
============================================
- Coverage     88.16%   88.12%   -0.05%     
- Complexity     2422     2426       +4     
============================================
  Files            92       92              
  Lines          8010     8033      +23     
  Branches       1594     1600       +6     
============================================
+ Hits           7062     7079      +17     
- Misses          492      494       +2     
- Partials        456      460       +4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Copy Markdown
Collaborator

@msridhar msridhar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking better! A few more comments

Comment thread jdk-recent-unit-tests/src/test/java/com/uber/nullaway/jdk17/SwitchTests.java Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
nullaway/src/main/java/com/uber/nullaway/dataflow/AccessPathNullnessPropagation.java (2)

976-987: Mark scrutinee non-null in matching type-pattern
In visitCase (nullaway/src/main/java/com/uber/nullaway/dataflow/AccessPathNullnessPropagation.java), after thenUpdates.set(localVariableNode, NONNULL); add:

@@ –982,6 +982,8
         // Pattern variable is non-null in the matching branch
         thenUpdates.set(localVariableNode, NONNULL);
+        // Scrutinee is non-null when the type-pattern matches
+        setNonnullIfAnalyzeable(thenUpdates, switchOperand);

Add a SwitchTests.java case that dereferences the switch expression inside a matching pattern branch to verify this behavior.


1015-1034: Guard against null from BindingPatternUtils and drop brittle kind-name checks.

Rely on BindingPatternUtils.getVariable(label); if it returns null, skip. This removes the string-kind dependency and avoids potential NPEs.

-    for (Tree label : labels) {
-      String kindName = label.getKind().name();
-      if (kindName.equals("BINDING_PATTERN") || kindName.equals("PATTERN_CASE_LABEL")) {
-        VariableTree varTree = TreeUtilsAfterJava11.BindingPatternUtils.getVariable(label);
-        for (Node operand : caseNode.getCaseOperands()) {
-          Symbol operandSymbol = ASTHelpers.getSymbol(operand.getTree());
-          Symbol varSymbol = ASTHelpers.getSymbol(varTree);
-          if (operand instanceof LocalVariableNode) {
-            if (operandSymbol != null && operandSymbol.equals(varSymbol)) {
-              return (LocalVariableNode) operand;
-            }
-          }
-        }
-        throw new RuntimeException("Unexpectedly did not find the pattern variable");
-      }
-    }
+    for (Tree label : labels) {
+      VariableTree varTree = TreeUtilsAfterJava11.BindingPatternUtils.getVariable(label);
+      if (varTree == null) {
+        continue;
+      }
+      Symbol varSymbol = ASTHelpers.getSymbol(varTree);
+      for (Node operand : caseNode.getCaseOperands()) {
+        if (operand instanceof LocalVariableNode) {
+          Symbol operandSymbol = ASTHelpers.getSymbol(operand.getTree());
+          if (operandSymbol != null && operandSymbol.equals(varSymbol)) {
+            return (LocalVariableNode) operand;
+          }
+        }
+      }
+      throw new RuntimeException(
+          "Unexpectedly did not find the pattern variable for label: "
+              + state.getSourceForNode(label));
+    }
🧹 Nitpick comments (2)
jdk-recent-unit-tests/src/test/java/com/uber/nullaway/jdk17/SwitchTests.java (1)

364-369: Stray semicolon after method end.

Harmless but noisy; consider removing for clarity.

-   };
+   }
nullaway/src/main/java/com/uber/nullaway/dataflow/AccessPathNullnessPropagation.java (1)

989-997: Minor: avoid computing caseOperand when unused.

Move caseOperand acquisition into the non-pattern path.

-      Node caseOperand = caseOperands.get(0);
       LocalVariableNode localVariableNode = getVariableNodeForTypePattern(caseNode);
       if (localVariableNode != null) {
         ...
       } else {
+        Node caseOperand = caseOperands.get(0);
         AccessPath switchOperandAccessPath =
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 42694e3 and f374b60.

📒 Files selected for processing (2)
  • jdk-recent-unit-tests/src/test/java/com/uber/nullaway/jdk17/SwitchTests.java (1 hunks)
  • nullaway/src/main/java/com/uber/nullaway/dataflow/AccessPathNullnessPropagation.java (3 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-08-14T18:50:06.159Z
Learnt from: msridhar
PR: uber/NullAway#1245
File: guava-recent-unit-tests/src/test/java/com/uber/nullaway/guava/NullAwayGuavaParametricNullnessTests.java:101-102
Timestamp: 2025-08-14T18:50:06.159Z
Learning: In NullAway JSpecify tests, when JDK version requirements exist due to bytecode annotation reading capabilities, prefer failing tests over skipping them on unsupported versions to ensure CI catches regressions and enforces proper JDK version usage for developers.

Applied to files:

  • jdk-recent-unit-tests/src/test/java/com/uber/nullaway/jdk17/SwitchTests.java
📚 Learning: 2025-08-29T18:41:43.566Z
Learnt from: msridhar
PR: uber/NullAway#1259
File: jdk-recent-unit-tests/src/test/java/com/uber/nullaway/jdk17/SwitchTests.java:318-321
Timestamp: 2025-08-29T18:41:43.566Z
Learning: Classes annotated with NullMarked are analyzed by NullAway even if they are not in packages specified by the AnnotatedPackages configuration. The NullMarked annotation guarantees NullAway analysis.

Applied to files:

  • jdk-recent-unit-tests/src/test/java/com/uber/nullaway/jdk17/SwitchTests.java
📚 Learning: 2025-08-28T04:54:20.922Z
Learnt from: msridhar
PR: uber/NullAway#1248
File: nullaway/src/main/java/com/uber/nullaway/generics/GenericsChecks.java:847-857
Timestamp: 2025-08-28T04:54:20.922Z
Learning: In NullAway's GenericsChecks.java, NewClassTree support for explicit type argument substitution requires more extensive changes beyond just modifying the conditional in compareGenericTypeParameterNullabilityForCall. The maintainers prefer to handle NewClassTree support in a separate follow-up rather than expanding the scope of PRs focused on specific issues like super constructor calls.

Applied to files:

  • nullaway/src/main/java/com/uber/nullaway/dataflow/AccessPathNullnessPropagation.java
🧬 Code graph analysis (1)
nullaway/src/main/java/com/uber/nullaway/dataflow/AccessPathNullnessPropagation.java (1)
nullaway/src/main/java/com/uber/nullaway/dataflow/AccessPath.java (1)
  • AccessPath (78-797)
🔇 Additional comments (5)
jdk-recent-unit-tests/src/test/java/com/uber/nullaway/jdk17/SwitchTests.java (3)

318-321: Good use of @NullMarked to force analysis.

This ensures NullAway analyzes these helper classes without needing a com.uber package.


337-339: Dereferencing the bound variable is great.

e.toString() proves NONNULL for the pattern variable in the matching branch.


395-399: Covers else-branch propagation.

This case verifies that subsequent labels (default) keep prior nullness facts for the scrutinee.

nullaway/src/main/java/com/uber/nullaway/dataflow/AccessPathNullnessPropagation.java (2)

33-38: Imports for pattern-case support look correct.

Also applies to: 137-137


984-987: Else-branch store propagation matches objective.

No unintended nullness changes leak into following cases.

Comment thread jdk-recent-unit-tests/src/test/java/com/uber/nullaway/jdk17/SwitchTests.java Outdated
Copy link
Copy Markdown
Collaborator

@msridhar msridhar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for the fix!

@msridhar msridhar enabled auto-merge (squash) September 1, 2025 05:01
@msridhar msridhar merged commit e2b65aa into uber:master Sep 1, 2025
10 of 14 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

False positive in switch expressions with nonnullable instance

2 participants