Index: clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tidy/misc/CMakeLists.txt +++ clang-tidy/misc/CMakeLists.txt @@ -4,6 +4,7 @@ ForwardingReferenceOverloadCheck.cpp LambdaFunctionNameCheck.cpp MisplacedConstCheck.cpp + ThrowKeywordMissingCheck.cpp UnconventionalAssignOperatorCheck.cpp DefinitionsInHeadersCheck.cpp IncorrectRoundings.cpp Index: clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tidy/misc/MiscTidyModule.cpp +++ clang-tidy/misc/MiscTidyModule.cpp @@ -33,6 +33,7 @@ #include "SuspiciousStringCompareCheck.h" #include "SwappedArgumentsCheck.h" #include "ThrowByValueCatchByReferenceCheck.h" +#include "ThrowKeywordMissingCheck.h" #include "UnconventionalAssignOperatorCheck.h" #include "UndelegatedConstructor.h" #include "UniqueptrResetReleaseCheck.h" @@ -53,6 +54,8 @@ CheckFactories.registerCheck( "misc-lambda-function-name"); CheckFactories.registerCheck("misc-misplaced-const"); + CheckFactories.registerCheck( + "misc-throw-keyword-missing"); CheckFactories.registerCheck( "misc-unconventional-assign-operator"); CheckFactories.registerCheck( Index: clang-tidy/misc/ThrowKeywordMissingCheck.h =================================================================== --- /dev/null +++ clang-tidy/misc/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_MISC_THROWKEYWORDMISSINGCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_THROWKEYWORDMISSINGCHECK_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace misc { + +/// 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/misc-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 misc +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_THROWKEYWORDMISSINGCHECK_H Index: clang-tidy/misc/ThrowKeywordMissingCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/misc/ThrowKeywordMissingCheck.cpp @@ -0,0 +1,51 @@ +//===--- 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 misc { + +void ThrowKeywordMissingCheck::registerMatchers(MatchFinder *Finder) { + // This is a C++ only checker. + 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(cxxConstructorDecl( + hasAnyConstructorInitializer(anything()))), + 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(), "exception created but is not thrown"); +} + +} // namespace misc +} // namespace tidy +} // namespace clang Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -64,6 +64,11 @@ object is statically initialized with a ``constexpr`` constructor or has no explicit constructor. +- New `misc-throw-keyword-missing + `_ check + + Warns if a ``throw`` keyword is potentialy missing before a temporary exception object. + Improvements to include-fixer ----------------------------- Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -143,6 +143,7 @@ misc-suspicious-string-compare misc-swapped-arguments misc-throw-by-value-catch-by-reference + misc-throw-keyword-missing misc-unconventional-assign-operator misc-undelegated-constructor misc-uniqueptr-reset-release Index: docs/clang-tidy/checks/misc-throw-keyword-missing.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/misc-throw-keyword-missing.rst @@ -0,0 +1,20 @@ +.. title:: clang-tidy - misc-throw-keyword-missing + +misc-throw-keyword-missing +========================== + +This check warns about the potentially missing `throw` keyword. If a temporary object is created, +but the object's type derives from (or 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: test/clang-tidy/misc-throw-keyword-missing.cpp =================================================================== --- /dev/null +++ test/clang-tidy/misc-throw-keyword-missing.cpp @@ -0,0 +1,152 @@ +// RUN: %check_clang_tidy %s misc-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 {}; + +// Classname 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 stdExceptionNotTrown(int i) { + if (i < 0) + //CHECK-MESSAGES: :[[@LINE+1]]:5: warning: exception created but is not thrown [misc-throw-keyword-missing] + std::exception(); + + if (i > 0) + //CHECK-MESSAGES: :[[@LINE+1]]:5: warning: exception created but is not thrown [misc-throw-keyword-missing] + std::runtime_error("Unexpected argument"); + +} + +void stdExceptionThrown(int i) { + if (i < 0) + throw std::exception(); + + if (i > 0) + throw std::runtime_error("Unexpected argument"); +} + +void regularClassNotThrown(int i) { + if (i < 0) + RegularClass(); +} + +void regularClassThrown(int i) { + if (i < 0) + throw RegularClass(); +} + +void nameContainsExceptionNotThrown(int i) { + if (i < 0) + //CHECK-MESSAGES: :[[@LINE+1]]:5: warning: exception created but is not thrown [misc-throw-keyword-missing] + RegularException(); + + if (i > 0) + //CHECK-MESSAGES: :[[@LINE+1]]:5: warning: exception created but is not thrown [misc-throw-keyword-missing] + RegularException(5); +} + +void nameContainsExceptionThrown(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 + } + catch(...){ + //CHECK-MESSAGES: :[[@LINE+1]]:7: warning: exception created but is not thrown [misc-throw-keyword-missing] + RegularException(); + } + + CtorInitializerListTest(float); +}; + +CtorInitializerListTest::CtorInitializerListTest(float) + try : exc(RegularException()) { + //Constructor + } + catch(...){ + //CHECK-MESSAGES: :[[@LINE+1]]:5: warning: exception created but is not thrown [misc-throw-keyword-missing] + RegularException(); + } + +RegularException funcReturningExceptioniTest(int i){ + return RegularException(); +} + +struct ClassBracedInitListTest{ + ClassBracedInitListTest(RegularException exc) {} +}; + +void functionBracedInitListTest(RegularException, ClassBracedInitListTest) {} + +void bracedInitListTest(){ + RegularException exc{}; + ClassBracedInitListTest test = {RegularException()}; + functionBracedInitListTest({}, {RegularException()}); +}