Index: clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tidy/misc/CMakeLists.txt +++ clang-tidy/misc/CMakeLists.txt @@ -6,6 +6,7 @@ MiscTidyModule.cpp RedundantSmartptrGet.cpp SwappedArgumentsCheck.cpp + UnusedRAII.cpp UseOverride.cpp LINK_LIBS Index: clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tidy/misc/MiscTidyModule.cpp +++ clang-tidy/misc/MiscTidyModule.cpp @@ -14,6 +14,7 @@ #include "BoolPointerImplicitConversion.h" #include "RedundantSmartptrGet.h" #include "SwappedArgumentsCheck.h" +#include "UnusedRAII.h" #include "UseOverride.h" namespace clang { @@ -35,6 +36,9 @@ "misc-swapped-arguments", new ClangTidyCheckFactory()); CheckFactories.addCheckFactory( + "misc-unused-raii", + new ClangTidyCheckFactory()); + CheckFactories.addCheckFactory( "misc-use-override", new ClangTidyCheckFactory()); } Index: clang-tidy/misc/UnusedRAII.h =================================================================== --- /dev/null +++ clang-tidy/misc/UnusedRAII.h @@ -0,0 +1,46 @@ +//===--- UnusedRAII.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_UNUSED_RAII_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSED_RAII_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { + +/// \brief Finds temporaries that look like RAII objects. +/// +/// The canonical example for this is a scoped lock. +/// \code +/// { +/// scoped_lock(&global_mutex); +/// critical_section(); +/// } +/// \endcode +/// The destructor of the scoped_lock is called before the critical_section is +/// entered, leaving it unprotected. +/// +/// We apply a number of heuristics to reduce the false positive count of this +/// check: +/// - Ignore code expanded from macros. Testing frameworks make heavy use of +/// this. +/// - Ignore types with no user-declared constructor. Those are very unlikely +/// to be RAII objects. +/// - Ignore objects returned from a call. +class UnusedRAIICheck : public ClangTidyCheck { +public: + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_UNUSED_RAII_H Index: clang-tidy/misc/UnusedRAII.cpp =================================================================== --- /dev/null +++ clang-tidy/misc/UnusedRAII.cpp @@ -0,0 +1,56 @@ +//===--- UnusedRAII.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 "UnusedRAII.h" +#include "clang/AST/ASTContext.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/SmallPtrSet.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace ast_matchers { +AST_MATCHER(CXXRecordDecl, hasUserDeclaredDestructor) { + return Node.hasUserDeclaredDestructor(); +} +} // namespace ast_matchers + +namespace tidy { + +void UnusedRAIICheck::registerMatchers(MatchFinder *Finder) { + // Look for temporaries that are constructed in-place and immediately + // destroyed. Look for temporaries created by a functional cast but not for + // those returned from a call. + auto BindTemp = bindTemporaryExpr(unless(has(callExpr()))); + Finder->addMatcher( + exprWithCleanups( + unless(hasAncestor(decl( + anyOf(recordDecl(ast_matchers::isTemplateInstantiation()), + functionDecl(ast_matchers::isTemplateInstantiation()))))), + hasParent(compoundStmt()), + hasType(recordDecl(hasUserDeclaredDestructor())), + anyOf(has(BindTemp), has(functionalCastExpr(has(BindTemp))))) + .bind("expr"), + this); +} + +void UnusedRAIICheck::check(const MatchFinder::MatchResult &Result) { + const auto *E = Result.Nodes.getStmtAs("expr"); + + // We ignore code expanded from macros to reduce the number of false + // positives. + if (E->getLocStart().isMacroID()) + return; + + diag(E->getLocStart(), "object destroyed immediately after creation; did you " + "mean to name the object?"); +} + +} // namespace tidy +} // namespace clang Index: test/clang-tidy/misc-unused-raii.cpp =================================================================== --- /dev/null +++ test/clang-tidy/misc-unused-raii.cpp @@ -0,0 +1,41 @@ +// RUN: clang-tidy %s -checks='-*,misc-unused-raii' -header-filter='.*' -- | FileCheck %s -implicit-check-not="{{warning|error}}:" + +struct Foo { + Foo(); + Foo(int); + ~Foo(); +}; + +struct Bar { + Bar(); + Foo f; +}; + +template +void qux() { + T(42); +} + +template +struct TFoo { + TFoo(T); + ~TFoo(); +}; + +Foo f(); + +void test() { + Foo(42); +// CHECK: :[[@LINE-1]]:3: warning: object destroyed immediately after creation; did you mean to name the object? + Foo(); +// CHECK: :[[@LINE-1]]:3: warning: object destroyed immediately after creation; did you mean to name the object? + TFoo(23); +// CHECK: :[[@LINE-1]]:3: warning: object destroyed immediately after creation; did you mean to name the object? + + Bar(); + f(); + qux(); + +#define M Foo(); + M +}