sammccall updated this revision to Diff 322681.
sammccall added a comment.

Also handle parens and unresolved calls


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D96376/new/

https://reviews.llvm.org/D96376

Files:
  clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
  clang/lib/Sema/SemaCodeComplete.cpp
  clang/test/CodeCompletion/member-access.cpp

Index: clang/test/CodeCompletion/member-access.cpp
===================================================================
--- clang/test/CodeCompletion/member-access.cpp
+++ clang/test/CodeCompletion/member-access.cpp
@@ -84,6 +84,9 @@
   T function() { }
   T field;
 
+  TemplateClass<S, T> &relatedField;
+  BaseTemplate<S> &relatedFunction();
+
   void overload1(const T &);
   void overload1(const S &);
 };
@@ -102,8 +105,12 @@
 // CHECK-CC2: overload1 : [#void#]overload1(<#const T &#>)
 // CHECK-CC2: overload1 : [#void#]overload1(<#const S &#>)
 
-// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:94:10 %s -o - | FileCheck -check-prefix=CHECK-CC2 %s
-// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:95:12 %s -o - | FileCheck -check-prefix=CHECK-CC2 %s
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:97:10 %s -o - | FileCheck -check-prefix=CHECK-CC2 %s
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:98:12 %s -o - | FileCheck -check-prefix=CHECK-CC2 %s
+
+  object.relatedField.relatedFunction().baseTemplateField;
+// CHECK-DEP-CHAIN: baseTemplateField : [#T#]baseTemplateField
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:111:41 %s -o - | FileCheck -check-prefix=CHECK-DEP-CHAIN %s
 }
 
 
@@ -120,8 +127,8 @@
 // CHECK-CC3: overload1 : [#void#]overload1(<#const int &#>)
 // CHECK-CC3: overload1 : [#void#]overload1(<#const double &#>)
 
-// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:112:10 %s -o - | FileCheck -check-prefix=CHECK-CC3 %s
-// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:113:12 %s -o - | FileCheck -check-prefix=CHECK-CC3 %s
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:119:10 %s -o - | FileCheck -check-prefix=CHECK-CC3 %s
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:120:12 %s -o - | FileCheck -check-prefix=CHECK-CC3 %s
 }
 
 template <typename T>
@@ -135,17 +142,17 @@
 // CHECK-CC4: BaseTemplate : BaseTemplate::
 // CHECK-CC4: baseTemplateField : [#int#]baseTemplateField
 // CHECK-CC4: baseTemplateFunction : [#int#]baseTemplateFunction()
-// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:134:8 %s -o - | FileCheck -check-prefix=CHECK-CC4 %s
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:141:8 %s -o - | FileCheck -check-prefix=CHECK-CC4 %s
     o2.baseTemplateField;
 // CHECK-CC5: BaseTemplate : BaseTemplate::
 // CHECK-CC5: baseTemplateField : [#T#]baseTemplateField
 // CHECK-CC5: baseTemplateFunction : [#T#]baseTemplateFunction()
-// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:139:8 %s -o - | FileCheck -check-prefix=CHECK-CC5 %s
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:146:8 %s -o - | FileCheck -check-prefix=CHECK-CC5 %s
     this->o1;
 // CHECK-CC6: [#void#]function()
 // CHECK-CC6: o1 : [#BaseTemplate<int>#]o1
 // CHECK-CC6: o2 : [#BaseTemplate<T>#]o2
-// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:144:11 %s -o - | FileCheck -check-prefix=CHECK-CC6 %s
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:151:11 %s -o - | FileCheck -check-prefix=CHECK-CC6 %s
   }
 
   static void staticFn(T &obj);
@@ -162,9 +169,9 @@
 // CHECK-CC7: o2 : [#BaseTemplate<T>#]o2
 // CHECK-CC7: staticFn : [#void#]staticFn(<#T &obj#>)
 // CHECK-CC7: Template : Template
-// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:158:16 %s -o - | FileCheck -check-prefix=CHECK-CC7 %s
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:165:16 %s -o - | FileCheck -check-prefix=CHECK-CC7 %s
   typename Template<T>::Nested m;
-// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:166:25 %s -o - | FileCheck -check-prefix=CHECK-CC7 %s
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:173:25 %s -o - | FileCheck -check-prefix=CHECK-CC7 %s
 }
 
 class Proxy2 {
@@ -181,34 +188,34 @@
   p.
 }
 
