Index: clang-tools-extra/trunk/clang-tidy/bugprone/BugproneTidyModule.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/bugprone/BugproneTidyModule.cpp +++ clang-tools-extra/trunk/clang-tidy/bugprone/BugproneTidyModule.cpp @@ -25,6 +25,7 @@ #include "MultipleStatementMacroCheck.h" #include "StringConstructorCheck.h" #include "SuspiciousMemsetUsageCheck.h" +#include "ThrowKeywordMissingCheck.h" #include "UndefinedMemoryManipulationCheck.h" #include "UseAfterMoveCheck.h" #include "VirtualNearMissCheck.h" @@ -66,6 +67,8 @@ "bugprone-string-constructor"); CheckFactories.registerCheck( "bugprone-suspicious-memset-usage"); + CheckFactories.registerCheck( + "bugprone-throw-keyword-missing"); CheckFactories.registerCheck( "bugprone-undefined-memory-manipulation"); CheckFactories.registerCheck( Index: clang-tools-extra/trunk/clang-tidy/bugprone/CMakeLists.txt =================================================================== --- clang-tools-extra/trunk/clang-tidy/bugprone/CMakeLists.txt +++ clang-tools-extra/trunk/clang-tidy/bugprone/CMakeLists.txt @@ -17,6 +17,7 @@ MultipleStatementMacroCheck.cpp StringConstructorCheck.cpp SuspiciousMemsetUsageCheck.cpp + ThrowKeywordMissingCheck.cpp UndefinedMemoryManipulationCheck.cpp UseAfterMoveCheck.cpp VirtualNearMissCheck.cpp Index: clang-tools-extra/trunk/clang-tidy/bugprone/ThrowKeywordMissingCheck.h =================================================================== --- clang-tools-extra/trunk/clang-tidy/bugprone/ThrowKeywordMissingCheck.h +++ clang-tools-extra/trunk/clang-tidy/bugprone/ThrowKeywordMissingCheck.h @@ -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 Index: clang-tools-extra/trunk/clang-tidy/bugprone/ThrowKeywordMissingCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/bugprone/ThrowKeywordMissingCheck.cpp +++ clang-tools-extra/trunk/clang-tidy/bugprone/ThrowKeywordMissingCheck.cpp @@ -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("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 Index: clang-tools-extra/trunk/docs/ReleaseNotes.rst =================================================================== --- clang-tools-extra/trunk/docs/ReleaseNotes.rst +++ clang-tools-extra/trunk/docs/ReleaseNotes.rst @@ -94,6 +94,11 @@ `cppcoreguidelines-avoid-goto `_ added. +- New `bugprone-throw-keyword-missing + `_ check + + Diagnoses when a temporary object that appears to be an exception is constructed but not thrown. + Improvements to include-fixer ----------------------------- Index: clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-throw-keyword-missing.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-throw-keyword-missing.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/bugprone-throw-keyword-missing.rst @@ -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"); + } + } + + Index: clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst =================================================================== --- clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst +++ clang-tools-extra/trunk/docs/clang-tidy/checks/list.rst @@ -32,6 +32,7 @@ bugprone-multiple-statement-macro bugprone-string-constructor bugprone-suspicious-memset-usage + bugprone-throw-keyword-missing bugprone-undefined-memory-manipulation bugprone-use-after-move bugprone-virtual-near-miss Index: clang-tools-extra/trunk/test/clang-tidy/bugprone-throw-keyword-missing.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/bugprone-throw-keyword-missing.cpp +++ clang-tools-extra/trunk/test/clang-tidy/bugprone-throw-keyword-missing.cpp @@ -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 +class allocator {}; +template +class char_traits {}; +template , typename A = std::allocator> +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 string; +typedef basic_string 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 +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; +}