[clang] [clang][dataflow] Make optional checker work for types derived from optional. (PR #84138)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 18 03:30:28 PDT 2024
================
@@ -64,39 +64,117 @@ static bool hasOptionalClassName(const CXXRecordDecl &RD) {
return false;
}
+static const CXXRecordDecl *getOptionalBaseClass(const CXXRecordDecl *RD) {
+ if (RD == nullptr)
+ return nullptr;
+ if (hasOptionalClassName(*RD))
+ return RD;
+
+ if (!RD->hasDefinition())
+ return nullptr;
+
+ for (const CXXBaseSpecifier &Base : RD->bases())
+ if (const CXXRecordDecl *BaseClass =
+ getOptionalBaseClass(Base.getType()->getAsCXXRecordDecl()))
+ return BaseClass;
+
+ return nullptr;
+}
+
namespace {
using namespace ::clang::ast_matchers;
using LatticeTransferState = TransferState<NoopLattice>;
-AST_MATCHER(CXXRecordDecl, hasOptionalClassNameMatcher) {
- return hasOptionalClassName(Node);
+AST_MATCHER(CXXRecordDecl, optionalClass) { return hasOptionalClassName(Node); }
+
+AST_MATCHER(CXXRecordDecl, optionalOrDerivedClass) {
+ return getOptionalBaseClass(&Node) != nullptr;
}
-DeclarationMatcher optionalClass() {
- return classTemplateSpecializationDecl(
- hasOptionalClassNameMatcher(),
- hasTemplateArgument(0, refersToType(type().bind("T"))));
+auto desugarsToOptionalType() {
+ return hasUnqualifiedDesugaredType(
+ recordType(hasDeclaration(cxxRecordDecl(optionalClass()))));
}
-auto optionalOrAliasType() {
+auto desugarsToOptionalOrDerivedType() {
return hasUnqualifiedDesugaredType(
- recordType(hasDeclaration(optionalClass())));
+ recordType(hasDeclaration(cxxRecordDecl(optionalOrDerivedClass()))));
}
-/// Matches any of the spellings of the optional types and sugar, aliases, etc.
-auto hasOptionalType() { return hasType(optionalOrAliasType()); }
+auto hasOptionalType() { return hasType(desugarsToOptionalType()); }
+
+/// Matches any of the spellings of the optional types and sugar, aliases,
+/// derived classes, etc.
+auto hasOptionalOrDerivedType() {
+ return hasType(desugarsToOptionalOrDerivedType());
+}
+
+QualType getPublicType(const Expr *E) {
+ auto *Cast = dyn_cast<ImplicitCastExpr>(E->IgnoreParens());
+ if (Cast == nullptr || Cast->getCastKind() != CK_UncheckedDerivedToBase) {
+ QualType Ty = E->getType();
+ if (Ty->isPointerType())
+ return Ty->getPointeeType();
+ return Ty;
+ }
+
+ QualType Ty = getPublicType(Cast->getSubExpr());
+
+ // Is `Ty` the type of `*this`? In this special case, we can upcast to the
+ // base class even if the base is non-public.
+ bool TyIsThisType = isa<CXXThisExpr>(Cast->getSubExpr());
+
+ for (const CXXBaseSpecifier *Base : Cast->path()) {
+ if (Base->getAccessSpecifier() != AS_public && !TyIsThisType)
+ break;
+ Ty = Base->getType();
+ TyIsThisType = false;
+ }
----------------
martinboehme wrote:
> Is the idea that we want the last in the list of base classes?
Exactly. I've added comments that will hopefully make it clearer what is going on here.
https://github.com/llvm/llvm-project/pull/84138
More information about the cfe-commits
mailing list