-// RUN: %clang_cc1 -fsyntax-only -code-completion-with-fixits -code-completion-at=%s:177:6 %s -o - | FileCheck -check-prefix=CHECK-CC8 --implicit-check-not="Derived : Derived(" %s
+// RUN: %clang_cc1 -fsyntax-only -code-completion-with-fixits -code-completion-at=%s:184:6 %s -o - | FileCheck -check-prefix=CHECK-CC8 --implicit-check-not="Derived : Derived(" %s
 // CHECK-CC8: Base1 (InBase) : Base1::
 // CHECK-CC8: member1 (InBase) : [#int#][#Base1::#]member1
 // CHECK-CC8: member1 (InBase) : [#int#][#Base2::#]member1
 // CHECK-CC8: member2 (InBase) : [#float#][#Base1::#]member2
 // CHECK-CC8: member3 (InBase) : [#double#][#Base2::#]member3
 // CHECK-CC8: member4 : [#int#]member4
-// CHECK-CC8: member5 : [#int#]member5 (requires fix-it: {177:4-177:6} to ".")
+// CHECK-CC8: member5 : [#int#]member5 (requires fix-it: {184:4-184:6} to ".")
 // CHECK-CC8: memfun1 (InBase) : [#void#][#Base3::#]memfun1(<#float#>)
 // CHECK-CC8: memfun1 (InBase) : [#void#][#Base3::#]memfun1(<#double#>)[# const#]
 // CHECK-CC8: memfun1 (Hidden,InBase) : [#void#]Base2::memfun1(<#int#>)
 // CHECK-CC8: memfun2 (InBase) : [#void#][#Base3::#]memfun2(<#int#>)
 // CHECK-CC8: memfun3 : [#int#]memfun3(<#int#>)
-// CHECK-CC8: operator-> : [#Derived *#]operator->()[# const#] (requires fix-it: {177:4-177:6} to ".")
+// CHECK-CC8: operator-> : [#Derived *#]operator->()[# const#] (requires fix-it: {184:4-184:6} to ".")
 
-// RUN: %clang_cc1 -fsyntax-only -code-completion-with-fixits -code-completion-at=%s:181:6 %s -o - | FileCheck -check-prefix=CHECK-CC9 --implicit-check-not="Derived : Derived(" %s
+// RUN: %clang_cc1 -fsyntax-only -code-completion-with-fixits -code-completion-at=%s:188:6 %s -o - | FileCheck -check-prefix=CHECK-CC9 --implicit-check-not="Derived : Derived(" %s
 // CHECK-CC9: Base1 (InBase) : Base1::
-// CHECK-CC9: member1 (InBase) : [#int#][#Base1::#]member1 (requires fix-it: {181:4-181:5} to "->")
-// CHECK-CC9: member1 (InBase) : [#int#][#Base2::#]member1 (requires fix-it: {181:4-181:5} to "->")
-// CHECK-CC9: member2 (InBase) : [#float#][#Base1::#]member2 (requires fix-it: {181:4-181:5} to "->")
-// CHECK-CC9: member3 (InBase) : [#double#][#Base2::#]member3 (requires fix-it: {181:4-181:5} to "->")
-// CHECK-CC9: member4 : [#int#]member4 (requires fix-it: {181:4-181:5} to "->")
+// CHECK-CC9: member1 (InBase) : [#int#][#Base1::#]member1 (requires fix-it: {188:4-188:5} to "->")
+// CHECK-CC9: member1 (InBase) : [#int#][#Base2::#]member1 (requires fix-it: {188:4-188:5} to "->")
+// CHECK-CC9: member2 (InBase) : [#float#][#Base1::#]member2 (requires fix-it: {188:4-188:5} to "->")
+// CHECK-CC9: member3 (InBase) : [#double#][#Base2::#]member3 (requires fix-it: {188:4-188:5} to "->")
+// CHECK-CC9: member4 : [#int#]member4 (requires fix-it: {188:4-188:5} to "->")
 // CHECK-CC9: member5 : [#int#]member5
-// CHECK-CC9: memfun1 (InBase) : [#void#][#Base3::#]memfun1(<#float#>) (requires fix-it: {181:4-181:5} to "->")
-// CHECK-CC9: memfun1 (InBase) : [#void#][#Base3::#]memfun1(<#double#>)[# const#] (requires fix-it: {181:4-181:5} to "->")
-// CHECK-CC9: memfun1 (Hidden,InBase) : [#void#]Base2::memfun1(<#int#>) (requires fix-it: {181:4-181:5} to "->")
-// CHECK-CC9: memfun2 (InBase) : [#void#][#Base3::#]memfun2(<#int#>) (requires fix-it: {181:4-181:5} to "->")
-// CHECK-CC9: memfun3 : [#int#]memfun3(<#int#>) (requires fix-it: {181:4-181:5} to "->")
+// CHECK-CC9: memfun1 (InBase) : [#void#][#Base3::#]memfun1(<#float#>) (requires fix-it: {188:4-188:5} to "->")
+// CHECK-CC9: memfun1 (InBase) : [#void#][#Base3::#]memfun1(<#double#>)[# const#] (requires fix-it: {188:4-188:5} to "->")
+// CHECK-CC9: memfun1 (Hidden,InBase) : [#void#]Base2::memfun1(<#int#>) (requires fix-it: {188:4-188:5} to "->")
+// CHECK-CC9: memfun2 (InBase) : [#void#][#Base3::#]memfun2(<#int#>) (requires fix-it: {188:4-188:5} to "->")
+// CHECK-CC9: memfun3 : [#int#]memfun3(<#int#>) (requires fix-it: {188:4-188:5} to "->")
 // CHECK-CC9: operator-> : [#Derived *#]operator->()[# const#]
 
 // These overload sets differ only by return type and this-qualifiers.
