-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Fix: Recover boolean test flag after visiting subexpressions #13909
Conversation
|
Thanks. Would you mind adding a test demonstrating that restoring the flag is necessary? |
@MichaReiser Could you direct me to where this test would fit? |
We unfortunately don't have any semantic model tests. So the best you can do is to extend the tests of a rule that uses the flag. One such rule is
RUF019.py )
|
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.
Could you explain me why restoring the flags here is necessary.
To me, it doesn't seem necessary because restoring the semantic.flags
here means that the BOOLEAN_TEST
flag won't be set for the analyze::expression
phase.
We do reset the `semantic.flags on line 1480 so that it is correctly restored for its ancestor and sibling expressions
Take for example this simple code: if not f(a):
pass Currently, when visiting the expression f(a), it loses its BOOLEAN_TEST flag. This happens because it doesn’t match the conditions for
But the Example with Current vs. Proposed BehaviorTo see the difference, here’s a comparison using various test cases: if not a(b):
pass
assert c(d)
x = e(f) or g(h)
while i == j:
pass
Testing the ChangeTesting this change with current rules is difficult, as they cover only Expr::BoolOp. However, implementing rule |
Can you expand on why do you think it's an issue for this specific rule? I think that rule would need to be implemented at the |
I intended to illustrate with the rule example that the I agree with your interpretation of how the
|
|
||
// Restore boolean test flag after examining subexpressions | ||
if flags_snapshot.intersects(SemanticModelFlags::BOOLEAN_TEST) { | ||
self.semantic.flags |= SemanticModelFlags::BOOLEAN_TEST; | ||
} | ||
|
||
// Step 4: Analysis | ||
analyze::expression(expr, self); | ||
match expr { |
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.
Thanks for the explanation. The change now makes sense to me. However, I do think that this problem isn't specific to BOOLEAN_TEST
but applies to all flags.
The flags should be restored when visiting the expression itself. They're only relevant when visiting the sub-expressions.
That's why I suggest that we change the code to:
// Step 4: Analysis
match expr {
Expr::StringLiteral(string_literal) => {
analyze::string_like(string_literal.into(), self);
}
Expr::BytesLiteral(bytes_literal) => analyze::string_like(bytes_literal.into(), self),
Expr::FString(f_string) => analyze::string_like(f_string.into(), self),
_ => {}
}
self.semantic.flags = flags_snapshot;
analyze::expression(expr, self);
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.
I'm not sure about this. Could there be cases where a subexpression changes the semantics of its parent expression?
If so, this approach might not be correct.
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.
I skimmed through the SemanticFlags
and I didn't spot any expression level flag that would change the flags for the parent or a sibling.
Thanks and nice find! |
Summary
This PR corrects the logic for handling the boolean test flag during expression analysis.
Since the flag can be modified during subexpression traversal, it must be restored before analyzing the full expression.
Additionally, this change allows the
in_bool_test()
method incrates/ruff_python_semantic/src/model.rs
to be used outside of boolean operations, such as in conditional tests withinStmt::If
,Stmt::While
,Stmt::Assert
, and others.