diff --git a/clang/lib/Sema/HeuristicResolver.cpp b/clang/lib/Sema/HeuristicResolver.cpp index 2a726fe51d355..e893afed71d26 100644 --- a/clang/lib/Sema/HeuristicResolver.cpp +++ b/clang/lib/Sema/HeuristicResolver.cpp @@ -227,6 +227,7 @@ std::vector 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); @@ -237,11 +238,25 @@ std::vector HeuristicResolverImpl::resolveMemberExpr( // 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. - Expr *Base = ME->isImplicitAccess() ? nullptr : ME->getBase(); if (Base && BT->getKind() == BuiltinType::Dependent) { BaseType = resolveExprToType(Base); } } + 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(Base)) { + if (const auto *VD = dyn_cast(DRE->getDecl())) { + if (VD->hasInit()) + BaseType = resolveExprToType(VD->getInit()); + } + } + } + } return resolveDependentMember(BaseType, ME->getMember(), NoFilter); } diff --git a/clang/unittests/Sema/HeuristicResolverTest.cpp b/clang/unittests/Sema/HeuristicResolverTest.cpp index 2cd5486b3227f..2b775b11719ea 100644 --- a/clang/unittests/Sema/HeuristicResolverTest.cpp +++ b/clang/unittests/Sema/HeuristicResolverTest.cpp @@ -155,6 +155,46 @@ TEST(HeuristicResolver, MemberExpr_SmartPointer_Qualified) { cxxMethodDecl(hasName("find"), isConst()).bind("output")); } +TEST(HeuristicResolver, MemberExpr_AutoTypeDeduction1) { + std::string Code = R"cpp( + template + struct A { + int waldo; + }; + template + void foo(A a) { + auto copy = a; + copy.waldo; + } + )cpp"; + expectResolution( + Code, &HeuristicResolver::resolveMemberExpr, + cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"), + fieldDecl(hasName("waldo")).bind("output")); +} + +TEST(HeuristicResolver, MemberExpr_AutoTypeDeduction2) { + std::string Code = R"cpp( + struct B { + int waldo; + }; + + template + struct A { + B b; + }; + template + void foo(A a) { + auto b = a.b; + b.waldo; + } + )cpp"; + expectResolution( + Code, &HeuristicResolver::resolveMemberExpr, + cxxDependentScopeMemberExpr(hasMemberName("waldo")).bind("input"), + fieldDecl(hasName("waldo")).bind("output")); +} + TEST(HeuristicResolver, MemberExpr_Chained) { std::string Code = R"cpp( struct A { void foo() {} };