diff options
author | Sam McCall <sam.mccall@gmail.com> | 2021-02-10 00:11:35 +0100 |
---|---|---|
committer | Sam McCall <sam.mccall@gmail.com> | 2021-02-11 11:03:40 +0100 |
commit | 5c55d3747b0cab17aaa07729cf90a49dbda71bbe (patch) | |
tree | 7c5b4ed4b5a7dbe09ebd59f6f776074f51b5ea4b /clang/lib/Sema/SemaCodeComplete.cpp | |
parent | a874d182c61c7f8e5291c718b40be0e133bf21f0 (diff) | |
download | llvm-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.cpp | 73 |
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(); |