Fix crash in bugprone-redundant-branch-condition on ExprWithCleanups.
Details
Diff Detail
Event Timeline
Think this has an incorrect name, seems to have something to do with bugprone-redundant-branch-condition
| clang-tools-extra/clang-tidy/bugprone/RedundantBranchConditionCheck.cpp | ||
|---|---|---|
| 87 | Please fix warning. | |
| clang-tools-extra/clang-tidy/bugprone/RedundantBranchConditionCheck.cpp | ||
|---|---|---|
| 85–91 | Under what circumstances does getCond() return nullptr? | |
| 90 | You can call ExprWithCleanupsCond->getSubExpr() to do this more cleanly -- but similar question here as above -- what circumstances lead to a null sub expression? | |
| 138–144 | Same feedback here as above. | |
| clang-tools-extra/clang-tidy/bugprone/RedundantBranchConditionCheck.cpp | ||
|---|---|---|
| 85–91 | That's what I figured, but you changed dyn_cast<> to be dyn_cast_or_null<> and that seems incorrect -- getCond() doesn't return null so the dyn_cast<> was correct. | |
| clang-tools-extra/clang-tidy/bugprone/RedundantBranchConditionCheck.cpp | ||
|---|---|---|
| 85–91 | dyn_cast asserts unless getCond() returns BinaryExpression, right? | |
| clang-tools-extra/clang-tidy/bugprone/RedundantBranchConditionCheck.cpp | ||
|---|---|---|
| 85–91 |
Nope. cast<> asserts if the cast cannot be completed or if the cast input is null. | |
| clang-tools-extra/clang-tidy/bugprone/RedundantBranchConditionCheck.cpp | ||
|---|---|---|
| 85–91 | Yes, you are right, I definitely need a vacation :-) | |
| clang-tools-extra/clang-tidy/bugprone/RedundantBranchConditionCheck.cpp | ||
|---|---|---|
| 145 | The old code used to assert that CondOp was a BinaryOperator but the new code means that CondOp can be null -- should you add an assert in to ensure we have a valid CondOp still or will that assert trigger on some constructs? | |
| clang-tools-extra/test/clang-tidy/checkers/bugprone-redundant-branch-condition.cpp | ||
| 1077 | Can remove the newline here. | |
Taking a step back, rather than worrying about if its an ExprWithCleanups. Shouldn't we just get the condition removing all implicit nodes.
const Expr* Cond = InnerIf->getCond()->ignoreImplicit();
This has to be simpler and will likely future proof this against any other implicit nodes potentially added in future that can appear between the Condition and the binary operator.
| clang-tools-extra/clang-tidy/bugprone/RedundantBranchConditionCheck.cpp | ||
|---|---|---|
| 145 | Reverted to cast. | |
| clang-tools-extra/test/clang-tidy/checkers/bugprone-redundant-branch-condition.cpp | ||
|---|---|---|
| 1092 | This line isn't really doing anything. CHECK-FIXES just runs FileCheck on the whole file after clang tidy has applied any fixes. You need to explicitly say what you are expecting it to be. | |
| clang-tools-extra/clang-tidy/bugprone/RedundantBranchConditionCheck.cpp | ||
|---|---|---|
| 45 | Just noticed, as this is ignoring Parenthesis and implicit nodes/casts, maybe we should also be ignoring those when getting the condition in the check if (IsSet){ if ((OtherCond && IsSet)) ; } | |
| clang-tools-extra/clang-tidy/bugprone/RedundantBranchConditionCheck.cpp | ||
|---|---|---|
| 45 | You are right, thanks! I fixed it. | |
| clang-tools-extra/test/clang-tidy/checkers/bugprone-redundant-branch-condition.cpp | ||
|---|---|---|
| 1092 | There's not a whole lot of context for FileCheck to determine if it's been correctly applied or not (same below) -- for instance, won't this pass even if no changes are applied because FileCheck is still going to find isSet in the file? | |
| clang-tools-extra/test/clang-tidy/checkers/bugprone-redundant-branch-condition.cpp | ||
|---|---|---|
| 1092 | Thanks. Fixed. | |
| clang-tools-extra/test/clang-tidy/checkers/bugprone-redundant-branch-condition.cpp | ||
|---|---|---|
| 1092 | Maybe it's just early in the morning for me, but... I was expecting the transformation to be: if (RetT::Test(isSet).Ok() && isSet) {
if (RetT::Test(isSet).Ok() && isSet) {
}
}turns into if (RetT::Test(isSet).Ok() && isSet) {
}Why does it remove the && isSet instead? That seems like it's changing the logic here from if (true && false) to if (true). | |
| clang-tools-extra/test/clang-tidy/checkers/bugprone-redundant-branch-condition.cpp | ||
|---|---|---|
| 1092 | IMO it's correct.
As I understand only the second if is transformed. | |
| clang-tools-extra/test/clang-tidy/checkers/bugprone-redundant-branch-condition.cpp | ||
|---|---|---|
| 1092 | Does this only trigger as a redundant branch condition if the definition of RetT::Test() is available? Because Test() takes a bool& so it sure seems like isSet could be modified between the branches if the definition isn't found because it's being passed as a non-const reference to Test(). | |
| clang-tools-extra/test/clang-tidy/checkers/bugprone-redundant-branch-condition.cpp | ||
|---|---|---|
| 1092 |
Removing the body from RetT::Test changes nothing.
| |
LGTM!
| clang-tools-extra/test/clang-tidy/checkers/bugprone-redundant-branch-condition.cpp | ||
|---|---|---|
| 1092 | Given the following four ways of declaring Test(): static RetT Test(bool &_isSet); // #1
static RetT Test(bool _isSet); // #2
static RetT Test(const bool &_isSet); // #3
static RetT Test(bool &_isSet) { return 0; } // #4I would expect #2 and #3 to behave the same way -- the isSet argument could never be modified by calling Test() and so the second Test(isSet) && isSet is partially redundant and the second if statement can drop the && isSet. (Without dropping the Test(isSet) because the call could still modify some global somewhere, etc.) I would expect #1 to not modify the second if statement at all because there's no way of knowing whether Test(isSet) && isSet is the same between the first if statement and the second one (because the second call to Test(isSet) may modify isSet in a way the caller can observe). Ideally, #4 would be a case where we could remove the entire second if statement because we can identify that not only does isSet not get modified despite being passed by non-const reference, we can see that the Test() function doesn't modify any state at all. However, this seems like it would require data flow analysis and so I think it makes sense to treat #4 the same as #1. That said, I just realized the check isn't being very careful with reference parameters in the first place: https://godbolt.org/z/P1aP3W, so your changes aren't introducing a new problem, they just happened to highlight an existing issue. | |
| clang-tools-extra/test/clang-tidy/checkers/bugprone-redundant-branch-condition.cpp | ||
|---|---|---|
| 1092 | Please look at https://reviews.llvm.org/D91495 - there's a fix for it. | |
Just noticed, as this is ignoring Parenthesis and implicit nodes/casts, maybe we should also be ignoring those when getting the condition in the check
InnerIf->getCond()->IgnoreParenImpCasts()
I reckon if that change isnt made this could fail on