1 //===--- ExceptionSpecAnalyzer.cpp - clang-tidy ---------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "ExceptionSpecAnalyzer.h"
11 #include "clang/AST/Expr.h"
13 namespace clang::tidy::utils
{
15 ExceptionSpecAnalyzer::State
16 ExceptionSpecAnalyzer::analyze(const FunctionDecl
*FuncDecl
) {
17 ExceptionSpecAnalyzer::State State
;
19 // Check if the function has already been analyzed and reuse that result.
20 const auto CacheEntry
= FunctionCache
.find(FuncDecl
);
21 if (CacheEntry
== FunctionCache
.end()) {
22 State
= analyzeImpl(FuncDecl
);
24 // Cache the result of the analysis.
25 FunctionCache
.try_emplace(FuncDecl
, State
);
27 State
= CacheEntry
->getSecond();
32 ExceptionSpecAnalyzer::State
33 ExceptionSpecAnalyzer::analyzeUnresolvedOrDefaulted(
34 const CXXMethodDecl
*MethodDecl
, const FunctionProtoType
*FuncProto
) {
35 if (!FuncProto
|| !MethodDecl
)
36 return State::Unknown
;
38 const DefaultableMemberKind Kind
= getDefaultableMemberKind(MethodDecl
);
40 if (Kind
== DefaultableMemberKind::None
)
41 return State::Unknown
;
43 return analyzeRecord(MethodDecl
->getParent(), Kind
, SkipMethods::Yes
);
46 ExceptionSpecAnalyzer::State
47 ExceptionSpecAnalyzer::analyzeFieldDecl(const FieldDecl
*FDecl
,
48 DefaultableMemberKind Kind
) {
50 return State::Unknown
;
52 if (const CXXRecordDecl
*RecDecl
=
53 FDecl
->getType()->getUnqualifiedDesugaredType()->getAsCXXRecordDecl())
54 return analyzeRecord(RecDecl
, Kind
);
56 // Trivial types do not throw
57 if (FDecl
->getType().isTrivialType(FDecl
->getASTContext()))
58 return State::NotThrowing
;
60 return State::Unknown
;
63 ExceptionSpecAnalyzer::State
64 ExceptionSpecAnalyzer::analyzeBase(const CXXBaseSpecifier
&Base
,
65 DefaultableMemberKind Kind
) {
66 const auto *RecType
= Base
.getType()->getAs
<RecordType
>();
68 return State::Unknown
;
70 const auto *BaseClass
= cast
<CXXRecordDecl
>(RecType
->getDecl());
72 return analyzeRecord(BaseClass
, Kind
);
75 ExceptionSpecAnalyzer::State
76 ExceptionSpecAnalyzer::analyzeRecord(const CXXRecordDecl
*RecordDecl
,
77 DefaultableMemberKind Kind
,
78 SkipMethods SkipMethods
) {
80 return State::Unknown
;
82 // Trivial implies noexcept
83 if (hasTrivialMemberKind(RecordDecl
, Kind
))
84 return State::NotThrowing
;
86 if (SkipMethods
== SkipMethods::No
)
87 for (const auto *MethodDecl
: RecordDecl
->methods())
88 if (getDefaultableMemberKind(MethodDecl
) == Kind
)
89 return analyze(MethodDecl
);
91 for (const auto &BaseSpec
: RecordDecl
->bases()) {
92 State Result
= analyzeBase(BaseSpec
, Kind
);
93 if (Result
== State::Throwing
|| Result
== State::Unknown
)
97 for (const auto &BaseSpec
: RecordDecl
->vbases()) {
98 State Result
= analyzeBase(BaseSpec
, Kind
);
99 if (Result
== State::Throwing
|| Result
== State::Unknown
)
103 for (const auto *FDecl
: RecordDecl
->fields())
104 if (!FDecl
->isInvalidDecl() && !FDecl
->isUnnamedBitfield()) {
105 State Result
= analyzeFieldDecl(FDecl
, Kind
);
106 if (Result
== State::Throwing
|| Result
== State::Unknown
)
110 return State::NotThrowing
;
113 ExceptionSpecAnalyzer::State
114 ExceptionSpecAnalyzer::analyzeImpl(const FunctionDecl
*FuncDecl
) {
115 const auto *FuncProto
= FuncDecl
->getType()->getAs
<FunctionProtoType
>();
117 return State::Unknown
;
119 const ExceptionSpecificationType EST
= FuncProto
->getExceptionSpecType();
121 if (EST
== EST_Unevaluated
|| (EST
== EST_None
&& FuncDecl
->isDefaulted()))
122 return analyzeUnresolvedOrDefaulted(cast
<CXXMethodDecl
>(FuncDecl
),
125 return analyzeFunctionEST(FuncDecl
, FuncProto
);
128 ExceptionSpecAnalyzer::State
129 ExceptionSpecAnalyzer::analyzeFunctionEST(const FunctionDecl
*FuncDecl
,
130 const FunctionProtoType
*FuncProto
) {
131 if (!FuncDecl
|| !FuncProto
)
132 return State::Unknown
;
134 if (isUnresolvedExceptionSpec(FuncProto
->getExceptionSpecType()))
135 return State::Unknown
;
137 // A non defaulted destructor without the noexcept specifier is still noexcept
138 if (isa
<CXXDestructorDecl
>(FuncDecl
) &&
139 FuncDecl
->getExceptionSpecType() == EST_None
)
140 return State::NotThrowing
;
142 switch (FuncProto
->canThrow()) {
144 return State::NotThrowing
;
146 const Expr
*NoexceptExpr
= FuncProto
->getNoexceptExpr();
148 return (NoexceptExpr
&& !NoexceptExpr
->isValueDependent() &&
149 NoexceptExpr
->EvaluateAsBooleanCondition(
150 Result
, FuncDecl
->getASTContext(), true) &&
156 return State::Throwing
;
160 bool ExceptionSpecAnalyzer::hasTrivialMemberKind(const CXXRecordDecl
*RecDecl
,
161 DefaultableMemberKind Kind
) {
166 case DefaultableMemberKind::DefaultConstructor
:
167 return RecDecl
->hasTrivialDefaultConstructor();
168 case DefaultableMemberKind::CopyConstructor
:
169 return RecDecl
->hasTrivialCopyConstructor();
170 case DefaultableMemberKind::MoveConstructor
:
171 return RecDecl
->hasTrivialMoveConstructor();
172 case DefaultableMemberKind::CopyAssignment
:
173 return RecDecl
->hasTrivialCopyAssignment();
174 case DefaultableMemberKind::MoveAssignment
:
175 return RecDecl
->hasTrivialMoveAssignment();
176 case DefaultableMemberKind::Destructor
:
177 return RecDecl
->hasTrivialDestructor();
184 bool ExceptionSpecAnalyzer::isConstructor(DefaultableMemberKind Kind
) {
186 case DefaultableMemberKind::DefaultConstructor
:
187 case DefaultableMemberKind::CopyConstructor
:
188 case DefaultableMemberKind::MoveConstructor
:
196 bool ExceptionSpecAnalyzer::isSpecialMember(DefaultableMemberKind Kind
) {
198 case DefaultableMemberKind::DefaultConstructor
:
199 case DefaultableMemberKind::CopyConstructor
:
200 case DefaultableMemberKind::MoveConstructor
:
201 case DefaultableMemberKind::CopyAssignment
:
202 case DefaultableMemberKind::MoveAssignment
:
203 case DefaultableMemberKind::Destructor
:
210 bool ExceptionSpecAnalyzer::isComparison(DefaultableMemberKind Kind
) {
212 case DefaultableMemberKind::CompareEqual
:
213 case DefaultableMemberKind::CompareNotEqual
:
214 case DefaultableMemberKind::CompareRelational
:
215 case DefaultableMemberKind::CompareThreeWay
:
222 ExceptionSpecAnalyzer::DefaultableMemberKind
223 ExceptionSpecAnalyzer::getDefaultableMemberKind(const FunctionDecl
*FuncDecl
) {
224 if (const auto *MethodDecl
= dyn_cast
<CXXMethodDecl
>(FuncDecl
)) {
225 if (const auto *Ctor
= dyn_cast
<CXXConstructorDecl
>(FuncDecl
)) {
226 if (Ctor
->isDefaultConstructor())
227 return DefaultableMemberKind::DefaultConstructor
;
229 if (Ctor
->isCopyConstructor())
230 return DefaultableMemberKind::CopyConstructor
;
232 if (Ctor
->isMoveConstructor())
233 return DefaultableMemberKind::MoveConstructor
;
236 if (MethodDecl
->isCopyAssignmentOperator())
237 return DefaultableMemberKind::CopyAssignment
;
239 if (MethodDecl
->isMoveAssignmentOperator())
240 return DefaultableMemberKind::MoveAssignment
;
242 if (isa
<CXXDestructorDecl
>(FuncDecl
))
243 return DefaultableMemberKind::Destructor
;
246 const LangOptions
&LangOpts
= FuncDecl
->getLangOpts();
248 switch (FuncDecl
->getDeclName().getCXXOverloadedOperator()) {
250 return DefaultableMemberKind::CompareEqual
;
252 case OO_ExclaimEqual
:
253 return DefaultableMemberKind::CompareNotEqual
;
256 // No point allowing this if <=> doesn't exist in the current language mode.
257 if (!LangOpts
.CPlusPlus20
)
259 return DefaultableMemberKind::CompareThreeWay
;
264 case OO_GreaterEqual
:
265 // No point allowing this if <=> doesn't exist in the current language mode.
266 if (!LangOpts
.CPlusPlus20
)
268 return DefaultableMemberKind::CompareRelational
;
274 // Not a defaultable member kind
275 return DefaultableMemberKind::None
;
278 } // namespace clang::tidy::utils