Index: clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tidy/misc/CMakeLists.txt +++ clang-tidy/misc/CMakeLists.txt @@ -13,6 +13,7 @@ NoexceptMoveConstructorCheck.cpp StaticAssertCheck.cpp SwappedArgumentsCheck.cpp + ThrowByValueCatchByReferenceCheck.cpp UndelegatedConstructor.cpp UnusedRAIICheck.cpp UniqueptrResetReleaseCheck.cpp Index: clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tidy/misc/MiscTidyModule.cpp +++ clang-tidy/misc/MiscTidyModule.cpp @@ -21,6 +21,7 @@ #include "NoexceptMoveConstructorCheck.h" #include "StaticAssertCheck.h" #include "SwappedArgumentsCheck.h" +#include "ThrowByValueCatchByReferenceCheck.h" #include "UndelegatedConstructor.h" #include "UniqueptrResetReleaseCheck.h" #include "UnusedRAIICheck.h" @@ -54,6 +55,8 @@ "misc-static-assert"); CheckFactories.registerCheck( "misc-swapped-arguments"); + CheckFactories.registerCheck( + "misc-throw-by-value-catch-by-reference"); CheckFactories.registerCheck( "misc-undelegated-constructor"); CheckFactories.registerCheck( Index: clang-tidy/misc/ThrowByValueCatchByReferenceCheck.h =================================================================== --- /dev/null +++ clang-tidy/misc/ThrowByValueCatchByReferenceCheck.h @@ -0,0 +1,32 @@ +//===--- ThrowByValueCatchByReferenceCheck.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_THROW_BY_VALUE_CATCH_BY_REFERENCE_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_THROW_BY_VALUE_CATCH_BY_REFERENCE_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { + +///\brief checks for locations that do not throw by value +// or catch by reference. +class ThrowByValueCatchByReferenceCheck : public ClangTidyCheck { +public: + ThrowByValueCatchByReferenceCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + 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_THROW_BY_VALUE_CATCH_BY_REFERENCE_H + Index: clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/misc/ThrowByValueCatchByReferenceCheck.cpp @@ -0,0 +1,57 @@ +//===--- ThrowByValueCatchByReferenceCheck.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 "ThrowByValueCatchByReferenceCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { + +void ThrowByValueCatchByReferenceCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(throwExpr().bind("throw"), this); + Finder->addMatcher(catchStmt().bind("catch"), this); +} + +void ThrowByValueCatchByReferenceCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *throwExpr = Result.Nodes.getNodeAs("throw"); + if (throwExpr != nullptr) { + auto *subExpr = throwExpr->getSubExpr(); + auto qualType = subExpr->getType(); + auto *type = qualType.getTypePtr(); + if (type->isPointerType() == true) { // throwing a pointer + diag(subExpr->getLocStart(), "avoid throwing pointer"); + } + } + const auto *catchStmt = Result.Nodes.getNodeAs("catch"); + if (catchStmt != nullptr) { + auto caughtType = catchStmt->getCaughtType(); + auto *type = caughtType.getTypePtr(); + auto *varDecl = catchStmt->getExceptionDecl(); + if (type->isPointerType()) { + diag(varDecl->getLocStart(), + "catch by const reference"); + } else if (!type->isReferenceType()) { + diag(varDecl->getLocStart(), "catch by const reference"); + } else { + auto* reference = type->castAs(); + assert(reference!=nullptr); + if (reference->getPointeeType().isConstQualified() == false) { + diag(varDecl->getLocStart(), + "catch by const reference to avoid unnecessary copies"); + } + } + } +} + +} // namespace tidy +} // namespace clang Index: test/clang-tidy/misc-throw-by-value-catch-by-reference.cpp =================================================================== --- /dev/null +++ test/clang-tidy/misc-throw-by-value-catch-by-reference.cpp @@ -0,0 +1,71 @@ +// RUN: $(dirname %s)/check_clang_tidy.sh %s misc-throw-by-value-catch-by-reference %t +// REQUIRES: shell + +class logic_error { +public: + logic_error(const char *message) {} +}; + +int selector; +typedef logic_error *logic_ptr; +typedef logic_ptr logic_double_typedef; + +void testThrowFunc() { + switch (selector) { + case 0: + throw new logic_error("by pointer"); + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: avoid throwing pointer + // [misc-throw-by-value-catch-by-reference] + case 1: { + logic_ptr tmp = new logic_error("by pointer"); + throw tmp; + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: avoid throwing pointer + // [misc-throw-by-value-catch-by-reference] + } + case 2: + throw logic_error("by value"); + } +} + +void catchByPointer() { + try { + testThrowFunc(); + } catch (logic_error *e) { + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: catch by const reference + // [misc-throw-by-value-catch-by-reference] + } +} + +void catchByValue() { + try { + testThrowFunc(); + } catch (logic_error e) { + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: catch by const reference + // [misc-throw-by-value-catch-by-reference] + } +} + +void catchByReference() { + try { + testThrowFunc(); + } catch (logic_error &e) { + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: catch by const reference to + // avoid unnecessary copies [misc-throw-by-value-catch-by-reference] + } +} + +void catchByConstReference() { + try { + testThrowFunc(); + } catch (const logic_error &e) { + } +} + +void catchTypedef() { + try { + testThrowFunc(); + } catch (logic_ptr e) { + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: catch by const reference + // [misc-throw-by-value-catch-by-reference] + } +}