@@ -234,28 +241,28 @@
   static_cast<Overloads&&>(X).
 }
 
-// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:225:7 %s -o - | FileCheck -check-prefix=CHECK-LVALUE %s \
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:232:7 %s -o - | FileCheck -check-prefix=CHECK-LVALUE %s \
 // RUN: --implicit-check-not="[#int#]ConstOverload(" \
 // RUN: --implicit-check-not="[#double#]RefOverload(" \
 // RUN: --implicit-check-not="[#char#]RefOverload("
 // CHECK-LVALUE-DAG: [#double#]ConstOverload(
 // CHECK-LVALUE-DAG: [#int#]RefOverload(
 
-// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:228:12 %s -o - | FileCheck -check-prefix=CHECK-CONSTLVALUE %s \
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:235:12 %s -o - | FileCheck -check-prefix=CHECK-CONSTLVALUE %s \
 // RUN: --implicit-check-not="[#double#]ConstOverload(" \
 // RUN: --implicit-check-not="[#int#]RefOverload(" \
 // RUN: --implicit-check-not="[#char#]RefOverload("
 // CHECK-CONSTLVALUE: [#int#]ConstOverload(
 // CHECK-CONSTLVALUE: [#double#]RefOverload(
 
-// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:231:15 %s -o - | FileCheck -check-prefix=CHECK-PRVALUE %s \
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:238:15 %s -o - | FileCheck -check-prefix=CHECK-PRVALUE %s \
 // RUN: --implicit-check-not="[#int#]ConstOverload(" \
 // RUN: --implicit-check-not="[#int#]RefOverload(" \
 // RUN: --implicit-check-not="[#double#]RefOverload("
 // CHECK-PRVALUE: [#double#]ConstOverload(
 // CHECK-PRVALUE: [#char#]RefOverload(
 
-// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:234:31 %s -o - | FileCheck -check-prefix=CHECK-XVALUE %s \
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:241:31 %s -o - | FileCheck -check-prefix=CHECK-XVALUE %s \
 // RUN: --implicit-check-not="[#int#]ConstOverload(" \
 // RUN: --implicit-check-not="[#int#]RefOverload(" \
 // RUN: --implicit-check-not="[#double#]RefOverload("
@@ -269,7 +276,7 @@
   } s;
   return s.
 }
-// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:270:12 %s -o - | FileCheck -check-prefix=CHECK-OPER %s \
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:277:12 %s -o - | FileCheck -check-prefix=CHECK-OPER %s \
 // RUN: --implicit-check-not="[#char#]operator=("
 // CHECK-OPER: [#int#]operator=(
 
@@ -280,5 +287,12 @@
   // No overload matches, but we have recovery-expr with the correct type.
   overloaded().
 }
-// RUN: not %clang_cc1 -fsyntax-only -frecovery-ast -frecovery-ast-type -code-completion-at=%s:281:16 %s -o - | FileCheck -check-prefix=CHECK-RECOVERY %s
+// RUN: not %clang_cc1 -fsyntax-only -frecovery-ast -frecovery-ast-type -code-completion-at=%s:288:16 %s -o - | FileCheck -check-prefix=CHECK-RECOVERY %s
 // CHECK-RECOVERY: [#int#]member
+template <typename T>
+void fooDependent(T t) {
+  // Overload not resolved, but we notice all candidates return the same type.
+  overloaded(t).
+}
+// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:295:17 %s -o - | FileCheck -check-prefix=CHECK-OVERLOAD %s
+// CHECK-OVERLOAD: [#int#]member
Index: clang/lib/Sema/SemaCodeComplete.cpp
===================================================================
--- clang/lib/Sema/SemaCodeComplete.cpp
+++ clang/lib/Sema/SemaCodeComplete.cpp
@@ -5176,6 +5176,75 @@
 
   llvm::DenseMap<const IdentifierInfo *, Member> Results;
 };
