[clang] 7fd8426 - [clang][Sema] Handle pointer and reference type more robustly in HeuristicResolver::resolveMemberExpr() (#124451)

via cfe-commits cfe-commits at lists.llvm.org
Wed Jan 29 22:54:34 PST 2025


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 {


        


More information about the cfe-commits mailing list