diff options
| author | Benjamin Kramer <benny.kra@googlemail.com> | 2014-07-22 12:30:35 +0000 |
|---|---|---|
| committer | Benjamin Kramer <benny.kra@googlemail.com> | 2014-07-22 12:30:35 +0000 |
| commit | 8ca8651171ae2caeb93c15a9ec6189cff8d2444a (patch) | |
| tree | 27527e7a36aba0a245f2c401f32da6dcdcfc7b27 /clang-tools-extra/clang-tidy/misc | |
| parent | 8f41c3eb74619a82b1c41bba3e09467d0a65db0c (diff) | |
| download | bcm5719-llvm-8ca8651171ae2caeb93c15a9ec6189cff8d2444a.tar.gz bcm5719-llvm-8ca8651171ae2caeb93c15a9ec6189cff8d2444a.zip | |
[clang-tidy] Add a check for RAII temporaries.
Summary:
This tries to find code similar that immediately destroys
an object that looks like it's trying to follow RAII.
{
scoped_lock(&global_mutex);
critical_section();
}
This checker will have false positives if someone uses this pattern
to legitimately invoke a destructor immediately (or the statement is
at the end of a scope anyway). To reduce the number we ignore this
pattern in macros (this is heavily used by gtest) and ignore objects
with no user-defined destructor.
Reviewers: alexfh, djasper
Subscribers: cfe-commits
Differential Revision: http://reviews.llvm.org/D4615
llvm-svn: 213647
Diffstat (limited to 'clang-tools-extra/clang-tidy/misc')
| -rw-r--r-- | clang-tools-extra/clang-tidy/misc/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp | 4 | ||||
| -rw-r--r-- | clang-tools-extra/clang-tidy/misc/UnusedRAII.cpp | 83 | ||||
| -rw-r--r-- | clang-tools-extra/clang-tidy/misc/UnusedRAII.h | 47 |
4 files changed, 135 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt index aee804956a5..5614d125fe7 100644 --- a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt @@ -6,6 +6,7 @@ add_clang_library(clangTidyMiscModule MiscTidyModule.cpp RedundantSmartptrGet.cpp SwappedArgumentsCheck.cpp + UnusedRAII.cpp UseOverride.cpp LINK_LIBS diff --git a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp index 669ad081112..e3dbdc3bcbe 100644 --- a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp +++ b/clang-tools-extra/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 @@ public: "misc-swapped-arguments", new ClangTidyCheckFactory<SwappedArgumentsCheck>()); CheckFactories.addCheckFactory( + "misc-unused-raii", + new ClangTidyCheckFactory<UnusedRAIICheck>()); + CheckFactories.addCheckFactory( "misc-use-override", new ClangTidyCheckFactory<UseOverride>()); } diff --git a/clang-tools-extra/clang-tidy/misc/UnusedRAII.cpp b/clang-tools-extra/clang-tidy/misc/UnusedRAII.cpp new file mode 100644 index 00000000000..7f06a49f5f8 --- /dev/null +++ b/clang-tools-extra/clang-tidy/misc/UnusedRAII.cpp @@ -0,0 +1,83 @@ +//===--- 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" + +using namespace clang::ast_matchers; + +namespace clang { +namespace ast_matchers { +AST_MATCHER(CXXRecordDecl, hasUserDeclaredDestructor) { + // TODO: If the dtor is there but empty we don't want to warn either. + 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()))).bind("temp"); + Finder->addMatcher( + exprWithCleanups( + unless(hasAncestor(decl( + anyOf(recordDecl(ast_matchers::isTemplateInstantiation()), + functionDecl(ast_matchers::isTemplateInstantiation()))))), + hasParent(compoundStmt().bind("compound")), + hasDescendant(typeLoc().bind("typeloc")), + 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>("expr"); + + // We ignore code expanded from macros to reduce the number of false + // positives. + if (E->getLocStart().isMacroID()) + return; + + // Don't emit a warning for the last statement in the surrounding compund + // statement. + const auto *CS = Result.Nodes.getStmtAs<CompoundStmt>("compound"); + if (E == CS->body_back()) + return; + + // Emit a warning. + auto D = diag(E->getLocStart(), "object destroyed immediately after " + "creation; did you mean to name the object?"); + const char *Replacement = " give_me_a_name"; + + // If this is a default ctor we have to remove the parens or we'll introduce a + // most vexing parse. + const auto *BTE = Result.Nodes.getStmtAs<CXXBindTemporaryExpr>("temp"); + if (const auto *TOE = dyn_cast<CXXTemporaryObjectExpr>(BTE->getSubExpr())) + if (TOE->getNumArgs() == 0) { + D << FixItHint::CreateReplacement( + CharSourceRange::getTokenRange(TOE->getParenOrBraceRange()), + Replacement); + return; + } + + // Otherwise just suggest adding a name. + const auto *TL = Result.Nodes.getNodeAs<TypeLoc>("typeloc"); + D << FixItHint::CreateInsertion( + Lexer::getLocForEndOfToken(TL->getLocEnd(), 0, *Result.SourceManager, + Result.Context->getLangOpts()), + Replacement); +} + +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/clang-tidy/misc/UnusedRAII.h b/clang-tools-extra/clang-tidy/misc/UnusedRAII.h new file mode 100644 index 00000000000..6850ab9a231 --- /dev/null +++ b/clang-tools-extra/clang-tidy/misc/UnusedRAII.h @@ -0,0 +1,47 @@ +//===--- 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 at the end of a compound statement (doesn't change behavior). +/// - 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 |

