1//===--- ExceptionSpecAnalyzer.cpp - clang-tidy ---------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "ExceptionSpecAnalyzer.h"
10
11#include "clang/AST/Expr.h"
12
13namespace clang::tidy::utils {
14
15ExceptionSpecAnalyzer::State
16ExceptionSpecAnalyzer::analyze(const FunctionDecl *FuncDecl) {
17 // Check if function exist in cache or add temporary value to cache to protect
18 // against endless recursion.
19 const auto [CacheEntry, NotFound] =
20 FunctionCache.try_emplace(Key: FuncDecl, Args: State::NotThrowing);
21 if (NotFound) {
22 ExceptionSpecAnalyzer::State State = analyzeImpl(FuncDecl);
23 // Update result with calculated value
24 FunctionCache[FuncDecl] = State;
25 return State;
26 }
27
28 return CacheEntry->getSecond();
29}
30
31ExceptionSpecAnalyzer::State
32ExceptionSpecAnalyzer::analyzeUnresolvedOrDefaulted(
33 const CXXMethodDecl *MethodDecl, const FunctionProtoType *FuncProto) {
34 if (!FuncProto || !MethodDecl)
35 return State::Unknown;
36
37 const DefaultableMemberKind Kind = getDefaultableMemberKind(MethodDecl);
38
39 if (Kind == DefaultableMemberKind::None)
40 return State::Unknown;
41
42 return analyzeRecord(RecordDecl: MethodDecl->getParent(), Kind, SkipMethods: SkipMethods::Yes);
43}
44
45ExceptionSpecAnalyzer::State
46ExceptionSpecAnalyzer::analyzeFieldDecl(const FieldDecl *FDecl,
47 DefaultableMemberKind Kind) {
48 if (!FDecl)
49 return State::Unknown;
50
51 if (const CXXRecordDecl *RecDecl =
52 FDecl->getType()->getUnqualifiedDesugaredType()->getAsCXXRecordDecl())
53 return analyzeRecord(RecordDecl: RecDecl, Kind);
54
55 // Trivial types do not throw
56 if (FDecl->getType().isTrivialType(FDecl->getASTContext()))
57 return State::NotThrowing;
58
59 return State::Unknown;
60}
61
62ExceptionSpecAnalyzer::State
63ExceptionSpecAnalyzer::analyzeBase(const CXXBaseSpecifier &Base,
64 DefaultableMemberKind Kind) {
65 const auto *RecType = Base.getType()->getAs<RecordType>();
66 if (!RecType)
67 return State::Unknown;
68
69 const auto *BaseClass = cast<CXXRecordDecl>(Val: RecType->getDecl());
70
71 return analyzeRecord(RecordDecl: BaseClass, Kind);
72}
73
74ExceptionSpecAnalyzer::State
75ExceptionSpecAnalyzer::analyzeRecord(const CXXRecordDecl *RecordDecl,
76 DefaultableMemberKind Kind,
77 SkipMethods SkipMethods) {
78 if (!RecordDecl)
79 return State::Unknown;
80
81 // Trivial implies noexcept
82 if (hasTrivialMemberKind(RecDecl: RecordDecl, Kind))
83 return State::NotThrowing;
84
85 if (SkipMethods == SkipMethods::No)
86 for (const auto *MethodDecl : RecordDecl->methods())
87 if (getDefaultableMemberKind(MethodDecl) == Kind)
88 return analyze(MethodDecl);
89
90 for (const auto &BaseSpec : RecordDecl->bases()) {
91 State Result = analyzeBase(Base: BaseSpec, Kind);
92 if (Result == State::Throwing || Result == State::Unknown)
93 return Result;
94 }
95
96 for (const auto &BaseSpec : RecordDecl->vbases()) {
97 State Result = analyzeBase(Base: BaseSpec, Kind);
98 if (Result == State::Throwing || Result == State::Unknown)
99 return Result;
100 }
101
102 for (const auto *FDecl : RecordDecl->fields())
103 if (!FDecl->isInvalidDecl() && !FDecl->isUnnamedBitField()) {
104 State Result = analyzeFieldDecl(FDecl, Kind);
105 if (Result == State::Throwing || Result == State::Unknown)
106 return Result;
107 }
108
109 return State::NotThrowing;
110}
111
112ExceptionSpecAnalyzer::State
113ExceptionSpecAnalyzer::analyzeImpl(const FunctionDecl *FuncDecl) {
114 const auto *FuncProto = FuncDecl->getType()->getAs<FunctionProtoType>();
115 if (!FuncProto)
116 return State::Unknown;
117
118 const ExceptionSpecificationType EST = FuncProto->getExceptionSpecType();
119
120 if (EST == EST_Unevaluated || (EST == EST_None && FuncDecl->isDefaulted()))
121 return analyzeUnresolvedOrDefaulted(MethodDecl: cast<CXXMethodDecl>(Val: FuncDecl),
122 FuncProto: FuncProto);
123
124 return analyzeFunctionEST(FuncDecl, FuncProto: FuncProto);
125}
126
127ExceptionSpecAnalyzer::State
128ExceptionSpecAnalyzer::analyzeFunctionEST(const FunctionDecl *FuncDecl,
129 const FunctionProtoType *FuncProto) {
130 if (!FuncDecl || !FuncProto)
131 return State::Unknown;
132
133 if (isUnresolvedExceptionSpec(ESpecType: FuncProto->getExceptionSpecType()))
134 return State::Unknown;
135
136 // A non defaulted destructor without the noexcept specifier is still noexcept
137 if (isa<CXXDestructorDecl>(Val: FuncDecl) &&
138 FuncDecl->getExceptionSpecType() == EST_None)
139 return State::NotThrowing;
140
141 switch (FuncProto->canThrow()) {
142 case CT_Cannot:
143 return State::NotThrowing;
144 case CT_Dependent: {
145 const Expr *NoexceptExpr = FuncProto->getNoexceptExpr();
146 if (!NoexceptExpr)
147 return State::NotThrowing;
148
149 // We can't resolve value dependence so just return unknown
150 if (NoexceptExpr->isValueDependent())
151 return State::Unknown;
152
153 // Try to evaluate the expression to a boolean value
154 bool Result = false;
155 if (NoexceptExpr->EvaluateAsBooleanCondition(
156 Result, Ctx: FuncDecl->getASTContext(), InConstantContext: true))
157 return Result ? State::NotThrowing : State::Throwing;
158
159 // The noexcept expression is not value dependent but we can't evaluate it
160 // as a boolean condition so we have no idea if its throwing or not
161 return State::Unknown;
162 }
163 default:
164 return State::Throwing;
165 };
166}
167
168bool ExceptionSpecAnalyzer::hasTrivialMemberKind(const CXXRecordDecl *RecDecl,
169 DefaultableMemberKind Kind) {
170 if (!RecDecl)
171 return false;
172
173 switch (Kind) {
174 case DefaultableMemberKind::DefaultConstructor:
175 return RecDecl->hasTrivialDefaultConstructor();
176 case DefaultableMemberKind::CopyConstructor:
177 return RecDecl->hasTrivialCopyConstructor();
178 case DefaultableMemberKind::MoveConstructor:
179 return RecDecl->hasTrivialMoveConstructor();
180 case DefaultableMemberKind::CopyAssignment:
181 return RecDecl->hasTrivialCopyAssignment();
182 case DefaultableMemberKind::MoveAssignment:
183 return RecDecl->hasTrivialMoveAssignment();
184 case DefaultableMemberKind::Destructor:
185 return RecDecl->hasTrivialDestructor();
186
187 default:
188 return false;
189 }
190}
191
192bool ExceptionSpecAnalyzer::isConstructor(DefaultableMemberKind Kind) {
193 switch (Kind) {
194 case DefaultableMemberKind::DefaultConstructor:
195 case DefaultableMemberKind::CopyConstructor:
196 case DefaultableMemberKind::MoveConstructor:
197 return true;
198
199 default:
200 return false;
201 }
202}
203
204bool ExceptionSpecAnalyzer::isSpecialMember(DefaultableMemberKind Kind) {
205 switch (Kind) {
206 case DefaultableMemberKind::DefaultConstructor:
207 case DefaultableMemberKind::CopyConstructor:
208 case DefaultableMemberKind::MoveConstructor:
209 case DefaultableMemberKind::CopyAssignment:
210 case DefaultableMemberKind::MoveAssignment:
211 case DefaultableMemberKind::Destructor:
212 return true;
213 default:
214 return false;
215 }
216}
217
218bool ExceptionSpecAnalyzer::isComparison(DefaultableMemberKind Kind) {
219 switch (Kind) {
220 case DefaultableMemberKind::CompareEqual:
221 case DefaultableMemberKind::CompareNotEqual:
222 case DefaultableMemberKind::CompareRelational:
223 case DefaultableMemberKind::CompareThreeWay:
224 return true;
225 default:
226 return false;
227 }
228}
229
230ExceptionSpecAnalyzer::DefaultableMemberKind
231ExceptionSpecAnalyzer::getDefaultableMemberKind(const FunctionDecl *FuncDecl) {
232 if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(Val: FuncDecl)) {
233 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Val: FuncDecl)) {
234 if (Ctor->isDefaultConstructor())
235 return DefaultableMemberKind::DefaultConstructor;
236
237 if (Ctor->isCopyConstructor())
238 return DefaultableMemberKind::CopyConstructor;
239
240 if (Ctor->isMoveConstructor())
241 return DefaultableMemberKind::MoveConstructor;
242 }
243
244 if (MethodDecl->isCopyAssignmentOperator())
245 return DefaultableMemberKind::CopyAssignment;
246
247 if (MethodDecl->isMoveAssignmentOperator())
248 return DefaultableMemberKind::MoveAssignment;
249
250 if (isa<CXXDestructorDecl>(Val: FuncDecl))
251 return DefaultableMemberKind::Destructor;
252 }
253
254 const LangOptions &LangOpts = FuncDecl->getLangOpts();
255
256 switch (FuncDecl->getDeclName().getCXXOverloadedOperator()) {
257 case OO_EqualEqual:
258 return DefaultableMemberKind::CompareEqual;
259
260 case OO_ExclaimEqual:
261 return DefaultableMemberKind::CompareNotEqual;
262
263 case OO_Spaceship:
264 // No point allowing this if <=> doesn't exist in the current language mode.
265 if (!LangOpts.CPlusPlus20)
266 break;
267 return DefaultableMemberKind::CompareThreeWay;
268
269 case OO_Less:
270 case OO_LessEqual:
271 case OO_Greater:
272 case OO_GreaterEqual:
273 // No point allowing this if <=> doesn't exist in the current language mode.
274 if (!LangOpts.CPlusPlus20)
275 break;
276 return DefaultableMemberKind::CompareRelational;
277
278 default:
279 break;
280 }
281
282 // Not a defaultable member kind
283 return DefaultableMemberKind::None;
284}
285
286} // namespace clang::tidy::utils
287

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of clang-tools-extra/clang-tidy/utils/ExceptionSpecAnalyzer.cpp