18
18
#include " clang/AST/ExprCXX.h"
19
19
#include " clang/AST/Stmt.h"
20
20
#include " clang/ASTMatchers/ASTMatchers.h"
21
+ #include " clang/ASTMatchers/ASTMatchersMacros.h"
21
22
#include " clang/Analysis/CFG.h"
22
23
#include " clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
23
24
#include " clang/Analysis/FlowSensitive/DataflowEnvironment.h"
36
37
37
38
namespace clang {
38
39
namespace dataflow {
40
+
41
+ static bool isTopLevelNamespaceWithName (const NamespaceDecl &NS,
42
+ llvm::StringRef Name) {
43
+ return NS.getDeclName ().isIdentifier () && NS.getName () == Name &&
44
+ NS.getParent () != nullptr && NS.getParent ()->isTranslationUnit ();
45
+ }
46
+
47
+ static bool hasOptionalClassName (const CXXRecordDecl &RD) {
48
+ if (!RD.getDeclName ().isIdentifier ())
49
+ return false ;
50
+
51
+ if (RD.getName () == " optional" ) {
52
+ if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext ()))
53
+ return N->isStdNamespace () || isTopLevelNamespaceWithName (*N, " absl" );
54
+ return false ;
55
+ }
56
+
57
+ if (RD.getName () == " Optional" ) {
58
+ // Check whether namespace is "::base".
59
+ const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext ());
60
+ return N != nullptr && isTopLevelNamespaceWithName (*N, " base" );
61
+ }
62
+
63
+ return false ;
64
+ }
65
+
39
66
namespace {
40
67
41
68
using namespace ::clang::ast_matchers;
42
69
using LatticeTransferState = TransferState<NoopLattice>;
43
70
71
+ AST_MATCHER (CXXRecordDecl, hasOptionalClassNameMatcher) {
72
+ return hasOptionalClassName (Node);
73
+ }
74
+
44
75
DeclarationMatcher optionalClass () {
45
76
return classTemplateSpecializationDecl (
46
- hasAnyName (" ::std::optional" , " ::std::__optional_storage_base" ,
47
- " ::std::__optional_destruct_base" , " ::absl::optional" ,
48
- " ::base::Optional" ),
77
+ hasOptionalClassNameMatcher (),
49
78
hasTemplateArgument (0 , refersToType (type ().bind (" T" ))));
50
79
}
51
80
@@ -63,8 +92,10 @@ auto isOptionalMemberCallWithName(
63
92
auto Exception = unless (Ignorable ? expr (anyOf (*Ignorable, cxxThisExpr ()))
64
93
: cxxThisExpr ());
65
94
return cxxMemberCallExpr (
66
- on (expr (Exception)),
67
- callee (cxxMethodDecl (hasName (MemberName), ofClass (optionalClass ()))));
95
+ on (expr (Exception,
96
+ anyOf (hasOptionalType (),
97
+ hasType (pointerType (pointee (optionalOrAliasType ())))))),
98
+ callee (cxxMethodDecl (hasName (MemberName))));
68
99
}
69
100
70
101
auto isOptionalOperatorCallWithName (
@@ -251,34 +282,12 @@ QualType stripReference(QualType Type) {
251
282
return Type->isReferenceType () ? Type->getPointeeType () : Type;
252
283
}
253
284
254
- bool isTopLevelNamespaceWithName (const NamespaceDecl &NS,
255
- llvm::StringRef Name) {
256
- return NS.getDeclName ().isIdentifier () && NS.getName () == Name &&
257
- NS.getParent () != nullptr && NS.getParent ()->isTranslationUnit ();
258
- }
259
-
260
285
// / Returns true if and only if `Type` is an optional type.
261
286
bool isOptionalType (QualType Type) {
262
287
if (!Type->isRecordType ())
263
288
return false ;
264
289
const CXXRecordDecl *D = Type->getAsCXXRecordDecl ();
265
- if (D == nullptr || !D->getDeclName ().isIdentifier ())
266
- return false ;
267
- if (D->getName () == " optional" ) {
268
- if (const auto *N =
269
- dyn_cast_or_null<NamespaceDecl>(D->getDeclContext ()))
270
- return N->isStdNamespace () || isTopLevelNamespaceWithName (*N, " absl" );
271
- return false ;
272
- }
273
-
274
- if (D->getName () == " Optional" ) {
275
- // Check whether namespace is "::base".
276
- const auto *N =
277
- dyn_cast_or_null<NamespaceDecl>(D->getDeclContext ());
278
- return N != nullptr && isTopLevelNamespaceWithName (*N, " base" );
279
- }
280
-
281
- return false ;
290
+ return D != nullptr && hasOptionalClassName (*D);
282
291
}
283
292
284
293
// / Returns the number of optional wrappers in `Type`.
0 commit comments