-
Notifications
You must be signed in to change notification settings - Fork 12.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[clang-tidy] New checker for exceptions that are created but not thrown
Patch by: Kristof Umann Differential Revision: https://reviews.llvm.org/D43120 llvm-svn: 325222
- Loading branch information
Showing
8 changed files
with
285 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
clang-tools-extra/clang-tidy/bugprone/ThrowKeywordMissingCheck.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| //===--- ThrowKeywordMissingCheck.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 "ThrowKeywordMissingCheck.h" | ||
| #include "clang/AST/ASTContext.h" | ||
| #include "clang/ASTMatchers/ASTMatchFinder.h" | ||
|
|
||
| using namespace clang::ast_matchers; | ||
|
|
||
| namespace clang { | ||
| namespace tidy { | ||
| namespace bugprone { | ||
|
|
||
| void ThrowKeywordMissingCheck::registerMatchers(MatchFinder *Finder) { | ||
| if (!getLangOpts().CPlusPlus) | ||
| return; | ||
|
|
||
| auto CtorInitializerList = | ||
| cxxConstructorDecl(hasAnyConstructorInitializer(anything())); | ||
|
|
||
| Finder->addMatcher( | ||
| expr(anyOf(cxxFunctionalCastExpr(), cxxBindTemporaryExpr(), | ||
| cxxTemporaryObjectExpr()), | ||
| hasType(cxxRecordDecl( | ||
| isSameOrDerivedFrom(matchesName("[Ee]xception|EXCEPTION")))), | ||
| unless(anyOf(hasAncestor(stmt( | ||
| anyOf(cxxThrowExpr(), callExpr(), returnStmt()))), | ||
| hasAncestor(varDecl()), | ||
| allOf(hasAncestor(CtorInitializerList), | ||
| unless(hasAncestor(cxxCatchStmt())))))) | ||
| .bind("temporary-exception-not-thrown"), | ||
| this); | ||
| } | ||
|
|
||
| void ThrowKeywordMissingCheck::check(const MatchFinder::MatchResult &Result) { | ||
| const auto *TemporaryExpr = | ||
| Result.Nodes.getNodeAs<Expr>("temporary-exception-not-thrown"); | ||
|
|
||
| diag(TemporaryExpr->getLocStart(), "suspicious exception object created but " | ||
| "not thrown; did you mean 'throw %0'?") | ||
| << TemporaryExpr->getType().getBaseTypeIdentifier()->getName(); | ||
| } | ||
|
|
||
| } // namespace bugprone | ||
| } // namespace tidy | ||
| } // namespace clang |
36 changes: 36 additions & 0 deletions
36
clang-tools-extra/clang-tidy/bugprone/ThrowKeywordMissingCheck.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| //===--- ThrowKeywordMissingCheck.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_BUGPRONE_THROWKEYWORDMISSINGCHECK_H | ||
| #define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_THROWKEYWORDMISSINGCHECK_H | ||
|
|
||
| #include "../ClangTidy.h" | ||
|
|
||
| namespace clang { | ||
| namespace tidy { | ||
| namespace bugprone { | ||
|
|
||
| /// Emits a warning about temporary objects whose type is (or is derived from) a | ||
| /// class that has 'EXCEPTION', 'Exception' or 'exception' in its name. | ||
| /// | ||
| /// For the user-facing documentation see: | ||
| /// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-throw-keyword-missing.html | ||
| class ThrowKeywordMissingCheck : public ClangTidyCheck { | ||
| public: | ||
| ThrowKeywordMissingCheck(StringRef Name, ClangTidyContext *Context) | ||
| : ClangTidyCheck(Name, Context) {} | ||
| void registerMatchers(ast_matchers::MatchFinder *Finder) override; | ||
| void check(const ast_matchers::MatchFinder::MatchResult &Result) override; | ||
| }; | ||
|
|
||
| } // namespace bugprone | ||
| } // namespace tidy | ||
| } // namespace clang | ||
|
|
||
| #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_THROWKEYWORDMISSINGCHECK_H |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 20 additions & 0 deletions
20
clang-tools-extra/docs/clang-tidy/checks/bugprone-throw-keyword-missing.rst
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| .. title:: clang-tidy - bugprone-throw-keyword-missing | ||
|
|
||
| bugprone-throw-keyword-missing | ||
| ============================== | ||
|
|
||
| Warns about a potentially missing ``throw`` keyword. If a temporary object is created, but the | ||
| object's type derives from (or is the same as) a class that has 'EXCEPTION', 'Exception' or | ||
| 'exception' in its name, we can assume that the programmer's intention was to throw that object. | ||
|
|
||
| Example: | ||
|
|
||
| .. code-block:: c++ | ||
| void f(int i) { | ||
| if (i < 0) { | ||
| // Exception is created but is not thrown. | ||
| std::runtime_error("Unexpected argument"); | ||
| } | ||
| } | ||
|
|
||
|
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
167 changes: 167 additions & 0 deletions
167
clang-tools-extra/test/clang-tidy/bugprone-throw-keyword-missing.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,167 @@ | ||
| // RUN: %check_clang_tidy %s bugprone-throw-keyword-missing %t | ||
|
|
||
| namespace std { | ||
|
|
||
| // std::string declaration (taken from test/clang-tidy/readability-redundant-string-cstr-msvc.cpp). | ||
| template <typename T> | ||
| class allocator {}; | ||
| template <typename T> | ||
| class char_traits {}; | ||
| template <typename C, typename T = std::char_traits<C>, typename A = std::allocator<C>> | ||
| struct basic_string { | ||
| basic_string(); | ||
| basic_string(const basic_string &); | ||
| // MSVC headers define two constructors instead of using optional arguments. | ||
| basic_string(const C *); | ||
| basic_string(const C *, const A &); | ||
| ~basic_string(); | ||
| }; | ||
| typedef basic_string<char> string; | ||
| typedef basic_string<wchar_t> wstring; | ||
|
|
||
| // std::exception and std::runtime_error declaration. | ||
| struct exception { | ||
| exception(); | ||
| exception(const exception &other); | ||
| virtual ~exception(); | ||
| }; | ||
|
|
||
| struct runtime_error : public exception { | ||
| explicit runtime_error(const std::string &what_arg); | ||
| }; | ||
|
|
||
| } // namespace std | ||
|
|
||
| // The usage of this class should never emit a warning. | ||
| struct RegularClass {}; | ||
|
|
||
| // Class name contains the substring "exception", in certain cases using this class should emit a warning. | ||
| struct RegularException { | ||
| RegularException() {} | ||
|
|
||
| // Constructors with a single argument are treated differently (cxxFunctionalCastExpr). | ||
| RegularException(int) {} | ||
| }; | ||
|
|
||
| // -------------- | ||
|
|
||
| void stdExceptionNotTrownTest(int i) { | ||
| if (i < 0) | ||
| // CHECK-MESSAGES: :[[@LINE+1]]:5: warning: suspicious exception object created but not thrown; did you mean 'throw {{.*}}'? [bugprone-throw-keyword-missing] | ||
| std::exception(); | ||
|
|
||
| if (i > 0) | ||
| // CHECK-MESSAGES: :[[@LINE+1]]:5: warning: suspicious exception | ||
| std::runtime_error("Unexpected argument"); | ||
| } | ||
|
|
||
| void stdExceptionThrownTest(int i) { | ||
| if (i < 0) | ||
| throw std::exception(); | ||
|
|
||
| if (i > 0) | ||
| throw std::runtime_error("Unexpected argument"); | ||
| } | ||
|
|
||
| void regularClassNotThrownTest(int i) { | ||
| if (i < 0) | ||
| RegularClass(); | ||
| } | ||
|
|
||
| void regularClassThrownTest(int i) { | ||
| if (i < 0) | ||
| throw RegularClass(); | ||
| } | ||
|
|
||
| void nameContainsExceptionNotThrownTest(int i) { | ||
| if (i < 0) | ||
| // CHECK-MESSAGES: :[[@LINE+1]]:5: warning: suspicious exception | ||
| RegularException(); | ||
|
|
||
| if (i > 0) | ||
| // CHECK-MESSAGES: :[[@LINE+1]]:5: warning: suspicious exception | ||
| RegularException(5); | ||
| } | ||
|
|
||
| void nameContainsExceptionThrownTest(int i) { | ||
| if (i < 0) | ||
| throw RegularException(); | ||
|
|
||
| if (i > 0) | ||
| throw RegularException(5); | ||
| } | ||
|
|
||
| template <class Exception> | ||
| void f(int i, Exception excToBeThrown) {} | ||
|
|
||
| void funcCallWithTempExcTest() { | ||
| f(5, RegularException()); | ||
| } | ||
|
|
||
| // Global variable initilization test. | ||
| RegularException exc = RegularException(); | ||
| RegularException *excptr = new RegularException(); | ||
|
|
||
| void localVariableInitTest() { | ||
| RegularException exc = RegularException(); | ||
| RegularException *excptr = new RegularException(); | ||
| } | ||
|
|
||
| class CtorInitializerListTest { | ||
| RegularException exc; | ||
|
|
||
| CtorInitializerListTest() : exc(RegularException()) {} | ||
|
|
||
| CtorInitializerListTest(int) try : exc(RegularException()) { | ||
| // Constructor body | ||
| } catch (...) { | ||
| // CHECK-MESSAGES: :[[@LINE+1]]:5: warning: suspicious exception | ||
| RegularException(); | ||
| } | ||
|
|
||
| CtorInitializerListTest(float); | ||
| }; | ||
|
|
||
| CtorInitializerListTest::CtorInitializerListTest(float) try : exc(RegularException()) { | ||
| // Constructor body | ||
| } catch (...) { | ||
| // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: suspicious exception | ||
| RegularException(); | ||
| } | ||
|
|
||
| RegularException funcReturningExceptionTest(int i) { | ||
| return RegularException(); | ||
| } | ||
|
|
||
| void returnedValueTest() { | ||
| funcReturningExceptionTest(3); | ||
| } | ||
|
|
||
| struct ClassBracedInitListTest { | ||
| ClassBracedInitListTest(RegularException exc) {} | ||
| }; | ||
|
|
||
| void foo(RegularException, ClassBracedInitListTest) {} | ||
|
|
||
| void bracedInitListTest() { | ||
| RegularException exc{}; | ||
| ClassBracedInitListTest test = {RegularException()}; | ||
| foo({}, {RegularException()}); | ||
| } | ||
|
|
||
| typedef std::exception ERROR_BASE; | ||
| class RegularError : public ERROR_BASE {}; | ||
|
|
||
| void typedefTest() { | ||
| // CHECK-MESSAGES: :[[@LINE+1]]:3: warning: suspicious exception | ||
| RegularError(); | ||
| } | ||
|
|
||
| struct ExceptionRAII { | ||
| ExceptionRAII() {} | ||
| ~ExceptionRAII() {} | ||
| }; | ||
|
|
||
| void exceptionRAIITest() { | ||
| ExceptionRAII E; | ||
| } |