-
Notifications
You must be signed in to change notification settings - Fork 1.8k
C#: Bugfix for nullguards for complex patterns. #20449
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
C#: Bugfix for nullguards for complex patterns. #20449
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR fixes a bug in the C# code analysis library where pattern matching expressions were incorrectly identifying null guards in the wrong branches. The fix addresses complex patterns like x is (string or null) and x is not string that were being analyzed incorrectly.
Key changes:
- Introduces a new helper function
patternContainsNullto properly evaluate whether a pattern matches null values - Replaces simple null literal checks with comprehensive pattern analysis that handles complex nested patterns
- Fixes the logic for determining which branch represents a null guard condition
michaelnebel
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for doing this!
- Added some questions/comments.
- It looks like some tests are failing.
- We should probably run DCA.
| private import AbstractValues | ||
|
|
||
| /** Gets the value resulting from matching `null` against `pat`. */ | ||
| private boolean patternContainsNull(PatternExpr pat) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name might be a bit misleading at the pattern can have "mentions" (and thus containing) null without matching null. Maybe rename to matchesNull.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I was thinking "contains" in the sense that the pattern represents a set of values. But yes, I'll change it, patternMatchesNull is better.
| // E.g. `x is string` or `x is ""` | ||
| not pm.getPattern() instanceof NullLiteral and | ||
| branch = true and | ||
| branch.booleanNot() = patternContainsNull(pm.getPattern()) and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe consider elaborating the comment above.
Do we need the dual
// E.g. `x is null` or `x is null or ...`
branch = patternContainsNull(pm.getPattern()) and
isNull = true
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. Just because a pattern will match null doesn't mean that it's the only value that it'll match, so we can't conclude that the value is null based on a match or no-match branch edge (unless the pattern is literally null). I guess we could add another case for x is not null, which does imply x == null in its false branch, but I think I'll postpone that particular tweak to my work on instantiating Guards, because then we can handle those cases in slightly more generality.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yes, that is of course correct.
There is some overlap in this disjunct with
// E.g. `x is null`
pm.getPattern() instanceof NullLiteral and
isNull = branch
should the above be changed to only handle isNull = branch = true as a special case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, but probably doesn't matter. Regardless, this is just a stepping stone - I'm working on a shared Guards instantiation in a separate branch where this will be tweaked anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent! Once again, thank you!
Already done, we get some nice FP reduction. I'll add a change note describing that. |
michaelnebel
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
Patterns like
x is (string or null)andx is not stringwere being identified as null guards in the wrong branch.