Index: clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tidy/misc/CMakeLists.txt +++ clang-tidy/misc/CMakeLists.txt @@ -3,6 +3,7 @@ add_clang_library(clangTidyMiscModule ArgumentCommentCheck.cpp AssertSideEffectCheck.cpp + DeleteNullPointerCheck.cpp MisplacedConstCheck.cpp UnconventionalAssignOperatorCheck.cpp BoolPointerImplicitConversionCheck.cpp Index: clang-tidy/misc/DeleteNullPointerCheck.h =================================================================== --- /dev/null +++ clang-tidy/misc/DeleteNullPointerCheck.h @@ -0,0 +1,35 @@ +//===--- DeleteNullPointerCheck.h - clang-tidy-------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_DELETE_NULL_POINTER_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_DELETE_NULL_POINTER_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace misc { + +/// FIXME: Write a short description. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/misc-delete-null-pointer.html +class DeleteNullPointerCheck : public ClangTidyCheck { +public: + DeleteNullPointerCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace misc +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_DELETE_NULL_POINTER_H Index: clang-tidy/misc/DeleteNullPointerCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/misc/DeleteNullPointerCheck.cpp @@ -0,0 +1,66 @@ +//===--- DeleteNullPointerCheck.cpp - clang-tidy---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DeleteNullPointerCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Lex/Lexer.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace misc { + +void DeleteNullPointerCheck::registerMatchers(MatchFinder *Finder) { + const auto DeleteExpr = + cxxDeleteExpr(has(castExpr(has(declRefExpr( + to(decl(equalsBoundNode("deletedPointer")))))))) + .bind("deleteExpr"); + + const auto PointerCondition = castExpr(hasCastKind(CK_PointerToBoolean)); + const auto BinaryPointerCheckCondition = binaryOperator( + allOf(hasEitherOperand(castExpr(hasCastKind(CK_NullToPointer))), + hasEitherOperand(ignoringImpCasts(declRefExpr())))); + + Finder->addMatcher( + ifStmt(allOf(hasCondition( + anyOf(PointerCondition, BinaryPointerCheckCondition)), + hasCondition(anyOf( + ignoringImpCasts( + declRefExpr(to(decl().bind("deletedPointer")))), + binaryOperator(hasEitherOperand(ignoringImpCasts( + declRefExpr(to(decl().bind("deletedPointer")))))))), + hasThen(anyOf( + DeleteExpr, + compoundStmt(allOf(has(DeleteExpr), statementCountIs(1))) + .bind("compound"))))) + .bind("ifWithDelete"), + this); +} + +void DeleteNullPointerCheck::check(const MatchFinder::MatchResult &Result) { + const auto *IfWithDelete = Result.Nodes.getNodeAs("ifWithDelete"); + const auto *Delete = Result.Nodes.getNodeAs("deleteExpr"); + + auto D = + diag(IfWithDelete->getLocStart(), + "if statement is unnecessary (deleting null pointer has no effect)"); + std::string ReplacementText = Lexer::getSourceText( + CharSourceRange::getTokenRange(Delete->getSourceRange()), + *Result.SourceManager, Result.Context->getLangOpts()); + ReplacementText += ';'; + + D << FixItHint::CreateReplacement(IfWithDelete->getSourceRange(), + ReplacementText); +} + +} // namespace misc +} // namespace tidy +} // namespace clang Index: clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tidy/misc/MiscTidyModule.cpp +++ clang-tidy/misc/MiscTidyModule.cpp @@ -12,6 +12,7 @@ #include "../ClangTidyModuleRegistry.h" #include "ArgumentCommentCheck.h" #include "AssertSideEffectCheck.h" +#include "DeleteNullPointerCheck.h" #include "MisplacedConstCheck.h" #include "UnconventionalAssignOperatorCheck.h" #include "BoolPointerImplicitConversionCheck.h" @@ -63,6 +64,8 @@ CheckFactories.registerCheck("misc-argument-comment"); CheckFactories.registerCheck( "misc-assert-side-effect"); + CheckFactories.registerCheck( + "misc-delete-null-pointer"); CheckFactories.registerCheck( "misc-misplaced-const"); CheckFactories.registerCheck( Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -58,6 +58,7 @@ misc-bool-pointer-implicit-conversion misc-dangling-handle misc-definitions-in-headers + misc-delete-null-pointer misc-fold-init-type misc-forward-declaration-namespace misc-inaccurate-erase Index: docs/clang-tidy/checks/misc-delete-null-pointer.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/misc-delete-null-pointer.rst @@ -0,0 +1,12 @@ +.. title:: clang-tidy - misc-delete-null-pointer + +misc-delete-null-pointer +======================== + +Checks the if statements where a pointer's existence is checked and then deletes the pointer. +The check is unnecessary as deleting a nullpointer has no effect. + +.. code:: c++ + int *p; + if (p) + delete p; Index: test/clang-tidy/misc-delete-null-pointer.cpp =================================================================== --- /dev/null +++ test/clang-tidy/misc-delete-null-pointer.cpp @@ -0,0 +1,49 @@ +// RUN: %check_clang_tidy %s misc-delete-null-pointer %t + +#include + +void f() { + int *p = 0; + if (p) { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: if statement is unnecessary (deleting null pointer has no effect) [misc-delete-null-pointer] + delete p; + } + // CHECK-FIXES: delete p; + int *p3 = new int[3]; + if (p3) + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: if statement is unnecessary (deleting null pointer has no effect) [misc-delete-null-pointer] + delete[] p3; + // CHECK-FIXES: delete[] p3; + + if (NULL != p) { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: if statement is unnecessary (deleting null pointer has no effect) [misc-delete-null-pointer] + delete p; + } + // CHECK-FIXES: delete p; + + if (p != nullptr) { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: if statement is unnecessary (deleting null pointer has no effect) [misc-delete-null-pointer] + delete p; + } + // CHECK-FIXES: delete p; + + if (p != 0) { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: if statement is unnecessary (deleting null pointer has no effect) [misc-delete-null-pointer] + delete p; + } + // CHECK-FIXES: delete p; +} + +void g() { + int *p, *p2; + if (p) + delete p2; + + if (p && p2) + delete p; + + if (p2) { + int x = 5; + delete p2; + } +}