Author: Nathan Ridge Date: 2025-01-30T01:54:30-05:00 New Revision: 7fd84264eda6546950883a20c2f948eb1a560f4f
URL: https://github.com/llvm/llvm-project/commit/7fd84264eda6546950883a20c2f948eb1a560f4f DIFF: https://github.com/llvm/llvm-project/commit/7fd84264eda6546950883a20c2f948eb1a560f4f.diff LOG: [clang][Sema] Handle pointer and reference type more robustly in HeuristicResolver::resolveMemberExpr() (#124451) Fixes https://github.com/llvm/llvm-project/issues/124450 Added: Modified: clang/lib/Sema/HeuristicResolver.cpp clang/unittests/Sema/HeuristicResolverTest.cpp Removed: ################################################################################ diff --git a/clang/lib/Sema/HeuristicResolver.cpp b/clang/lib/Sema/HeuristicResolver.cpp index 0ebeb9e66e053a..0c57250e63df2e 100644 --- a/clang/lib/Sema/HeuristicResolver.cpp +++ b/clang/lib/Sema/HeuristicResolver.cpp @@ -74,6 +74,15 @@ class HeuristicResolverImpl { // resolves it to a TagDecl in which we can try name lookup. TagDecl *resolveTypeToTagDecl(const Type *T); + // Helper function for simplifying a type. + // `Type` is the type to simplify. + // `E` is the expression whose type `Type` is, if known. This sometimes + // contains information relevant to the type that's not stored in `Type` + // itself. + // If `UnwrapPointer` is true, exactly only pointer type will be unwrapped + // during simplification, and the operation fails if no pointer type is found. + QualType simplifyType(QualType Type, const Expr *E, bool UnwrapPointer); + // This is a reimplementation of CXXRecordDecl::lookupDependentName() // so that the implementation can call into other HeuristicResolver helpers. // FIXME: Once HeuristicResolver is upstreamed to the clang libraries @@ -198,6 +207,57 @@ QualType HeuristicResolverImpl::getPointeeType(QualType T) { return FirstArg.getAsType(); } +QualType HeuristicResolverImpl::simplifyType(QualType Type, const Expr *E, + bool UnwrapPointer) { + bool DidUnwrapPointer = false; + auto SimplifyOneStep = [&](QualType T) { + if (UnwrapPointer) { + if (QualType Pointee = getPointeeType(T); !Pointee.isNull()) { + DidUnwrapPointer = true; + return Pointee; + } + } + if (const auto *RT = T->getAs<ReferenceType>()) { + // Does not count as "unwrap pointer". + return RT->getPointeeType(); + } + if (const auto *BT = T->getAs<BuiltinType>()) { + // If BaseType is the type of a dependent expression, it's just + // represented as BuiltinType::Dependent which gives us no information. We + // can get further by analyzing the dependent expression. + if (E && BT->getKind() == BuiltinType::Dependent) { + return resolveExprToType(E); + } + } + if (const auto *AT = T->getContainedAutoType()) { + // If T contains a dependent `auto` type, deduction will not have + // been performed on it yet. In simple cases (e.g. `auto` variable with + // initializer), get the approximate type that would result from + // deduction. + // FIXME: A more accurate implementation would propagate things like the + // `const` in `const auto`. + if (E && AT->isUndeducedAutoType()) { + if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) { + if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { + if (VD->hasInit()) + return resolveExprToType(VD->getInit()); + } + } + } + } + return T; + }; + while (!Type.isNull()) { + QualType New = SimplifyOneStep(Type); + if (New == Type) + break; + Type = New; + } + if (UnwrapPointer && !DidUnwrapPointer) + return QualType(); + return Type; +} + std::vector<const NamedDecl *> HeuristicResolverImpl::resolveMemberExpr( const CXXDependentScopeMemberExpr *ME) { // If the expression has a qualifier, try resolving the member inside the @@ -230,36 +290,7 @@ std::vector<const NamedDecl *> HeuristicResolverImpl::resolveMemberExpr( // Try resolving the member inside the expression's base type. Expr *Base = ME->isImplicitAccess() ? nullptr : ME->getBase(); QualType BaseType = ME->getBaseType(); - if (ME->isArrow()) { - BaseType = getPointeeType(BaseType); - if (BaseType.isNull()) - return {}; - } - if (const auto *BT = BaseType->getAs<BuiltinType>()) { - // If BaseType is the type of a dependent expression, it's just - // represented as BuiltinType::Dependent which gives us no information. We - // can get further by analyzing the dependent expression. - if (Base && BT->getKind() == BuiltinType::Dependent) { - BaseType = resolveExprToType(Base); - if (BaseType.isNull()) - return {}; - } - } - if (const auto *AT = BaseType->getContainedAutoType()) { - // If BaseType contains a dependent `auto` type, deduction will not have - // been performed on it yet. In simple cases (e.g. `auto` variable with - // initializer), get the approximate type that would result from deduction. - // FIXME: A more accurate implementation would propagate things like the - // `const` in `const auto`. - if (AT->isUndeducedAutoType()) { - if (const auto *DRE = dyn_cast<DeclRefExpr>(Base)) { - if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) { - if (VD->hasInit()) - BaseType = resolveExprToType(VD->getInit()); - } - } - } - } + BaseType = simplifyType(BaseType, Base, ME->isArrow()); return resolveDependentMember(BaseType, ME->getMember(), NoFilter); } diff --git a/clang/unittests/Sema/HeuristicResolverTest.cpp b/clang/unittests/Sema/HeuristicResolverTest.cpp index 473eca669caabc..a0deb2d936756a 100644 --- a/clang/unittests/Sema/HeuristicResolverTest.cpp +++ b/clang/unittests/Sema/HeuristicResolverTest.cpp @@ -213,6 +213,48 @@ TEST(HeuristicResolver, MemberExpr_Chained) { cxxMethodDecl(hasName("foo")).bind("output")); } +TEST(HeuristicResolver, MemberExpr_ReferenceType) { + std::string Code = R"cpp( + struct B { + int waldo; + }; + template <typename T> + struct A { + B &b; + }; + template <typename T> + void foo(A<T> &a) { + a.b.waldo; + } + )cpp"; + // Test resolution of "waldo" in "a.b.waldo". + expectResolution( + Code, &HeuristicResolver::resolveMemberExpr, + cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"), + fieldDecl(hasName("waldo")).bind("output")); +} + +TEST(HeuristicResolver, MemberExpr_PointerType) { + std::string Code = R"cpp( + struct B { + int waldo; + }; + template <typename T> + struct A { + B *b; + }; + template <typename T> + void foo(A<T> &a) { + a.b->waldo; + } + )cpp"; + // Test resolution of "waldo" in "a.b->waldo". + expectResolution( + Code, &HeuristicResolver::resolveMemberExpr, + cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"), + fieldDecl(hasName("waldo")).bind("output")); +} + TEST(HeuristicResolver, MemberExpr_TemplateArgs) { std::string Code = R"cpp( struct Foo { _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits