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