aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/Sema/SemaCodeComplete.cpp
diff options
context:
space:
mode:
authorSam McCall <sam.mccall@gmail.com>2021-02-10 00:11:35 +0100
committerSam McCall <sam.mccall@gmail.com>2021-02-11 11:03:40 +0100
commit5c55d3747b0cab17aaa07729cf90a49dbda71bbe (patch)
tree7c5b4ed4b5a7dbe09ebd59f6f776074f51b5ea4b /clang/lib/Sema/SemaCodeComplete.cpp
parenta874d182c61c7f8e5291c718b40be0e133bf21f0 (diff)
downloadllvm-5c55d3747b0cab17aaa07729cf90a49dbda71bbe.zip
llvm-5c55d3747b0cab17aaa07729cf90a49dbda71bbe.tar.gz
llvm-5c55d3747b0cab17aaa07729cf90a49dbda71bbe.tar.bz2
[CodeComplete] Member completion: heuristically resolve some dependent base exprs
Today, inside a template, you can get completion for: Foo<T> t; t.^ t has dependent type Foo<T>, and we use the primary template to find its members. However we also want this to work: t.foo.bar().^ The type of t.foo.bar() is DependentTy, so we attempt to resolve using similar heuristics (e.g. primary template). Differential Revision: https://reviews.llvm.org/D96376
Diffstat (limited to 'clang/lib/Sema/SemaCodeComplete.cpp')
-rw-r--r--clang/lib/Sema/SemaCodeComplete.cpp73
1 files changed, 71 insertions, 2 deletions
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp
index 2f5a28e..1e4d5dd 100644
--- a/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/clang/lib/Sema/SemaCodeComplete.cpp
@@ -5176,6 +5176,75 @@ private:
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 @@ void Sema::CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base,
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 @@ void Sema::CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base,
return false;
Base = ConvertedBase.get();
- QualType BaseType = Base->getType();
+ QualType BaseType = getApproximateType(Base);
if (BaseType.isNull())
return false;
ExprValueKind BaseKind = Base->getValueKind();