clang-tools 3.8.0

- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

ThrowByValueCatchByReferenceCheck.cpp
Go to the documentation of this file.
1 //===--- ThrowByValueCatchByReferenceCheck.cpp - clang-tidy----------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "ThrowByValueCatchByReferenceCheck.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 #include "clang/AST/OperationKinds.h"
14
15 using namespace clang::ast_matchers;
16
17 namespace clang {
18 namespace tidy {
19
20 ThrowByValueCatchByReferenceCheck::ThrowByValueCatchByReferenceCheck(
21 StringRef Name, ClangTidyContext *Context)
22 : ClangTidyCheck(Name, Context),
23 CheckAnonymousTemporaries(Options.get("CheckThrowTemporaries", true)) {}
24
25 void ThrowByValueCatchByReferenceCheck::registerMatchers(MatchFinder *Finder) {
26 // This is a C++ only check thus we register the matchers only for C++
27 if (!getLangOpts().CPlusPlus)
28 return;
29
30 Finder->addMatcher(cxxThrowExpr().bind("throw"), this);
31 Finder->addMatcher(cxxCatchStmt().bind("catch"), this);
32 }
33
34 void ThrowByValueCatchByReferenceCheck::storeOptions(
35 ClangTidyOptions::OptionMap &Opts) {
36 Options.store(Opts, "CheckThrowTemporaries", true);
37 }
38
39 void ThrowByValueCatchByReferenceCheck::check(
40 const MatchFinder::MatchResult &Result) {
41 diagnoseThrowLocations(Result.Nodes.getNodeAs<CXXThrowExpr>("throw"));
42 diagnoseCatchLocations(Result.Nodes.getNodeAs<CXXCatchStmt>("catch"),
43 *Result.Context);
44 }
45
46 bool ThrowByValueCatchByReferenceCheck::isFunctionParameter(
47 const DeclRefExpr *declRefExpr) {
48 return isa<ParmVarDecl>(declRefExpr->getDecl());
49 }
50
51 bool ThrowByValueCatchByReferenceCheck::isCatchVariable(
52 const DeclRefExpr *declRefExpr) {
53 auto *valueDecl = declRefExpr->getDecl();
54 if (auto *varDecl = dyn_cast<VarDecl>(valueDecl))
55 return varDecl->isExceptionVariable();
56 return false;
57 }
58
59 bool ThrowByValueCatchByReferenceCheck::isFunctionOrCatchVar(
60 const DeclRefExpr *declRefExpr) {
61 return isFunctionParameter(declRefExpr) || isCatchVariable(declRefExpr);
62 }
63
64 void ThrowByValueCatchByReferenceCheck::diagnoseThrowLocations(
65 const CXXThrowExpr *throwExpr) {
66 if (!throwExpr)
67 return;
68 auto *subExpr = throwExpr->getSubExpr();
69 if (!subExpr)
70 return;
71 auto qualType = subExpr->getType();
72 if (qualType->isPointerType()) {
73 // The code is throwing a pointer.
74 // In case it is strng literal, it is safe and we return.
75 auto *inner = subExpr->IgnoreParenImpCasts();
76 if (isa<StringLiteral>(inner))
77 return;
78 // If it's a variable from a catch statement, we return as well.
79 auto *declRef = dyn_cast<DeclRefExpr>(inner);
80 if (declRef && isCatchVariable(declRef)) {
81 return;
82 }
83 diag(subExpr->getLocStart(), "throw expression throws a pointer; it should "
84 "throw a non-pointer value instead");
85 }
86 // If the throw statement does not throw by pointer then it throws by value
87 // which is ok.
88 // There are addition checks that emit diagnosis messages if the thrown value
89 // is not an RValue. See:
90 // https://www.securecoding.cert.org/confluence/display/cplusplus/ERR09-CPP.+Throw+anonymous+temporaries
91 // This behavior can be influenced by an option.
92
93 // If we encounter a CXXThrowExpr, we move through all casts until you either
94 // encounter a DeclRefExpr or a CXXConstructExpr.
95 // If it's a DeclRefExpr, we emit a message if the referenced variable is not
96 // a catch variable or function parameter.
97 // When encountering a CopyOrMoveConstructor: emit message if after casts,
98 // the expression is a LValue
99 if (CheckAnonymousTemporaries) {
100 bool emit = false;
101 auto *currentSubExpr = subExpr->IgnoreImpCasts();
102 const DeclRefExpr *variableReference =
103 dyn_cast<DeclRefExpr>(currentSubExpr);
104 const CXXConstructExpr *constructorCall =
105 dyn_cast<CXXConstructExpr>(currentSubExpr);
106 // If we have a DeclRefExpr, we flag for emitting a diagnosis message in
107 // case the referenced variable is neither a function parameter nor a
108 // variable declared in the catch statement.
109 if (variableReference)
110 emit = !isFunctionOrCatchVar(variableReference);
111 else if (constructorCall &&
112 constructorCall->getConstructor()->isCopyOrMoveConstructor()) {
113 // If we have a copy / move construction, we emit a diagnosis message if
114 // the object that we copy construct from is neither a function parameter
115 // nor a variable declared in a catch statement
116 auto argIter =
117 constructorCall
118 ->arg_begin(); // there's only one for copy constructors
119 auto *currentSubExpr = (*argIter)->IgnoreImpCasts();
120 if (currentSubExpr->isLValue()) {
121 if (auto *tmp = dyn_cast<DeclRefExpr>(currentSubExpr))
122 emit = !isFunctionOrCatchVar(tmp);
123 else if (isa<CallExpr>(currentSubExpr))
124 emit = true;
125 }
126 }
127 if (emit)
128 diag(subExpr->getLocStart(),
129 "throw expression should throw anonymous temporary values instead");
130 }
131 }
132
133 void ThrowByValueCatchByReferenceCheck::diagnoseCatchLocations(
134 const CXXCatchStmt *catchStmt, ASTContext &context) {
135 const char *diagMsgCatchReference = "catch handler catches a pointer value; "
136 "should throw a non-pointer value and "
137 "catch by reference instead";
138 if (!catchStmt)
139 return;
140 auto caughtType = catchStmt->getCaughtType();
141 if (caughtType.isNull())
142 return;
143 auto *varDecl = catchStmt->getExceptionDecl();
144 if (const auto *PT = caughtType.getCanonicalType()->getAs<PointerType>()) {
145 // We do not diagnose when catching pointer to strings since we also allow
146 // throwing string literals.
147 if (!PT->getPointeeType()->isAnyCharacterType())
148 diag(varDecl->getLocStart(), diagMsgCatchReference);
149 } else if (!caughtType->isReferenceType()) {
150 // If it's not a pointer and not a reference then it must be thrown "by
151 // value". In this case we should emit a diagnosis message unless the type
152 // is trivial.
153 if (!caughtType.isTrivialType(context))
154 diag(varDecl->getLocStart(), diagMsgCatchReference);
155 }
156 }
157
158 } // namespace tidy
159 } // namespace clang
ThrowByValueCatchByReferenceCheck.h
clang::tidy::ClangTidyCheck::getLangOpts
LangOptions getLangOpts() const
Returns the language options from the context.
Definition: ClangTidy.h:162
Name
StringHandle Name
Definition: PreprocessorTracker.cpp:525
Finder
std::unique_ptr< ast_matchers::MatchFinder > Finder
Definition: ClangTidy.cpp:188
clang::tidy::ThrowByValueCatchByReferenceCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register ASTMatchers with Finder.
Definition: ThrowByValueCatchByReferenceCheck.cpp:25
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidy.h:102
clang::tidy::ThrowByValueCatchByReferenceCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: ThrowByValueCatchByReferenceCheck.cpp:39
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidy.h:158
clang::tidy::OptionsView::store
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.
Definition: ClangTidy.cpp:344
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, std::string > OptionMap
Definition: ClangTidyOptions.h:82
clang::tidy::modernize::PointerType
static const char PointerType[]
Definition: MakeUniqueCheck.cpp:21
Context
ClangTidyContext & Context
Definition: ClangTidy.cpp:93
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:124
clang::tidy::ThrowByValueCatchByReferenceCheck::storeOptions
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Definition: ThrowByValueCatchByReferenceCheck.cpp:34
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidy.cpp:323
Result
const NamedDecl * Result
Definition: USRFinder.cpp:121