diff options
author | Francisco Lopes da Silva <oblita@gmail.com> | 2015-01-21 16:24:11 +0000 |
---|---|---|
committer | Francisco Lopes da Silva <oblita@gmail.com> | 2015-01-21 16:24:11 +0000 |
commit | 975a9f6ecea606c2af7b16f489893958badcad30 (patch) | |
tree | e7d6242fab5855cb423c0b825770f55b39fe5e44 /clang/lib/Sema/SemaCodeComplete.cpp | |
parent | 3f02c14cc742180a27f5c99a998e3c5d1ac2f6c8 (diff) | |
download | llvm-975a9f6ecea606c2af7b16f489893958badcad30.zip llvm-975a9f6ecea606c2af7b16f489893958badcad30.tar.gz llvm-975a9f6ecea606c2af7b16f489893958badcad30.tar.bz2 |
Initial support for C++ parameter completion
The improved completion in call context now works with:
- Functions.
- Member functions.
- Constructors.
- New expressions.
- Function call expressions.
- Template variants of the previous.
There are still rough edges to be fixed:
- Provide support for optional parameters. (fix known)
- Provide support for member initializers. (fix known)
- Provide support for variadic template functions. (fix unknown)
- Others?
llvm-svn: 226670
Diffstat (limited to 'clang/lib/Sema/SemaCodeComplete.cpp')
-rw-r--r-- | clang/lib/Sema/SemaCodeComplete.cpp | 226 |
1 files changed, 150 insertions, 76 deletions
diff --git a/clang/lib/Sema/SemaCodeComplete.cpp b/clang/lib/Sema/SemaCodeComplete.cpp index 384972b..b3059d5 100644 --- a/clang/lib/Sema/SemaCodeComplete.cpp +++ b/clang/lib/Sema/SemaCodeComplete.cpp @@ -3819,7 +3819,73 @@ static bool anyNullArguments(ArrayRef<Expr *> Args) { return false; } -void Sema::CodeCompleteCall(Scope *S, Expr *FnIn, ArrayRef<Expr *> Args) { +typedef CodeCompleteConsumer::OverloadCandidate ResultCandidate; + +void mergeCandidatesWithResults(Sema &SemaRef, + SmallVectorImpl<ResultCandidate> &Results, + OverloadCandidateSet &CandidateSet, + SourceLocation Loc) { + if (!CandidateSet.empty()) { + // Sort the overload candidate set by placing the best overloads first. + std::stable_sort( + CandidateSet.begin(), CandidateSet.end(), + [&](const OverloadCandidate &X, const OverloadCandidate &Y) { + return isBetterOverloadCandidate(SemaRef, X, Y, Loc); + }); + + // Add the remaining viable overload candidates as code-completion results. + for (auto &Candidate : CandidateSet) + if (Candidate.Viable) + Results.push_back(ResultCandidate(Candidate.Function)); + } +} + +/// \brief Get the type of the Nth parameter from a given set of overload +/// candidates. +QualType getParamType(Sema &SemaRef, ArrayRef<ResultCandidate> Candidates, + unsigned N) { + + // Given the overloads 'Candidates' for a function call matching all arguments + // up to N, return the type of the Nth parameter if it is the same for all + // overload candidates. + QualType ParamType; + for (auto &Candidate : Candidates) { + if (auto FType = Candidate.getFunctionType()) + if (auto Proto = dyn_cast<FunctionProtoType>(FType)) + if (N < Proto->getNumParams()) { + if (ParamType.isNull()) + ParamType = Proto->getParamType(N); + else if (!SemaRef.Context.hasSameUnqualifiedType( + ParamType.getNonReferenceType(), + Proto->getParamType(N).getNonReferenceType())) + // Otherwise return a default-constructed QualType. + return QualType(); + } + } + + return ParamType; +} + +void CodeCompleteOverloadResults(Sema &SemaRef, Scope *S, + MutableArrayRef<ResultCandidate> Candidates, + unsigned CurrentArg, + bool CompleteExpressionWithCurrentArg = true) { + QualType ParamType; + if (CompleteExpressionWithCurrentArg) + ParamType = getParamType(SemaRef, Candidates, CurrentArg); + + if (ParamType.isNull()) + SemaRef.CodeCompleteOrdinaryName(S, Sema::PCC_Expression); + else + SemaRef.CodeCompleteExpression(S, ParamType); + + if (!Candidates.empty()) + SemaRef.CodeCompleter->ProcessOverloadCandidates(SemaRef, CurrentArg, + Candidates.data(), + Candidates.size()); +} + +void Sema::CodeCompleteCall(Scope *S, Expr *Fn, ArrayRef<Expr *> Args) { if (!CodeCompleter) return; @@ -3828,7 +3894,8 @@ void Sema::CodeCompleteCall(Scope *S, Expr *FnIn, ArrayRef<Expr *> Args) { // results. We may want to revisit this strategy in the future, // e.g., by merging the two kinds of results. - Expr *Fn = (Expr *)FnIn; + // FIXME: Provide support for highlighting optional parameters. + // FIXME: Provide support for variadic template functions. // Ignore type-dependent call expressions entirely. if (!Fn || Fn->isTypeDependent() || anyNullArguments(Args) || @@ -3841,92 +3908,99 @@ void Sema::CodeCompleteCall(Scope *S, Expr *FnIn, ArrayRef<Expr *> Args) { SourceLocation Loc = Fn->getExprLoc(); OverloadCandidateSet CandidateSet(Loc, OverloadCandidateSet::CSK_Normal); - // FIXME: What if we're calling something that isn't a function declaration? - // FIXME: What if we're calling a pseudo-destructor? - // FIXME: What if we're calling a member function? - - typedef CodeCompleteConsumer::OverloadCandidate ResultCandidate; SmallVector<ResultCandidate, 8> Results; Expr *NakedFn = Fn->IgnoreParenCasts(); - if (UnresolvedLookupExpr *ULE = dyn_cast<UnresolvedLookupExpr>(NakedFn)) + if (auto ULE = dyn_cast<UnresolvedLookupExpr>(NakedFn)) AddOverloadedCallCandidates(ULE, Args, CandidateSet, - /*PartialOverloading=*/ true); - else if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(NakedFn)) { - FunctionDecl *FDecl = dyn_cast<FunctionDecl>(DRE->getDecl()); - if (FDecl) { + /*PartialOverloading=*/true); + else if (auto UME = dyn_cast<UnresolvedMemberExpr>(NakedFn)) { + TemplateArgumentListInfo TemplateArgsBuffer, *TemplateArgs = nullptr; + if (UME->hasExplicitTemplateArgs()) { + UME->copyTemplateArgumentsInto(TemplateArgsBuffer); + TemplateArgs = &TemplateArgsBuffer; + } + SmallVector<Expr *, 12> ArgExprs(1, UME->getBase()); + ArgExprs.append(Args.begin(), Args.end()); + UnresolvedSet<8> Decls; + Decls.append(UME->decls_begin(), UME->decls_end()); + AddFunctionCandidates(Decls, ArgExprs, CandidateSet, TemplateArgs, + /*SuppressUsedConversions=*/false, + /*PartialOverloading=*/true); + } else if (auto DC = NakedFn->getType()->getCanonicalTypeInternal() + ->getAsCXXRecordDecl()) { + // If it's a CXXRecordDecl, it may overload the function call operator, + // so we check if it does and add them as candidates. + DeclarationName OpName = Context.DeclarationNames + .getCXXOperatorName(OO_Call); + LookupResult R(*this, OpName, Loc, LookupOrdinaryName); + LookupQualifiedName(R, DC); + R.suppressDiagnostics(); + SmallVector<Expr *, 12> ArgExprs(1, NakedFn); + ArgExprs.append(Args.begin(), Args.end()); + AddFunctionCandidates(R.asUnresolvedSet(), ArgExprs, CandidateSet, + /*ExplicitArgs=*/nullptr, + /*SuppressUsedConversions=*/false, + /*PartialOverloading=*/true); + } else { + // Lastly we check, as a possibly resolved expression, whether it can be + // converted to a function. + FunctionDecl *FD = nullptr; + if (auto MCE = dyn_cast<MemberExpr>(NakedFn)) + FD = dyn_cast<FunctionDecl>(MCE->getMemberDecl()); + else if (auto DRE = dyn_cast<DeclRefExpr>(NakedFn)) + FD = dyn_cast<FunctionDecl>(DRE->getDecl()); + if (FD) { if (!getLangOpts().CPlusPlus || - !FDecl->getType()->getAs<FunctionProtoType>()) - Results.push_back(ResultCandidate(FDecl)); + !FD->getType()->getAs<FunctionProtoType>()) + Results.push_back(ResultCandidate(FD)); else - // FIXME: access? - AddOverloadCandidate(FDecl, DeclAccessPair::make(FDecl, AS_none), Args, - CandidateSet, false, /*PartialOverloading*/true); + AddOverloadCandidate(FD, DeclAccessPair::make(FD, FD->getAccess()), + Args, CandidateSet, + /*SuppressUsedConversions=*/false, + /*PartialOverloading=*/true); } } - - QualType ParamType; - - if (!CandidateSet.empty()) { - // Sort the overload candidate set by placing the best overloads first. - std::stable_sort( - CandidateSet.begin(), CandidateSet.end(), - [&](const OverloadCandidate &X, const OverloadCandidate &Y) { - return isBetterOverloadCandidate(*this, X, Y, Loc); - }); - // Add the remaining viable overload candidates as code-completion reslults. - for (OverloadCandidateSet::iterator Cand = CandidateSet.begin(), - CandEnd = CandidateSet.end(); - Cand != CandEnd; ++Cand) { - if (Cand->Viable) - Results.push_back(ResultCandidate(Cand->Function)); - } - - // From the viable candidates, try to determine the type of this parameter. - for (unsigned I = 0, N = Results.size(); I != N; ++I) { - if (const FunctionType *FType = Results[I].getFunctionType()) - if (const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FType)) - if (Args.size() < Proto->getNumParams()) { - if (ParamType.isNull()) - ParamType = Proto->getParamType(Args.size()); - else if (!Context.hasSameUnqualifiedType( - ParamType.getNonReferenceType(), - Proto->getParamType(Args.size()) - .getNonReferenceType())) { - ParamType = QualType(); - break; - } - } - } - } else { - // Try to determine the parameter type from the type of the expression - // being called. - QualType FunctionType = Fn->getType(); - if (const PointerType *Ptr = FunctionType->getAs<PointerType>()) - FunctionType = Ptr->getPointeeType(); - else if (const BlockPointerType *BlockPtr - = FunctionType->getAs<BlockPointerType>()) - FunctionType = BlockPtr->getPointeeType(); - else if (const MemberPointerType *MemPtr - = FunctionType->getAs<MemberPointerType>()) - FunctionType = MemPtr->getPointeeType(); - - if (const FunctionProtoType *Proto - = FunctionType->getAs<FunctionProtoType>()) { - if (Args.size() < Proto->getNumParams()) - ParamType = Proto->getParamType(Args.size()); + mergeCandidatesWithResults(*this, Results, CandidateSet, Loc); + CodeCompleteOverloadResults(*this, S, Results, Args.size(), + !CandidateSet.empty()); +} + +void Sema::CodeCompleteConstructor(Scope *S, QualType Type, SourceLocation Loc, + ArrayRef<Expr *> Args) { + if (!CodeCompleter) + return; + + // A complete type is needed to lookup for constructors. + if (RequireCompleteType(Loc, Type, 0)) + return; + + // FIXME: Provide support for member initializers. + // FIXME: Provide support for variadic template constructors. + // FIXME: Provide support for highlighting optional parameters. + + OverloadCandidateSet CandidateSet(Loc, OverloadCandidateSet::CSK_Normal); + + for (auto C : LookupConstructors(Type->getAsCXXRecordDecl())) { + if (auto FD = dyn_cast<FunctionDecl>(C)) { + AddOverloadCandidate(FD, DeclAccessPair::make(FD, C->getAccess()), + Args, CandidateSet, + /*SuppressUsedConversions=*/false, + /*PartialOverloading=*/true); + } else if (auto FTD = dyn_cast<FunctionTemplateDecl>(C)) { + AddTemplateOverloadCandidate(FTD, + DeclAccessPair::make(FTD, C->getAccess()), + /*ExplicitTemplateArgs=*/nullptr, + Args, CandidateSet, + /*SuppressUsedConversions=*/false, + /*PartialOverloading=*/true); } } - if (ParamType.isNull()) - CodeCompleteOrdinaryName(S, PCC_Expression); - else - CodeCompleteExpression(S, ParamType); - - if (!Results.empty()) - CodeCompleter->ProcessOverloadCandidates(*this, Args.size(), Results.data(), - Results.size()); + SmallVector<ResultCandidate, 8> Results; + mergeCandidatesWithResults(*this, Results, CandidateSet, Loc); + CodeCompleteOverloadResults(*this, S, Results, Args.size()); } void Sema::CodeCompleteInitializer(Scope *S, Decl *D) { |