-
Notifications
You must be signed in to change notification settings - Fork 63
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
Condition Logic Checker #1114
Condition Logic Checker #1114
Conversation
Implement feature for redundant_condition_checker Refactor z3 constraints traverse in CFG module Create unit tests for redundant_condition_checker
Implement checker for impossible condition Refactor checker for redundant condition Add more unit tests for redundant and impossible condition Create examples for redundant and impossible condition
Fix an error that unregisters checker during initialization Add examples for redundant and impossible conditions Fix an unit test's expected behavior Update checker documentation Modify check-examplet to take z3 argument
modify ControlFlowGraph to append node z3_constraint only when it's not none Enable z3 option in test_examples
Initialize z3_constraint attribute of CFGBlock with None Turn on z3 option for test_black and test_check
Pull Request Test Coverage Report for Build 12405548908Details
💛 - Coveralls |
Revert changes to transforms, test_black, and test_examples
python_ta/checkers/redundant_or_impossible_condition_checker.py
Outdated
Show resolved
Hide resolved
python_ta/checkers/redundant_or_impossible_condition_checker.py
Outdated
Show resolved
Hide resolved
if not node_block.is_feasible or node_block.z3_constraint is None: | ||
return | ||
|
||
for pred in node_block.predecessors: |
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.
only include feasible edges here; this also removes the need for a node_block.is_feasible
check above, as if there are no feasible predecessor edges the loop won't run at all
|
||
node_block = node.cfg_block | ||
|
||
if not node_block.is_feasible or node_block.z3_constraint is None: |
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.
Rather than create a separate instance attribute z3_constraint
:
- Extract the
condition
of thenode
object - Create a new
Z3Environment
from the variables from the underlying CFG (you can define a newControlFlowGraph
to produce this environment). - Use the environment to parse the
condition
expression directly, in this function.
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.
Hi Professor, I'm still working on this part and am unsure how to proceed. The main issue is that I cannot determine how to access the ControlFlowGraph
instance associated with a node. Currently, I can only access the node's CFGBlock
, which does not contain a reference back to the ControlFlowGraph
.
My previous approach of adding z3_constraint
to nodes could be the most convenient implementation. I understand your design decision to avoid embedding too much information directly into the control flow graph itself. However, even if I find a way to access the ControlFlowGraph
, we would still need a mechanism to track the reassignment status of variables within the current block, as this is not currently handled by the CFG.
In summary, I’m currently unable to parse the condition effectively without modifying the graph.py
module. I’d appreciate any suggestions you might have on how to proceed without altering the core module or if adjustments to it might be acceptable.
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.
@Raine-Yang-UofT this is a good summary of your current understanding. You said:
The main issue is that I cannot determine how to access the
ControlFlowGraph
instance associated with a node. Currently, I can only access the node'sCFGBlock
, which does not contain a reference back to theControlFlowGraph
.
Every astroid node can access its enclosing frame (either a function definition or the Module
), I think the method is called .frame()
or something similar. From this you can access the enclosing CFG.
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.
@david-yz-liu Hi Professor, thanks for this hint. However, there is another tricky problem even after accessing ControlFlowGraph
- knowing the reassignment status of each variables.
In our implementation, we discard the variables being reassigned in subsequent z3 constraints. As a result, to correctly parse the node condition, we need not only the z3 variables provided by ControlFlowGraph
, but also which variables are reassigned when we reach the condition node, information we do not have even with ControlFlowGraph
.
In addition, as the variable reassignment status may be different along different execution paths, it is possible that on different execution paths the node condition constraints should be parsed differently.
Given these problems, the most convenient solution might be adding z3_constraints
to CFGBlock
during graph traverse, where we have all the information above. Let me know how should I proceed.
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.
Hi @Raine-Yang-UofT, whether or not variables are reassigned should impact the constraints stored in the edges, but this should not impact the conversion of the if/while condition.
The algorithm used by these checkers should always use the translated if/while condition. The reassignments will impact how this condition is combined with the existing path constraints that reach the condition.
Add additional comments to checker examples Add unit test for redundant-condition-checker
Rename checker to ConditionLogicChecker Add example case for redundant condition
Use single _check_unsat to handle both redundant condition and impossible condition Revert changes to transforms
…dant-or-impossible-condition-checker
Use Z3Environment of CFG variables to parse node condition Change z3_var attribute of ControlFlowGraph to public
Change condition of redundant/impossible condition to to be triggered only if the z3 check fails for all branches for all paths Add test cases for multiple feasible branches
Turn on `separate-condition-block` in CFGVisitor when `z3` config applied Update unit tests
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.
@Raine-Yang-UofT great work! I left a few inline comments, which should be pretty small.
I did note one oddity: both the redundant and impossible condition checks were being triggered on my top-level if __name__ == '__main__'
condition, even when they shouldn't have been. I'm not sure exactly why this is, can you please look into it?
CHANGELOG.md
Outdated
@@ -43,6 +43,8 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). | |||
### 💫 New checkers | |||
|
|||
- `unmentioned-parameter`: Provide error message when a function parameter is not mentioned by name in the function's docstring. By default, this checker is disabled. | |||
- `redundant-condition`: Provide error message when a conditional statement within a function is guaranteed true. This checker requires `z3` option to be turned on. |
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.
These should be moved into the unreleased
section (note that I made a version update recently, so this part of the changelog now refers to 2.9.0).
docs/checkers/index.md
Outdated
|
||
``` | ||
|
||
This error will only be checked if `z3` dependency is install and `z3` option of PythonTA |
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.
z3
dependency -> the z3-solver
library
install -> installed
is turned on -> is enabled
docs/checkers/index.md
Outdated
|
||
``` | ||
|
||
This error will only be checked if `z3` dependency is install and `z3` option of PythonTA |
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.
similar to above
return 0 | ||
|
||
|
||
def redundant_condition(x: bool): |
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.
include a return type annotation of None
here
@@ -0,0 +1,28 @@ | |||
def print_none_negative_number(x: int): |
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.
include a return type annotation
self.add_message("impossible-condition", node=node) | ||
|
||
def _check_unsat(self, prev_constraints: z3.ExprRef, node_constraint: z3.ExprRef) -> bool: | ||
"""Check if the condition is redundant.""" |
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.
This docstring isn't quite right. This function returns whether the And
of its two inputs is unsatisfiable.
tests/test_examples.py
Outdated
@@ -149,7 +150,7 @@ def test_examples_files_pyta(test_file: str, pyta_examples_symbols: dict[str, se | |||
found_pylint_message = checker_name in file_symbols | |||
assert ( | |||
found_pylint_message | |||
), f"Failed {test_file}. File does not add expected message {file_symbols}." | |||
), f"Failed {test_file}. File does not add expected message {checker_name}: {file_symbols}." |
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.
This change is okay, but please delete the extra space after the colon
for edge in (pred for pred in node_block.predecessors if pred.is_feasible) | ||
for constraints in edge.z3_constraints.values() | ||
): | ||
self.add_message("redundant-condition", node=node) |
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.
pass node.test
as the argument (so that only the condition is highlighted)
for edge in (pred for pred in node_block.predecessors if pred.is_feasible) | ||
for constraints in edge.z3_constraints.values() | ||
): | ||
self.add_message("impossible-condition", node=node) |
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.
same as above
""" | ||
Check for redundant If or While conditions in functions based on z3 constraints | ||
""" | ||
|
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.
Include a from __future__ import annotations
, otherwise the type annotation z3.ExprRef
below will raise an error if z3
is not installed
Modify comments and error messages Add type annotations to example code Change node.test to be the node highlighted Update unit tests
Add checking for condition z3 expression to be not-None Add test cases for None condition
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 all of your work on this, @Raine-Yang-UofT!
Proposed Changes
(Describe your changes here. Also describe the motivation for your changes: what problem do they solve, or how do they improve the application or codebase? If this pull request fixes an open issue, use a keyword to link this pull request to the issue.)
Implement a custom checker for the following messages:
redundant-condition
: a conditional statement is guaranteed true given the control flow leading to the statement.impossible-condition
: a conditional statement is guaranteed false either due to contradition within the statement or contradiction with conditions leading to the statement.These two messages are enabled only if Z3 dependency is installed and
z3
option is turned on for PythonTAAdditional changes:
In
check-examples
, thez3
option is set toTrue
to check forredundant-condition
andimpossible-condition
...
Screenshots of your changes (if applicable)
Type of Change
(Write an
X
or a brief description next to the type or types that best describe your changes.)Checklist
(Complete each of the following items for your pull request. Indicate that you have completed an item by changing the
[ ]
into a[x]
in the raw text, or by clicking on the checkbox in the rendered description on GitHub.)Before opening your pull request:
After opening your pull request:
Questions and Comments
(Include any questions or comments you have regarding your changes.)