removes the unnecessary if statements, if it was used to check a pointer's validity just to call delete on that pointer
Details
Diff Detail
- Repository
- rL LLVM
Event Timeline
Please mention this check in docs/ReleaseNotes.rst (in alphabetical order).
I think misc-delete-null-pointer will be better name.
Will be good idea to change some if statements in regression test to (p != nullptr) (for C++11) and (p != NULL) (pre-C+11). See http://clang.llvm.org/extra/clang-tidy/checks/readability-implicit-bool-cast.html.
Some comments after a quick look to the code.
clang-tidy/misc/DeleteNullCheck.cpp | ||
---|---|---|
22 ↗ | (On Diff #60549) | HasDeleteExpr -> DeleteExpr |
23 ↗ | (On Diff #60549) | The use of hasDescendant is Expensive. If the intend of this match-statement is to match comparison against NULL, it should be expanded to be more precise. |
47 ↗ | (On Diff #60549) | This check could be moved to the matcher part above/ |
48 ↗ | (On Diff #60549) | see: equalsBoundNode |
49 ↗ | (On Diff #60549) | This check should be moved to the matcher part too. |
clang-tidy/misc/DeleteNullCheck.cpp | ||
---|---|---|
23 ↗ | (On Diff #60549) | I think you want to use has() instead of hasDescendant() here. Otherwise, I think this code may trigger on delete foo(some_declref) where foo() returns a pointer. You may also need to ignore implicit casts here. |
35 ↗ | (On Diff #60549) | Won't this trigger on code like if (foo && bar) delete foo->bar;? It doesn't look like you handle that sort of case below. |
docs/clang-tidy/checks/misc-delete-null-pointer.rst | ||
---|---|---|
10 ↗ | (On Diff #76803) | Please indent variable declaration. |
clang-tidy/misc/DeleteNullPointerCheck.cpp | ||
---|---|---|
54 ↗ | (On Diff #77015) | Rather than a parenthetical, I would just use a semicolon to distinguish the clauses. Also, I would quote 'if' to make it clear that you're talking about syntax rather than an English term. |
clang-tidy/misc/DeleteNullPointerCheck.h | ||
19 ↗ | (On Diff #77015) | You should write that short description. |
test/clang-tidy/misc-delete-null-pointer.cpp | ||
---|---|---|
11 ↗ | (On Diff #77015) | Is there check-fixes-not? This seems to be required here, because even if the fixit won't happen here, the test will pass. |
test/clang-tidy/readability-delete-null-pointer.cpp | ||
---|---|---|
3 ↗ | (On Diff #77972) | We don't rely on implementations of standard headers in lit test. You should fake the function/class that you need in this test. |
7 ↗ | (On Diff #77972) | Does it work the case like: int *p = nullptr; if (p == nullptr) { p = new int[3]; delete[] p; } ? |
test/clang-tidy/readability-delete-null-pointer.cpp | ||
---|---|---|
7 ↗ | (On Diff #77972) | it warns only if the compund statement contains only one statement (which is the delete). We want to warn because it is unnecessary to check the pointer validity if you want to just call delete. In other cases, we can't be sure about the actual behaviour. |
test/clang-tidy/readability-delete-null-pointer.cpp | ||
---|---|---|
7 ↗ | (On Diff #77972) | In my example, the compound statement does contain only one statement, the delete, but diagnosing is likely a false positive due to the else clause. In that case, the pointer validity controls more than just the delete because it also controls whether to execute the else clause. Removing the if clause shouldn't be a mechanical change in the presence of an else clause, so the fixit is definitely inappropriate. I think that diagnosing is still pretty reasonable, however. Another test case, which I think may already be handled appropriately but should still be explicitly tested: if (p) { // This comment should not disable the check or the fixit. // Nor should this comment. delete p; } I think this check should still be able to remove the if clause, but we should make sure that the comments don't disable the check, and that the fixit doesn't destroy the comments. |
It looks good to me, but you'd better wait for the approval from @aaron.ballman.
clang-tidy/readability/DeleteNullPointerCheck.cpp | ||
---|---|---|
55 ↗ | (On Diff #79338) | I would use an early return if (IfWithDelete->getElse()) return here. |
clang-tidy/readability/DeleteNullPointerCheck.cpp | ||
---|---|---|
52 ↗ | (On Diff #79338) | Rename D to Diag, please. |
55 ↗ | (On Diff #79338) | Definitely use early exit. |
56 ↗ | (On Diff #79338) | This variable is unused. |
72–76 ↗ | (On Diff #79338) | Please clang-format the file. |
test/clang-tidy/readability-delete-null-pointer.cpp | ||
20 ↗ | (On Diff #79338) | Please truncate repeated static parts of the check patterns that exceed 80 characters (e.g. remove the deleting null pointer has no effect [readability-delete-null-pointer] part from all but the first CHECK line). |
53 ↗ | (On Diff #79338) | Please add CHECK-FIXES lines. Now there's no easy way to see from the test whether any fixes are applied here. |
remove unused string
using early exit in condition
shorten check-message lines
add check-fisex to 'else' part
clang-tidy/readability/DeleteNullPointerCheck.cpp | ||
---|---|---|
33 ↗ | (On Diff #81458) | I think allOf matcher is redundant here because ifStmt takes variadic number of arguments and matches only if all of them matches. |
If @alexfh doesn't sign off by tomorrow, I think it's fine to commit. We can deal with any last minute comments post-commit.
clang-tidy/readability/DeleteNullPointerCheck.cpp | ||
---|---|---|
29 ↗ | (On Diff #82401) | Since binaryOperator (and most if not all other node matchers) is VariadicDynCastAllOfMatcher, allOf is redundant here (similar to Piotr's comment below). Same for compoundStmt below. |
docs/clang-tidy/checks/readability-delete-null-pointer.rst | ||
6 ↗ | (On Diff #82401) | Enclose if in double backquotes instead of single quotes. |
7 ↗ | (On Diff #82401) | Either null pointer or nullptr (enclosed in double backquotes). |
10 ↗ | (On Diff #82401) | Insert an empty line above and check that Sphinx can build this. |
test/clang-tidy/readability-delete-null-pointer.cpp | ||
7 ↗ | (On Diff #82401) | This check (also combined with the ones below) is useless. It will work for the unchanged file as well: it will skip the if (p) line and find the comment, the next CHECK-FIXES will find the delete p; line and the CHECK-FIXES-NOT will find no lines matching if (p) between them. I'd use something like this: // comment1 if (p) { // comment 2 delete p; } // comment 3 // CHECK-FIXES: {{^ }}// comment1 // CHECK-FIXES-NEXT: {{^ }} // comment2 // CHECK-FIXES-NEXT: {{^ }} delete p; // CHECK-FIXES-NEXT: {{^ }} // comment3 Same for patterns below. |
Yup, that's totally fine, especially when there was a thorough pre-commit code review.
Clarification: it's totally fine to submit without waiting for me longer than a grace period of a day or so, once all comments are addressed and other reviewers have LGTM'd the patch.
However, here specifically I have a few more comments ;)
I've noticed a few more minor issues. Otherwise looks good.
Thank you for the new check!
clang-tidy/readability/DeleteNullPointerCheck.cpp | ||
---|---|---|
27–38 ↗ | (On Diff #82732) | This can be simplified. Something like this (modulo formatting): const auto PointerExpr = ignoringImpCasts(declRefExpr(to(decl().bind("deletedPointer")))); const auto PointerCondition = castExpr(hasCastKind(CK_PointerToBoolean), hasSourceExpression(PointerExpr)); const auto BinaryPointerCheckCondition = binaryOperator(hasEitherOperand(castExpr(hasCastKind(CK_NullToPointer))), hasEitherOperand(PointerExpr)); (and remove the second hasCondition). |
53 ↗ | (On Diff #82732) | The check can suggest a fix in this case as well, but it's a bit more involved. Please add a FIXME. |
docs/clang-tidy/checks/readability-delete-null-pointer.rst | ||
7 ↗ | (On Diff #82401) | Sorry for not being clear enough: "null pointer" is not an inline code snippet, it shouldn't be enclosed in double backquotes or anything else. The "(enclosed in double backquotes)" part was meant to apply to nullptr only (since it's a keyword and should be highlighted as a code snippet). |
test/clang-tidy/readability-delete-null-pointer.cpp | ||
8 ↗ | (On Diff #82732) | The that should not be deleted part is superfluous, IMO. You could even leave just // #1, // #2, etc. |
docs/clang-tidy/checks/readability-delete-null-pointer.rst | ||
---|---|---|
7 ↗ | (On Diff #82401) | To be honest, I don't even understand why I did what I did... :D |
One more late comment (I should really add a check-list for new checks): this check lacks tests with macros and templates with multiple instantiations.
Incorrect handling of templates will likely not manifest in the current state of the check, it's brittle, since it relies on the error deduplication performed by clang-tidy and it can break easily (e.g. if message text will depend on the instantiation or if something changes in the way clang-tidy deduplicates messages). However, attempts to apply fixes to code resulting from macro expansions is unlikely to result in compilable code.