10#include "clang/AST/ASTContext.h"
11#include "clang/AST/OperationKinds.h"
12#include "clang/ASTMatchers/ASTMatchFinder.h"
21 CheckAnonymousTemporaries(Options.get(
"CheckThrowTemporaries", true)),
22 WarnOnLargeObject(Options.get(
"WarnOnLargeObject", false)),
25 Options.get(
"MaxSize", std::numeric_limits<uint64_t>::max())),
26 MaxSize(MaxSizeOptions) {}
29 Finder->addMatcher(cxxThrowExpr().bind(
"throw"),
this);
30 Finder->addMatcher(cxxCatchStmt().bind(
"catch"),
this);
36 Options.
store(Opts,
"WarnOnLargeObjects", WarnOnLargeObject);
41 const MatchFinder::MatchResult &Result) {
42 diagnoseThrowLocations(Result.Nodes.getNodeAs<CXXThrowExpr>(
"throw"));
43 diagnoseCatchLocations(Result.Nodes.getNodeAs<CXXCatchStmt>(
"catch"),
47bool ThrowByValueCatchByReferenceCheck::isFunctionParameter(
48 const DeclRefExpr *DeclRefExpr) {
49 return isa<ParmVarDecl>(DeclRefExpr->getDecl());
52bool ThrowByValueCatchByReferenceCheck::isCatchVariable(
53 const DeclRefExpr *DeclRefExpr) {
54 auto *ValueDecl = DeclRefExpr->getDecl();
55 if (
auto *VarDecl = dyn_cast<clang::VarDecl>(ValueDecl))
56 return VarDecl->isExceptionVariable();
60bool ThrowByValueCatchByReferenceCheck::isFunctionOrCatchVar(
61 const DeclRefExpr *DeclRefExpr) {
62 return isFunctionParameter(DeclRefExpr) || isCatchVariable(DeclRefExpr);
65void ThrowByValueCatchByReferenceCheck::diagnoseThrowLocations(
66 const CXXThrowExpr *ThrowExpr) {
69 auto *SubExpr = ThrowExpr->getSubExpr();
72 auto QualType = SubExpr->getType();
73 if (QualType->isPointerType()) {
76 auto *
Inner = SubExpr->IgnoreParenImpCasts();
77 if (isa<StringLiteral>(
Inner))
84 diag(SubExpr->getBeginLoc(),
"throw expression throws a pointer; it should "
85 "throw a non-pointer value instead");
100 if (CheckAnonymousTemporaries) {
102 auto *CurrentSubExpr = SubExpr->IgnoreImpCasts();
103 const auto *VariableReference = dyn_cast<DeclRefExpr>(CurrentSubExpr);
104 const auto *ConstructorCall = dyn_cast<CXXConstructExpr>(CurrentSubExpr);
108 if (VariableReference)
109 Emit = !isFunctionOrCatchVar(VariableReference);
110 else if (ConstructorCall &&
111 ConstructorCall->getConstructor()->isCopyOrMoveConstructor()) {
118 auto *CurrentSubExpr = (*ArgIter)->IgnoreImpCasts();
119 if (CurrentSubExpr->isLValue()) {
120 if (
auto *Tmp = dyn_cast<DeclRefExpr>(CurrentSubExpr))
121 Emit = !isFunctionOrCatchVar(Tmp);
122 else if (isa<CallExpr>(CurrentSubExpr))
127 diag(SubExpr->getBeginLoc(),
128 "throw expression should throw anonymous temporary values instead");
132void ThrowByValueCatchByReferenceCheck::diagnoseCatchLocations(
133 const CXXCatchStmt *CatchStmt, ASTContext &Context) {
136 auto CaughtType = CatchStmt->getCaughtType();
137 if (CaughtType.isNull())
139 auto *VarDecl = CatchStmt->getExceptionDecl();
140 if (
const auto *PT = CaughtType.getCanonicalType()->getAs<PointerType>()) {
141 const char *DiagMsgCatchReference =
142 "catch handler catches a pointer value; "
143 "should throw a non-pointer value and "
144 "catch by reference instead";
147 if (!PT->getPointeeType()->isAnyCharacterType())
148 diag(VarDecl->getBeginLoc(), DiagMsgCatchReference);
149 }
else if (!CaughtType->isReferenceType()) {
150 const char *DiagMsgCatchReference =
"catch handler catches by value; "
151 "should catch by reference instead";
155 if (!CaughtType.isTrivialType(Context)) {
156 diag(VarDecl->getBeginLoc(), DiagMsgCatchReference);
157 }
else if (WarnOnLargeObject) {
163 if (MaxSize == std::numeric_limits<uint64_t>::max())
164 MaxSize = Context.getTypeSize(Context.getSizeType());
165 if (Context.getTypeSize(CaughtType) > MaxSize)
166 diag(VarDecl->getBeginLoc(), DiagMsgCatchReference);
std::pair< Context, Canceler > Inner
llvm::SmallString< 256U > Name
const DeclRefExpr * DeclRef
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Base class for all clang-tidy checks.
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
ThrowByValueCatchByReferenceCheck(StringRef Name, ClangTidyContext *Context)
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
llvm::StringMap< ClangTidyValue > OptionMap