+
+// Returns a type for E that yields acceptable member completions.
+// In particular, when E->getType() is DependentTy, try to guess a likely type.
+// We accept some lossiness (like dropping parameters).
+// We only try to handle common expressions on the LHS of MemberExpr.
+QualType getApproximateType(const Expr *E) {
+  QualType Unresolved = E->getType();
+  if (Unresolved.isNull() ||
+      !Unresolved->isSpecificBuiltinType(BuiltinType::Dependent))
+    return Unresolved;
+  E = E->IgnoreParens();
+  // A call: approximate-resolve callee to a function type, get its return type
+  if (const CallExpr *CE = llvm::dyn_cast<CallExpr>(E)) {
+    QualType Callee = getApproximateType(CE->getCallee());
+    if (Callee.isNull() ||
+        Callee->isSpecificPlaceholderType(BuiltinType::BoundMember))
+      Callee = Expr::findBoundMemberType(CE->getCallee());
+    if (Callee.isNull())
+      return Unresolved;
+
+    if (const auto *FnTypePtr = Callee->getAs<PointerType>()) {
+      Callee = FnTypePtr->getPointeeType();
+    } else if (const auto *BPT = Callee->getAs<BlockPointerType>()) {
+      Callee = BPT->getPointeeType();
+    }
+    if (const FunctionType *FnType = Callee->getAs<FunctionType>())
+      return FnType->getReturnType().getNonReferenceType();
+
+    // Unresolved call: try to guess the return type.
+    if (const auto *OE = llvm::dyn_cast<OverloadExpr>(CE->getCallee())) {
+      // If all candidates have the same approximate return type, use it.
+      // Discard references and const to allow more to be "the same".
+      // (In particular, if there's one candidate + ADL, resolve it).
+      const Type *Common = nullptr;
+      for (const auto *D : OE->decls()) {
+        QualType ReturnType;
+        if (const auto *FD = llvm::dyn_cast<FunctionDecl>(D))
+          ReturnType = FD->getReturnType();
+        else if (const auto *FTD = llvm::dyn_cast<FunctionTemplateDecl>(D))
+          ReturnType = FTD->getTemplatedDecl()->getReturnType();
+        if (ReturnType.isNull())
+          continue;
+        const Type *Candidate =
+            ReturnType.getNonReferenceType().getCanonicalType().getTypePtr();
+        if (Common && Common != Candidate)
+          return Unresolved; // Multiple candidates.
+        Common = Candidate;
+      }
+      if (Common != nullptr)
+        return QualType(Common, 0);
+    }
+  }
+  // A dependent member: approximate-resolve the base, then lookup.
+  if (const auto *CDSME = llvm::dyn_cast<CXXDependentScopeMemberExpr>(E)) {
+    QualType Base = CDSME->isImplicitAccess()
+                        ? CDSME->getBaseType()
+                        : getApproximateType(CDSME->getBase());
+    if (CDSME->isArrow() && !Base.isNull())
+      Base = Base->getPointeeType(); // could handle unique_ptr etc here?
+    RecordDecl *RD = Base.isNull() ? nullptr : getAsRecordDecl(Base);
+    if (RD && RD->isCompleteDefinition()) {
+      for (const auto &Member : RD->lookup(CDSME->getMember()))
+        if (const ValueDecl *VD = llvm::dyn_cast<ValueDecl>(Member))
+          return VD->getType().getNonReferenceType();
+    }
+  }
+  return Unresolved;
+}
+
 } // namespace
 
 void Sema::CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base,
@@ -5198,7 +5267,7 @@
   ExprResult ConvertedBase = PerformMemberExprBaseConversion(Base, IsArrow);
   if (ConvertedBase.isInvalid())
     return;
-  QualType ConvertedBaseType = ConvertedBase.get()->getType();
+  QualType ConvertedBaseType = getApproximateType(ConvertedBase.get());
 
   enum CodeCompletionContext::Kind contextKind;
 
@@ -5234,7 +5303,7 @@
       return false;
     Base = ConvertedBase.get();
 
-    QualType BaseType = Base->getType();
+    QualType BaseType = getApproximateType(Base);
     if (BaseType.isNull())
       return false;
     ExprValueKind BaseKind = Base->getValueKind();
Index: clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
+++ clang-tools-extra/clangd/unittests/CodeCompleteTests.cpp
@@ -1098,6 +1098,20 @@
   EXPECT_THAT(Completions, Contains(Named("TT")));
 }
 
+TEST(CompletionTest, NestedTemplateHeuristics) {
+  auto Completions = completions(R"cpp(
+struct Plain { int xxx; };
+template <typename T> class Templ { Plain ppp; };
+template <typename T> void foo(Templ<T> &t) {
+  // Formally ppp has DependentTy, because Templ may be specialized.
+  // However we sholud be able to see into it using the primary template.
+  t.ppp.^
+}
+)cpp")
+                         .Completions;
+  EXPECT_THAT(Completions, Contains(Named("xxx")));
+}
+
 TEST(CompletionTest, RecordCCResultCallback) {
   std::vector<CodeCompletion> RecordedCompletions;
   CodeCompleteOptions Opts;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to