10#include "clang/AST/ASTContext.h"
11#include "clang/ASTMatchers/ASTMatchFinder.h"
20 CheckAnonymousTemporaries(Options.get(
"CheckThrowTemporaries", true)),
21 WarnOnLargeObject(Options.get(
"WarnOnLargeObject", false)),
24 Options.get(
"MaxSize", std::numeric_limits<uint64_t>::max())),
25 MaxSize(MaxSizeOptions) {}
28 Finder->addMatcher(cxxThrowExpr().bind(
"throw"),
this);
29 Finder->addMatcher(cxxCatchStmt().bind(
"catch"),
this);
34 Options.store(Opts,
"CheckThrowTemporaries", CheckAnonymousTemporaries);
35 Options.store(Opts,
"WarnOnLargeObject", WarnOnLargeObject);
36 Options.store(Opts,
"MaxSize", MaxSizeOptions);
40 const MatchFinder::MatchResult &Result) {
41 diagnoseThrowLocations(Result.Nodes.getNodeAs<CXXThrowExpr>(
"throw"));
42 diagnoseCatchLocations(Result.Nodes.getNodeAs<CXXCatchStmt>(
"catch"),
46bool ThrowByValueCatchByReferenceCheck::isFunctionParameter(
47 const DeclRefExpr *DeclRefExpr) {
48 return isa<ParmVarDecl>(DeclRefExpr->getDecl());
51bool ThrowByValueCatchByReferenceCheck::isCatchVariable(
52 const DeclRefExpr *DeclRefExpr) {
53 auto *ValueDecl = DeclRefExpr->getDecl();
54 if (
auto *Var = dyn_cast<VarDecl>(ValueDecl))
55 return Var->isExceptionVariable();
59bool ThrowByValueCatchByReferenceCheck::isFunctionOrCatchVar(
60 const DeclRefExpr *DeclRefExpr) {
61 return isFunctionParameter(DeclRefExpr) || isCatchVariable(DeclRefExpr);
64void ThrowByValueCatchByReferenceCheck::diagnoseThrowLocations(
65 const CXXThrowExpr *ThrowExpr) {
68 auto *SubExpr = ThrowExpr->getSubExpr();
71 auto QualType = SubExpr->getType();
72 if (QualType->isPointerType()) {
75 auto *Inner = SubExpr->IgnoreParenImpCasts();
76 if (isa<StringLiteral>(Inner))
79 auto *DeclRef = dyn_cast<DeclRefExpr>(Inner);
80 if (DeclRef && isCatchVariable(DeclRef))
82 diag(SubExpr->getBeginLoc(),
"throw expression throws a pointer; it should "
83 "throw a non-pointer value instead");
96 if (CheckAnonymousTemporaries) {
98 auto *CurrentSubExpr = SubExpr->IgnoreImpCasts();
99 const auto *VariableReference = dyn_cast<DeclRefExpr>(CurrentSubExpr);
100 const auto *ConstructorCall = dyn_cast<CXXConstructExpr>(CurrentSubExpr);
104 if (VariableReference) {
105 Emit = !isFunctionOrCatchVar(VariableReference);
106 }
else if (ConstructorCall &&
107 ConstructorCall->getConstructor()->isCopyOrMoveConstructor()) {
114 auto *CurrentSubExpr = (*ArgIter)->IgnoreImpCasts();
115 if (CurrentSubExpr->isLValue()) {
116 if (
auto *Tmp = dyn_cast<DeclRefExpr>(CurrentSubExpr))
117 Emit = !isFunctionOrCatchVar(Tmp);
118 else if (isa<CallExpr>(CurrentSubExpr))
123 diag(SubExpr->getBeginLoc(),
124 "throw expression should throw anonymous temporary values instead");
128void ThrowByValueCatchByReferenceCheck::diagnoseCatchLocations(
129 const CXXCatchStmt *CatchStmt, ASTContext &Context) {
132 auto CaughtType = CatchStmt->getCaughtType();
133 if (CaughtType.isNull())
135 auto *VarDecl = CatchStmt->getExceptionDecl();
136 if (
const auto *PT = CaughtType.getCanonicalType()->getAs<PointerType>()) {
137 const char *DiagMsgCatchReference =
138 "catch handler catches a pointer value; "
139 "should throw a non-pointer value and "
140 "catch by reference instead";
143 if (!PT->getPointeeType()->isAnyCharacterType())
144 diag(VarDecl->getBeginLoc(), DiagMsgCatchReference);
145 }
else if (!CaughtType->isReferenceType()) {
146 const char *DiagMsgCatchReference =
"catch handler catches by value; "
147 "should catch by reference instead";
151 if (!CaughtType.isTrivialType(Context)) {
152 diag(VarDecl->getBeginLoc(), DiagMsgCatchReference);
153 }
else if (WarnOnLargeObject) {
159 if (MaxSize == std::numeric_limits<uint64_t>::max())
160 MaxSize = Context.getTypeSize(Context.getSizeType());
161 if (Context.getTypeSize(CaughtType) > MaxSize)
162 diag(VarDecl->getBeginLoc(), DiagMsgCatchReference);
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
ThrowByValueCatchByReferenceCheck(StringRef Name, ClangTidyContext *Context)
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
llvm::StringMap< ClangTidyValue > OptionMap