@@ -64,38 +64,53 @@ static bool hasOptionalClassName(const CXXRecordDecl &RD) {
64
64
return false ;
65
65
}
66
66
67
+ static const CXXRecordDecl *getOptionalBaseClass (const CXXRecordDecl *RD) {
68
+ if (RD == nullptr )
69
+ return nullptr ;
70
+ if (hasOptionalClassName (*RD))
71
+ return RD;
72
+
73
+ if (!RD->hasDefinition ())
74
+ return nullptr ;
75
+
76
+ for (const CXXBaseSpecifier &Base : RD->bases ())
77
+ if (Base.getAccessSpecifier () == AS_public)
78
+ if (const CXXRecordDecl *BaseClass =
79
+ getOptionalBaseClass (Base.getType ()->getAsCXXRecordDecl ()))
80
+ return BaseClass;
81
+
82
+ return nullptr ;
83
+ }
84
+
67
85
namespace {
68
86
69
87
using namespace ::clang::ast_matchers;
70
88
using LatticeTransferState = TransferState<NoopLattice>;
71
89
72
- AST_MATCHER (CXXRecordDecl, hasOptionalClassNameMatcher ) {
73
- return hasOptionalClassName ( Node);
90
+ AST_MATCHER (CXXRecordDecl, optionalOrDerivedClass ) {
91
+ return getOptionalBaseClass (& Node) != nullptr ;
74
92
}
75
93
76
- DeclarationMatcher optionalClass () {
77
- return classTemplateSpecializationDecl (
78
- hasOptionalClassNameMatcher (),
79
- hasTemplateArgument (0 , refersToType (type ().bind (" T" ))));
80
- }
81
-
82
- auto optionalOrAliasType () {
94
+ auto desugarsToOptionalOrDerivedType () {
83
95
return hasUnqualifiedDesugaredType (
84
- recordType (hasDeclaration (optionalClass ( ))));
96
+ recordType (hasDeclaration (cxxRecordDecl ( optionalOrDerivedClass () ))));
85
97
}
86
98
87
- // / Matches any of the spellings of the optional types and sugar, aliases, etc.
88
- auto hasOptionalType () { return hasType (optionalOrAliasType ()); }
99
+ // / Matches any of the spellings of the optional types and sugar, aliases,
100
+ // / derived classes, etc.
101
+ auto hasOptionalOrDerivedType () {
102
+ return hasType (desugarsToOptionalOrDerivedType ());
103
+ }
89
104
90
105
auto isOptionalMemberCallWithNameMatcher (
91
106
ast_matchers::internal::Matcher<NamedDecl> matcher,
92
107
const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
93
108
auto Exception = unless (Ignorable ? expr (anyOf (*Ignorable, cxxThisExpr ()))
94
109
: cxxThisExpr ());
95
110
return cxxMemberCallExpr (
96
- on (expr (Exception,
97
- anyOf ( hasOptionalType (),
98
- hasType ( pointerType ( pointee ( optionalOrAliasType ())))))),
111
+ on (expr (Exception, anyOf ( hasOptionalOrDerivedType (),
112
+ hasType ( pointerType ( pointee (
113
+ desugarsToOptionalOrDerivedType ())))))),
99
114
callee (cxxMethodDecl (matcher)));
100
115
}
101
116
@@ -104,15 +119,15 @@ auto isOptionalOperatorCallWithName(
104
119
const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
105
120
return cxxOperatorCallExpr (
106
121
hasOverloadedOperatorName (operator_name),
107
- callee (cxxMethodDecl (ofClass (optionalClass ()))),
122
+ callee (cxxMethodDecl (ofClass (optionalOrDerivedClass ()))),
108
123
Ignorable ? callExpr (unless (hasArgument (0 , *Ignorable))) : callExpr ());
109
124
}
110
125
111
126
auto isMakeOptionalCall () {
112
127
return callExpr (callee (functionDecl (hasAnyName (
113
128
" std::make_optional" , " base::make_optional" ,
114
129
" absl::make_optional" , " folly::make_optional" ))),
115
- hasOptionalType ());
130
+ hasOptionalOrDerivedType ());
116
131
}
117
132
118
133
auto nulloptTypeDecl () {
@@ -129,19 +144,19 @@ auto inPlaceClass() {
129
144
130
145
auto isOptionalNulloptConstructor () {
131
146
return cxxConstructExpr (
132
- hasOptionalType (),
147
+ hasOptionalOrDerivedType (),
133
148
hasDeclaration (cxxConstructorDecl (parameterCountIs (1 ),
134
149
hasParameter (0 , hasNulloptType ()))));
135
150
}
136
151
137
152
auto isOptionalInPlaceConstructor () {
138
- return cxxConstructExpr (hasOptionalType (),
153
+ return cxxConstructExpr (hasOptionalOrDerivedType (),
139
154
hasArgument (0 , hasType (inPlaceClass ())));
140
155
}
141
156
142
157
auto isOptionalValueOrConversionConstructor () {
143
158
return cxxConstructExpr (
144
- hasOptionalType (),
159
+ hasOptionalOrDerivedType (),
145
160
unless (hasDeclaration (
146
161
cxxConstructorDecl (anyOf (isCopyConstructor (), isMoveConstructor ())))),
147
162
argumentCountIs (1 ), hasArgument (0 , unless (hasNulloptType ())));
@@ -150,28 +165,30 @@ auto isOptionalValueOrConversionConstructor() {
150
165
auto isOptionalValueOrConversionAssignment () {
151
166
return cxxOperatorCallExpr (
152
167
hasOverloadedOperatorName (" =" ),
153
- callee (cxxMethodDecl (ofClass (optionalClass ()))),
168
+ callee (cxxMethodDecl (ofClass (optionalOrDerivedClass ()))),
154
169
unless (hasDeclaration (cxxMethodDecl (
155
170
anyOf (isCopyAssignmentOperator (), isMoveAssignmentOperator ())))),
156
171
argumentCountIs (2 ), hasArgument (1 , unless (hasNulloptType ())));
157
172
}
158
173
159
174
auto isOptionalNulloptAssignment () {
160
- return cxxOperatorCallExpr (hasOverloadedOperatorName ( " = " ),
161
- callee ( cxxMethodDecl ( ofClass ( optionalClass ())) ),
162
- argumentCountIs ( 2 ),
163
- hasArgument (1 , hasNulloptType ()));
175
+ return cxxOperatorCallExpr (
176
+ hasOverloadedOperatorName ( " = " ),
177
+ callee ( cxxMethodDecl ( ofClass ( optionalOrDerivedClass ())) ),
178
+ argumentCountIs ( 2 ), hasArgument (1 , hasNulloptType ()));
164
179
}
165
180
166
181
auto isStdSwapCall () {
167
182
return callExpr (callee (functionDecl (hasName (" std::swap" ))),
168
- argumentCountIs (2 ), hasArgument (0 , hasOptionalType ()),
169
- hasArgument (1 , hasOptionalType ()));
183
+ argumentCountIs (2 ),
184
+ hasArgument (0 , hasOptionalOrDerivedType ()),
185
+ hasArgument (1 , hasOptionalOrDerivedType ()));
170
186
}
171
187
172
188
auto isStdForwardCall () {
173
189
return callExpr (callee (functionDecl (hasName (" std::forward" ))),
174
- argumentCountIs (1 ), hasArgument (0 , hasOptionalType ()));
190
+ argumentCountIs (1 ),
191
+ hasArgument (0 , hasOptionalOrDerivedType ()));
175
192
}
176
193
177
194
constexpr llvm::StringLiteral ValueOrCallID = " ValueOrCall" ;
@@ -181,21 +198,23 @@ auto isValueOrStringEmptyCall() {
181
198
return cxxMemberCallExpr (
182
199
callee (cxxMethodDecl (hasName (" empty" ))),
183
200
onImplicitObjectArgument (ignoringImplicit (
184
- cxxMemberCallExpr (on (expr (unless (cxxThisExpr ()))),
185
- callee (cxxMethodDecl (hasName (" value_or" ),
186
- ofClass (optionalClass ()))),
187
- hasArgument (0 , stringLiteral (hasSize (0 ))))
201
+ cxxMemberCallExpr (
202
+ on (expr (unless (cxxThisExpr ()))),
203
+ callee (cxxMethodDecl (hasName (" value_or" ),
204
+ ofClass (optionalOrDerivedClass ()))),
205
+ hasArgument (0 , stringLiteral (hasSize (0 ))))
188
206
.bind (ValueOrCallID))));
189
207
}
190
208
191
209
auto isValueOrNotEqX () {
192
210
auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {
193
211
return hasOperands (
194
212
ignoringImplicit (
195
- cxxMemberCallExpr (on (expr (unless (cxxThisExpr ()))),
196
- callee (cxxMethodDecl (hasName (" value_or" ),
197
- ofClass (optionalClass ()))),
198
- hasArgument (0 , Arg))
213
+ cxxMemberCallExpr (
214
+ on (expr (unless (cxxThisExpr ()))),
215
+ callee (cxxMethodDecl (hasName (" value_or" ),
216
+ ofClass (optionalOrDerivedClass ()))),
217
+ hasArgument (0 , Arg))
199
218
.bind (ValueOrCallID)),
200
219
ignoringImplicit (Arg));
201
220
};
@@ -212,8 +231,9 @@ auto isValueOrNotEqX() {
212
231
}
213
232
214
233
auto isCallReturningOptional () {
215
- return callExpr (hasType (qualType (anyOf (
216
- optionalOrAliasType (), referenceType (pointee (optionalOrAliasType ()))))));
234
+ return callExpr (hasType (qualType (
235
+ anyOf (desugarsToOptionalOrDerivedType (),
236
+ referenceType (pointee (desugarsToOptionalOrDerivedType ()))))));
217
237
}
218
238
219
239
template <typename L, typename R>
@@ -275,28 +295,23 @@ BoolValue *getHasValue(Environment &Env, RecordStorageLocation *OptionalLoc) {
275
295
return HasValueVal;
276
296
}
277
297
278
- // / Returns true if and only if `Type` is an optional type.
279
- bool isOptionalType (QualType Type) {
280
- if (!Type->isRecordType ())
281
- return false ;
282
- const CXXRecordDecl *D = Type->getAsCXXRecordDecl ();
283
- return D != nullptr && hasOptionalClassName (*D);
298
+ QualType valueTypeFromOptionalDecl (const CXXRecordDecl &RD) {
299
+ auto &CTSD = cast<ClassTemplateSpecializationDecl>(RD);
300
+ return CTSD.getTemplateArgs ()[0 ].getAsType ();
284
301
}
285
302
286
303
// / Returns the number of optional wrappers in `Type`.
287
304
// /
288
305
// / For example, if `Type` is `optional<optional<int>>`, the result of this
289
306
// / function will be 2.
290
307
int countOptionalWrappers (const ASTContext &ASTCtx, QualType Type) {
291
- if (!isOptionalType (Type))
308
+ const CXXRecordDecl *Optional =
309
+ getOptionalBaseClass (Type->getAsCXXRecordDecl ());
310
+ if (Optional == nullptr )
292
311
return 0 ;
293
312
return 1 + countOptionalWrappers (
294
313
ASTCtx,
295
- cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl ())
296
- ->getTemplateArgs ()
297
- .get (0 )
298
- .getAsType ()
299
- .getDesugaredType (ASTCtx));
314
+ valueTypeFromOptionalDecl (*Optional).getDesugaredType (ASTCtx));
300
315
}
301
316
302
317
StorageLocation *getLocBehindPossiblePointer (const Expr &E,
@@ -623,7 +638,7 @@ ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
623
638
if (Options.IgnoreSmartPointerDereference ) {
624
639
auto SmartPtrUse = expr (ignoringParenImpCasts (cxxOperatorCallExpr (
625
640
anyOf (hasOverloadedOperatorName (" ->" ), hasOverloadedOperatorName (" *" )),
626
- unless (hasArgument (0 , expr (hasOptionalType ()))))));
641
+ unless (hasArgument (0 , expr (hasOptionalOrDerivedType ()))))));
627
642
return expr (
628
643
anyOf (SmartPtrUse, memberExpr (hasObjectExpression (SmartPtrUse))));
629
644
}
@@ -758,32 +773,35 @@ auto buildTransferMatchSwitch() {
758
773
759
774
// Comparisons (==, !=):
760
775
.CaseOfCFGStmt <CXXOperatorCallExpr>(
761
- isComparisonOperatorCall (hasOptionalType (), hasOptionalType ()),
776
+ isComparisonOperatorCall (hasOptionalOrDerivedType (),
777
+ hasOptionalOrDerivedType ()),
762
778
transferOptionalAndOptionalCmp)
763
779
.CaseOfCFGStmt <CXXOperatorCallExpr>(
764
- isComparisonOperatorCall (hasOptionalType (), hasNulloptType ()),
780
+ isComparisonOperatorCall (hasOptionalOrDerivedType (),
781
+ hasNulloptType ()),
765
782
[](const clang::CXXOperatorCallExpr *Cmp,
766
783
const MatchFinder::MatchResult &, LatticeTransferState &State) {
767
784
transferOptionalAndNulloptCmp (Cmp, Cmp->getArg (0 ), State.Env );
768
785
})
769
786
.CaseOfCFGStmt <CXXOperatorCallExpr>(
770
- isComparisonOperatorCall (hasNulloptType (), hasOptionalType ()),
787
+ isComparisonOperatorCall (hasNulloptType (),
788
+ hasOptionalOrDerivedType ()),
771
789
[](const clang::CXXOperatorCallExpr *Cmp,
772
790
const MatchFinder::MatchResult &, LatticeTransferState &State) {
773
791
transferOptionalAndNulloptCmp (Cmp, Cmp->getArg (1 ), State.Env );
774
792
})
775
793
.CaseOfCFGStmt <CXXOperatorCallExpr>(
776
794
isComparisonOperatorCall (
777
- hasOptionalType (),
778
- unless (anyOf (hasOptionalType (), hasNulloptType ()))),
795
+ hasOptionalOrDerivedType (),
796
+ unless (anyOf (hasOptionalOrDerivedType (), hasNulloptType ()))),
779
797
[](const clang::CXXOperatorCallExpr *Cmp,
780
798
const MatchFinder::MatchResult &, LatticeTransferState &State) {
781
799
transferOptionalAndValueCmp (Cmp, Cmp->getArg (0 ), State.Env );
782
800
})
783
801
.CaseOfCFGStmt <CXXOperatorCallExpr>(
784
802
isComparisonOperatorCall (
785
- unless (anyOf (hasOptionalType (), hasNulloptType ())),
786
- hasOptionalType ()),
803
+ unless (anyOf (hasOptionalOrDerivedType (), hasNulloptType ())),
804
+ hasOptionalOrDerivedType ()),
787
805
[](const clang::CXXOperatorCallExpr *Cmp,
788
806
const MatchFinder::MatchResult &, LatticeTransferState &State) {
789
807
transferOptionalAndValueCmp (Cmp, Cmp->getArg (1 ), State.Env );
@@ -843,13 +861,7 @@ auto buildDiagnoseMatchSwitch(
843
861
844
862
ast_matchers::DeclarationMatcher
845
863
UncheckedOptionalAccessModel::optionalClassDecl () {
846
- return optionalClass ();
847
- }
848
-
849
- static QualType valueTypeFromOptionalType (QualType OptionalTy) {
850
- auto *CTSD =
851
- cast<ClassTemplateSpecializationDecl>(OptionalTy->getAsCXXRecordDecl ());
852
- return CTSD->getTemplateArgs ()[0 ].getAsType ();
864
+ return cxxRecordDecl (optionalOrDerivedClass ());
853
865
}
854
866
855
867
UncheckedOptionalAccessModel::UncheckedOptionalAccessModel (ASTContext &Ctx,
@@ -858,9 +870,11 @@ UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx,
858
870
TransferMatchSwitch (buildTransferMatchSwitch()) {
859
871
Env.getDataflowAnalysisContext ().setSyntheticFieldCallback (
860
872
[&Ctx](QualType Ty) -> llvm::StringMap<QualType> {
861
- if (!isOptionalType (Ty))
873
+ const CXXRecordDecl *Optional =
874
+ getOptionalBaseClass (Ty->getAsCXXRecordDecl ());
875
+ if (Optional == nullptr )
862
876
return {};
863
- return {{" value" , valueTypeFromOptionalType (Ty )},
877
+ return {{" value" , valueTypeFromOptionalDecl (*Optional )},
864
878
{" has_value" , Ctx.BoolTy }};
865
879
});
866
880
}
0 commit comments