10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/OperationKinds.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
20 ThrowByValueCatchByReferenceCheck::ThrowByValueCatchByReferenceCheck(
23 CheckAnonymousTemporaries(Options.get(
"CheckThrowTemporaries", true)),
24 WarnOnLargeObject(Options.get(
"WarnOnLargeObject", false)),
27 Options.get(
"MaxSize", std::numeric_limits<uint64_t>::max())),
28 MaxSize(MaxSizeOptions) {}
31 Finder->addMatcher(cxxThrowExpr().bind(
"throw"),
this);
32 Finder->addMatcher(cxxCatchStmt().bind(
"catch"),
this);
38 Options.
store(Opts,
"WarnOnLargeObjects", WarnOnLargeObject);
43 const MatchFinder::MatchResult &Result) {
44 diagnoseThrowLocations(Result.Nodes.getNodeAs<CXXThrowExpr>(
"throw"));
45 diagnoseCatchLocations(Result.Nodes.getNodeAs<CXXCatchStmt>(
"catch"),
49 bool ThrowByValueCatchByReferenceCheck::isFunctionParameter(
50 const DeclRefExpr *declRefExpr) {
51 return isa<ParmVarDecl>(declRefExpr->getDecl());
54 bool ThrowByValueCatchByReferenceCheck::isCatchVariable(
55 const DeclRefExpr *declRefExpr) {
56 auto *valueDecl = declRefExpr->getDecl();
57 if (
auto *varDecl = dyn_cast<VarDecl>(valueDecl))
58 return varDecl->isExceptionVariable();
62 bool ThrowByValueCatchByReferenceCheck::isFunctionOrCatchVar(
63 const DeclRefExpr *declRefExpr) {
64 return isFunctionParameter(declRefExpr) || isCatchVariable(declRefExpr);
67 void ThrowByValueCatchByReferenceCheck::diagnoseThrowLocations(
68 const CXXThrowExpr *throwExpr) {
71 auto *subExpr = throwExpr->getSubExpr();
74 auto qualType = subExpr->getType();
75 if (qualType->isPointerType()) {
78 auto *inner = subExpr->IgnoreParenImpCasts();
79 if (isa<StringLiteral>(inner))
82 auto *declRef = dyn_cast<DeclRefExpr>(inner);
83 if (declRef && isCatchVariable(declRef)) {
86 diag(subExpr->getBeginLoc(),
"throw expression throws a pointer; it should "
87 "throw a non-pointer value instead");
102 if (CheckAnonymousTemporaries) {
104 auto *currentSubExpr = subExpr->IgnoreImpCasts();
105 const auto *variableReference = dyn_cast<DeclRefExpr>(currentSubExpr);
106 const auto *constructorCall = dyn_cast<CXXConstructExpr>(currentSubExpr);
110 if (variableReference)
111 emit = !isFunctionOrCatchVar(variableReference);
112 else if (constructorCall &&
113 constructorCall->getConstructor()->isCopyOrMoveConstructor()) {
120 auto *currentSubExpr = (*argIter)->IgnoreImpCasts();
121 if (currentSubExpr->isLValue()) {
122 if (
auto *tmp = dyn_cast<DeclRefExpr>(currentSubExpr))
123 emit = !isFunctionOrCatchVar(tmp);
124 else if (isa<CallExpr>(currentSubExpr))
129 diag(subExpr->getBeginLoc(),
130 "throw expression should throw anonymous temporary values instead");
134 void ThrowByValueCatchByReferenceCheck::diagnoseCatchLocations(
135 const CXXCatchStmt *catchStmt, ASTContext &context) {
138 auto caughtType = catchStmt->getCaughtType();
139 if (caughtType.isNull())
141 auto *varDecl = catchStmt->getExceptionDecl();
142 if (
const auto *PT = caughtType.getCanonicalType()->getAs<PointerType>()) {
143 const char *diagMsgCatchReference =
"catch handler catches a pointer value; "
144 "should throw a non-pointer value and "
145 "catch by reference instead";
148 if (!PT->getPointeeType()->isAnyCharacterType())
149 diag(varDecl->getBeginLoc(), diagMsgCatchReference);
150 }
else if (!caughtType->isReferenceType()) {
151 const char *diagMsgCatchReference =
"catch handler catches by value; "
152 "should catch by reference instead";
156 if (!caughtType.isTrivialType(context)) {
157 diag(varDecl->getBeginLoc(), diagMsgCatchReference);
158 }
else if (WarnOnLargeObject) {
164 if (MaxSize == std::numeric_limits<uint64_t>::max())
165 MaxSize = context.getTypeSize(context.getSizeType());
166 if (context.getTypeSize(caughtType) > MaxSize)
167 diag(varDecl->getBeginLoc(), diagMsgCatchReference);