aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/clang/lib/Sema/SemaCodeComplete.cpp
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2020-07-31 21:22:58 +0000
committerDimitry Andric <dim@FreeBSD.org>2020-07-31 21:22:58 +0000
commit5ffd83dbcc34f10e07f6d3e968ae6365869615f4 (patch)
tree0e9f5cf729dde39f949698fddef45a34e2bc7f44 /contrib/llvm-project/clang/lib/Sema/SemaCodeComplete.cpp
parent1799696096df87b52968b8996d00c91e0a5de8d9 (diff)
parentcfca06d7963fa0909f90483b42a6d7d194d01e08 (diff)
Notes
Diffstat (limited to 'contrib/llvm-project/clang/lib/Sema/SemaCodeComplete.cpp')
-rw-r--r--contrib/llvm-project/clang/lib/Sema/SemaCodeComplete.cpp782
1 files changed, 669 insertions, 113 deletions
diff --git a/contrib/llvm-project/clang/lib/Sema/SemaCodeComplete.cpp b/contrib/llvm-project/clang/lib/Sema/SemaCodeComplete.cpp
index 7260977c634d..0a8a27068ebf 100644
--- a/contrib/llvm-project/clang/lib/Sema/SemaCodeComplete.cpp
+++ b/contrib/llvm-project/clang/lib/Sema/SemaCodeComplete.cpp
@@ -9,25 +9,36 @@
// This file defines the code-completion semantic actions.
//
//===----------------------------------------------------------------------===//
+#include "clang/AST/ASTConcept.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprConcepts.h"
#include "clang/AST/ExprObjC.h"
+#include "clang/AST/NestedNameSpecifier.h"
#include "clang/AST/QualTypeNames.h"
+#include "clang/AST/RecursiveASTVisitor.h"
#include "clang/AST/Type.h"
#include "clang/Basic/CharInfo.h"
+#include "clang/Basic/OperatorKinds.h"
#include "clang/Basic/Specifiers.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/MacroInfo.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/CodeCompleteConsumer.h"
+#include "clang/Sema/DeclSpec.h"
+#include "clang/Sema/Designator.h"
#include "clang/Sema/Lookup.h"
#include "clang/Sema/Overload.h"
#include "clang/Sema/Scope.h"
#include "clang/Sema/ScopeInfo.h"
+#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaInternal.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/SmallPtrSet.h"
@@ -36,7 +47,9 @@
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/Twine.h"
#include "llvm/ADT/iterator_range.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
#include <list>
#include <map>
#include <string>
@@ -1676,11 +1689,9 @@ static void AddTypeSpecifierResults(const LangOptions &LangOpts,
Results.AddResult(Result("class", CCP_Type));
Results.AddResult(Result("wchar_t", CCP_Type));
- // typename qualified-id
+ // typename name
Builder.AddTypedTextChunk("typename");
Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
- Builder.AddPlaceholderChunk("qualifier");
- Builder.AddTextChunk("::");
Builder.AddPlaceholderChunk("name");
Results.AddResult(Result(Builder.TakeString()));
@@ -1807,6 +1818,18 @@ static void AddTypedefResult(ResultBuilder &Results) {
Results.AddResult(CodeCompletionResult(Builder.TakeString()));
}
+// using name = type
+static void AddUsingAliasResult(CodeCompletionBuilder &Builder,
+ ResultBuilder &Results) {
+ Builder.AddTypedTextChunk("using");
+ Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
+ Builder.AddPlaceholderChunk("name");
+ Builder.AddChunk(CodeCompletionString::CK_Equal);
+ Builder.AddPlaceholderChunk("type");
+ Builder.AddChunk(CodeCompletionString::CK_SemiColon);
+ Results.AddResult(CodeCompletionResult(Builder.TakeString()));
+}
+
static bool WantTypesInContext(Sema::ParserCompletionContext CCC,
const LangOptions &LangOpts) {
switch (CCC) {
@@ -2050,6 +2073,9 @@ static void AddOrdinaryNameResults(Sema::ParserCompletionContext CCC, Scope *S,
Builder.AddChunk(CodeCompletionString::CK_SemiColon);
Results.AddResult(Result(Builder.TakeString()));
+ if (SemaRef.getLangOpts().CPlusPlus11)
+ AddUsingAliasResult(Builder, Results);
+
// using typename qualifier::name (only in a dependent context)
if (SemaRef.CurContext->isDependentContext()) {
Builder.AddTypedTextChunk("using typename");
@@ -2130,6 +2156,9 @@ static void AddOrdinaryNameResults(Sema::ParserCompletionContext CCC, Scope *S,
case Sema::PCC_RecoveryInFunction:
case Sema::PCC_Statement: {
+ if (SemaRef.getLangOpts().CPlusPlus11)
+ AddUsingAliasResult(Builder, Results);
+
AddTypedefResult(Results);
if (SemaRef.getLangOpts().CPlusPlus && Results.includeCodePatterns() &&
@@ -2748,7 +2777,7 @@ FormatFunctionParameter(const PrintingPolicy &Policy, const ParmVarDecl *Param,
std::string Result;
if (Param->getIdentifier() && !ObjCMethodParam && !SuppressName)
- Result = Param->getIdentifier()->getName();
+ Result = std::string(Param->getIdentifier()->getName());
QualType Type = Param->getType();
if (ObjCSubsts)
@@ -2787,7 +2816,7 @@ FormatFunctionParameter(const PrintingPolicy &Policy, const ParmVarDecl *Param,
// for the block; just use the parameter type as a placeholder.
std::string Result;
if (!ObjCMethodParam && Param->getIdentifier())
- Result = Param->getIdentifier()->getName();
+ Result = std::string(Param->getIdentifier()->getName());
QualType Type = Param->getType().getUnqualifiedType();
@@ -3002,7 +3031,7 @@ static void AddTemplateParameterChunks(
} else if (NonTypeTemplateParmDecl *NTTP =
dyn_cast<NonTypeTemplateParmDecl>(*P)) {
if (NTTP->getIdentifier())
- PlaceholderStr = NTTP->getIdentifier()->getName();
+ PlaceholderStr = std::string(NTTP->getIdentifier()->getName());
NTTP->getType().getAsStringInternal(PlaceholderStr, Policy);
HasDefaultArg = NTTP->hasDefaultArgument();
} else {
@@ -3705,8 +3734,11 @@ CodeCompleteConsumer::OverloadCandidate::CreateSignatureString(
Result.addBriefComment(RC->getBriefText(S.getASTContext()));
}
AddResultTypeChunk(S.Context, Policy, FDecl, QualType(), Result);
- Result.AddTextChunk(
- Result.getAllocator().CopyString(FDecl->getNameAsString()));
+
+ std::string Name;
+ llvm::raw_string_ostream OS(Name);
+ FDecl->getDeclName().print(OS, Policy);
+ Result.AddTextChunk(Result.getAllocator().CopyString(OS.str()));
} else {
Result.AddResultTypeChunk(Result.getAllocator().CopyString(
Proto->getReturnType().getAsString(Policy)));
@@ -4329,7 +4361,7 @@ static void AddLambdaCompletion(ResultBuilder &Results,
First = false;
constexpr llvm::StringLiteral NamePlaceholder = "!#!NAME_GOES_HERE!#!";
- std::string Type = NamePlaceholder;
+ std::string Type = std::string(NamePlaceholder);
Parameter.getAsStringInternal(Type, PrintingPolicy(LangOpts));
llvm::StringRef Prefix, Suffix;
std::tie(Prefix, Suffix) = llvm::StringRef(Type).split(NamePlaceholder);
@@ -4719,6 +4751,386 @@ static void AddRecordMembersCompletionResults(
}
}
+// Returns the RecordDecl inside the BaseType, falling back to primary template
+// in case of specializations. Since we might not have a decl for the
+// instantiation/specialization yet, e.g. dependent code.
+static RecordDecl *getAsRecordDecl(const QualType BaseType) {
+ if (auto *RD = BaseType->getAsRecordDecl())
+ return RD;
+
+ if (const auto *TST = BaseType->getAs<TemplateSpecializationType>()) {
+ if (const auto *TD = dyn_cast_or_null<ClassTemplateDecl>(
+ TST->getTemplateName().getAsTemplateDecl())) {
+ return TD->getTemplatedDecl();
+ }
+ }
+
+ return nullptr;
+}
+
+namespace {
+// Collects completion-relevant information about a concept-constrainted type T.
+// In particular, examines the constraint expressions to find members of T.
+//
+// The design is very simple: we walk down each constraint looking for
+// expressions of the form T.foo().
+// If we're extra lucky, the return type is specified.
+// We don't do any clever handling of && or || in constraint expressions, we
+// take members from both branches.
+//
+// For example, given:
+// template <class T> concept X = requires (T t, string& s) { t.print(s); };
+// template <X U> void foo(U u) { u.^ }
+// We want to suggest the inferred member function 'print(string)'.
+// We see that u has type U, so X<U> holds.
+// X<U> requires t.print(s) to be valid, where t has type U (substituted for T).
+// By looking at the CallExpr we find the signature of print().
+//
+// While we tend to know in advance which kind of members (access via . -> ::)
+// we want, it's simpler just to gather them all and post-filter.
+//
+// FIXME: some of this machinery could be used for non-concept type-parms too,
+// enabling completion for type parameters based on other uses of that param.
+//
+// FIXME: there are other cases where a type can be constrained by a concept,
+// e.g. inside `if constexpr(ConceptSpecializationExpr) { ... }`
+class ConceptInfo {
+public:
+ // Describes a likely member of a type, inferred by concept constraints.
+ // Offered as a code completion for T. T-> and T:: contexts.
+ struct Member {
+ // Always non-null: we only handle members with ordinary identifier names.
+ const IdentifierInfo *Name = nullptr;
+ // Set for functions we've seen called.
+ // We don't have the declared parameter types, only the actual types of
+ // arguments we've seen. These are still valuable, as it's hard to render
+ // a useful function completion with neither parameter types nor names!
+ llvm::Optional<SmallVector<QualType, 1>> ArgTypes;
+ // Whether this is accessed as T.member, T->member, or T::member.
+ enum AccessOperator {
+ Colons,
+ Arrow,
+ Dot,
+ } Operator = Dot;
+ // What's known about the type of a variable or return type of a function.
+ const TypeConstraint *ResultType = nullptr;
+ // FIXME: also track:
+ // - kind of entity (function/variable/type), to expose structured results
+ // - template args kinds/types, as a proxy for template params
+
+ // For now we simply return these results as "pattern" strings.
+ CodeCompletionString *render(Sema &S, CodeCompletionAllocator &Alloc,
+ CodeCompletionTUInfo &Info) const {
+ CodeCompletionBuilder B(Alloc, Info);
+ // Result type
+ if (ResultType) {
+ std::string AsString;
+ {
+ llvm::raw_string_ostream OS(AsString);
+ QualType ExactType = deduceType(*ResultType);
+ if (!ExactType.isNull())
+ ExactType.print(OS, getCompletionPrintingPolicy(S));
+ else
+ ResultType->print(OS, getCompletionPrintingPolicy(S));
+ }
+ B.AddResultTypeChunk(Alloc.CopyString(AsString));
+ }
+ // Member name
+ B.AddTypedTextChunk(Alloc.CopyString(Name->getName()));
+ // Function argument list
+ if (ArgTypes) {
+ B.AddChunk(clang::CodeCompletionString::CK_LeftParen);
+ bool First = true;
+ for (QualType Arg : *ArgTypes) {
+ if (First)
+ First = false;
+ else {
+ B.AddChunk(clang::CodeCompletionString::CK_Comma);
+ B.AddChunk(clang::CodeCompletionString::CK_HorizontalSpace);
+ }
+ B.AddPlaceholderChunk(Alloc.CopyString(
+ Arg.getAsString(getCompletionPrintingPolicy(S))));
+ }
+ B.AddChunk(clang::CodeCompletionString::CK_RightParen);
+ }
+ return B.TakeString();
+ }
+ };
+
+ // BaseType is the type parameter T to infer members from.
+ // T must be accessible within S, as we use it to find the template entity
+ // that T is attached to in order to gather the relevant constraints.
+ ConceptInfo(const TemplateTypeParmType &BaseType, Scope *S) {
+ auto *TemplatedEntity = getTemplatedEntity(BaseType.getDecl(), S);
+ for (const Expr *E : constraintsForTemplatedEntity(TemplatedEntity))
+ believe(E, &BaseType);
+ }
+
+ std::vector<Member> members() {
+ std::vector<Member> Results;
+ for (const auto &E : this->Results)
+ Results.push_back(E.second);
+ llvm::sort(Results, [](const Member &L, const Member &R) {
+ return L.Name->getName() < R.Name->getName();
+ });
+ return Results;
+ }
+
+private:
+ // Infer members of T, given that the expression E (dependent on T) is true.
+ void believe(const Expr *E, const TemplateTypeParmType *T) {
+ if (!E || !T)
+ return;
+ if (auto *CSE = dyn_cast<ConceptSpecializationExpr>(E)) {
+ // If the concept is
+ // template <class A, class B> concept CD = f<A, B>();
+ // And the concept specialization is
+ // CD<int, T>
+ // Then we're substituting T for B, so we want to make f<A, B>() true
+ // by adding members to B - i.e. believe(f<A, B>(), B);
+ //
+ // For simplicity:
+ // - we don't attempt to substitute int for A
+ // - when T is used in other ways (like CD<T*>) we ignore it
+ ConceptDecl *CD = CSE->getNamedConcept();
+ TemplateParameterList *Params = CD->getTemplateParameters();
+ unsigned Index = 0;
+ for (const auto &Arg : CSE->getTemplateArguments()) {
+ if (Index >= Params->size())
+ break; // Won't happen in valid code.
+ if (isApprox(Arg, T)) {
+ auto *TTPD = dyn_cast<TemplateTypeParmDecl>(Params->getParam(Index));
+ if (!TTPD)
+ continue;
+ // T was used as an argument, and bound to the parameter TT.
+ auto *TT = cast<TemplateTypeParmType>(TTPD->getTypeForDecl());
+ // So now we know the constraint as a function of TT is true.
+ believe(CD->getConstraintExpr(), TT);
+ // (concepts themselves have no associated constraints to require)
+ }
+
+ ++Index;
+ }
+ } else if (auto *BO = dyn_cast<BinaryOperator>(E)) {
+ // For A && B, we can infer members from both branches.
+ // For A || B, the union is still more useful than the intersection.
+ if (BO->getOpcode() == BO_LAnd || BO->getOpcode() == BO_LOr) {
+ believe(BO->getLHS(), T);
+ believe(BO->getRHS(), T);
+ }
+ } else if (auto *RE = dyn_cast<RequiresExpr>(E)) {
+ // A requires(){...} lets us infer members from each requirement.
+ for (const concepts::Requirement *Req : RE->getRequirements()) {
+ if (!Req->isDependent())
+ continue; // Can't tell us anything about T.
+ // Now Req cannot a substitution-error: those aren't dependent.
+
+ if (auto *TR = dyn_cast<concepts::TypeRequirement>(Req)) {
+ // Do a full traversal so we get `foo` from `typename T::foo::bar`.
+ QualType AssertedType = TR->getType()->getType();
+ ValidVisitor(this, T).TraverseType(AssertedType);
+ } else if (auto *ER = dyn_cast<concepts::ExprRequirement>(Req)) {
+ ValidVisitor Visitor(this, T);
+ // If we have a type constraint on the value of the expression,
+ // AND the whole outer expression describes a member, then we'll
+ // be able to use the constraint to provide the return type.
+ if (ER->getReturnTypeRequirement().isTypeConstraint()) {
+ Visitor.OuterType =
+ ER->getReturnTypeRequirement().getTypeConstraint();
+ Visitor.OuterExpr = ER->getExpr();
+ }
+ Visitor.TraverseStmt(ER->getExpr());
+ } else if (auto *NR = dyn_cast<concepts::NestedRequirement>(Req)) {
+ believe(NR->getConstraintExpr(), T);
+ }
+ }
+ }
+ }
+
+ // This visitor infers members of T based on traversing expressions/types
+ // that involve T. It is invoked with code known to be valid for T.
+ class ValidVisitor : public RecursiveASTVisitor<ValidVisitor> {
+ ConceptInfo *Outer;
+ const TemplateTypeParmType *T;
+
+ CallExpr *Caller = nullptr;
+ Expr *Callee = nullptr;
+
+ public:
+ // If set, OuterExpr is constrained by OuterType.
+ Expr *OuterExpr = nullptr;
+ const TypeConstraint *OuterType = nullptr;
+
+ ValidVisitor(ConceptInfo *Outer, const TemplateTypeParmType *T)
+ : Outer(Outer), T(T) {
+ assert(T);
+ }
+
+ // In T.foo or T->foo, `foo` is a member function/variable.
+ bool VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E) {
+ const Type *Base = E->getBaseType().getTypePtr();
+ bool IsArrow = E->isArrow();
+ if (Base->isPointerType() && IsArrow) {
+ IsArrow = false;
+ Base = Base->getPointeeType().getTypePtr();
+ }
+ if (isApprox(Base, T))
+ addValue(E, E->getMember(), IsArrow ? Member::Arrow : Member::Dot);
+ return true;
+ }
+
+ // In T::foo, `foo` is a static member function/variable.
+ bool VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *E) {
+ if (E->getQualifier() && isApprox(E->getQualifier()->getAsType(), T))
+ addValue(E, E->getDeclName(), Member::Colons);
+ return true;
+ }
+
+ // In T::typename foo, `foo` is a type.
+ bool VisitDependentNameType(DependentNameType *DNT) {
+ const auto *Q = DNT->getQualifier();
+ if (Q && isApprox(Q->getAsType(), T))
+ addType(DNT->getIdentifier());
+ return true;
+ }
+
+ // In T::foo::bar, `foo` must be a type.
+ // VisitNNS() doesn't exist, and TraverseNNS isn't always called :-(
+ bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc NNSL) {
+ if (NNSL) {
+ NestedNameSpecifier *NNS = NNSL.getNestedNameSpecifier();
+ const auto *Q = NNS->getPrefix();
+ if (Q && isApprox(Q->getAsType(), T))
+ addType(NNS->getAsIdentifier());
+ }
+ // FIXME: also handle T::foo<X>::bar
+ return RecursiveASTVisitor::TraverseNestedNameSpecifierLoc(NNSL);
+ }
+
+ // FIXME also handle T::foo<X>
+
+ // Track the innermost caller/callee relationship so we can tell if a
+ // nested expr is being called as a function.
+ bool VisitCallExpr(CallExpr *CE) {
+ Caller = CE;
+ Callee = CE->getCallee();
+ return true;
+ }
+
+ private:
+ void addResult(Member &&M) {
+ auto R = Outer->Results.try_emplace(M.Name);
+ Member &O = R.first->second;
+ // Overwrite existing if the new member has more info.
+ // The preference of . vs :: vs -> is fairly arbitrary.
+ if (/*Inserted*/ R.second ||
+ std::make_tuple(M.ArgTypes.hasValue(), M.ResultType != nullptr,
+ M.Operator) > std::make_tuple(O.ArgTypes.hasValue(),
+ O.ResultType != nullptr,
+ O.Operator))
+ O = std::move(M);
+ }
+
+ void addType(const IdentifierInfo *Name) {
+ if (!Name)
+ return;
+ Member M;
+ M.Name = Name;
+ M.Operator = Member::Colons;
+ addResult(std::move(M));
+ }
+
+ void addValue(Expr *E, DeclarationName Name,
+ Member::AccessOperator Operator) {
+ if (!Name.isIdentifier())
+ return;
+ Member Result;
+ Result.Name = Name.getAsIdentifierInfo();
+ Result.Operator = Operator;
+ // If this is the callee of an immediately-enclosing CallExpr, then
+ // treat it as a method, otherwise it's a variable.
+ if (Caller != nullptr && Callee == E) {
+ Result.ArgTypes.emplace();
+ for (const auto *Arg : Caller->arguments())
+ Result.ArgTypes->push_back(Arg->getType());
+ if (Caller == OuterExpr) {
+ Result.ResultType = OuterType;
+ }
+ } else {
+ if (E == OuterExpr)
+ Result.ResultType = OuterType;
+ }
+ addResult(std::move(Result));
+ }
+ };
+
+ static bool isApprox(const TemplateArgument &Arg, const Type *T) {
+ return Arg.getKind() == TemplateArgument::Type &&
+ isApprox(Arg.getAsType().getTypePtr(), T);
+ }
+
+ static bool isApprox(const Type *T1, const Type *T2) {
+ return T1 && T2 &&
+ T1->getCanonicalTypeUnqualified() ==
+ T2->getCanonicalTypeUnqualified();
+ }
+
+ // Returns the DeclContext immediately enclosed by the template parameter
+ // scope. For primary templates, this is the templated (e.g.) CXXRecordDecl.
+ // For specializations, this is e.g. ClassTemplatePartialSpecializationDecl.
+ static DeclContext *getTemplatedEntity(const TemplateTypeParmDecl *D,
+ Scope *S) {
+ if (D == nullptr)
+ return nullptr;
+ Scope *Inner = nullptr;
+ while (S) {
+ if (S->isTemplateParamScope() && S->isDeclScope(D))
+ return Inner ? Inner->getEntity() : nullptr;
+ Inner = S;
+ S = S->getParent();
+ }
+ return nullptr;
+ }
+
+ // Gets all the type constraint expressions that might apply to the type
+ // variables associated with DC (as returned by getTemplatedEntity()).
+ static SmallVector<const Expr *, 1>
+ constraintsForTemplatedEntity(DeclContext *DC) {
+ SmallVector<const Expr *, 1> Result;
+ if (DC == nullptr)
+ return Result;
+ // Primary templates can have constraints.
+ if (const auto *TD = cast<Decl>(DC)->getDescribedTemplate())
+ TD->getAssociatedConstraints(Result);
+ // Partial specializations may have constraints.
+ if (const auto *CTPSD =
+ dyn_cast<ClassTemplatePartialSpecializationDecl>(DC))
+ CTPSD->getAssociatedConstraints(Result);
+ if (const auto *VTPSD = dyn_cast<VarTemplatePartialSpecializationDecl>(DC))
+ VTPSD->getAssociatedConstraints(Result);
+ return Result;
+ }
+
+ // Attempt to find the unique type satisfying a constraint.
+ // This lets us show e.g. `int` instead of `std::same_as<int>`.
+ static QualType deduceType(const TypeConstraint &T) {
+ // Assume a same_as<T> return type constraint is std::same_as or equivalent.
+ // In this case the return type is T.
+ DeclarationName DN = T.getNamedConcept()->getDeclName();
+ if (DN.isIdentifier() && DN.getAsIdentifierInfo()->isStr("same_as"))
+ if (const auto *Args = T.getTemplateArgsAsWritten())
+ if (Args->getNumTemplateArgs() == 1) {
+ const auto &Arg = Args->arguments().front().getArgument();
+ if (Arg.getKind() == TemplateArgument::Type)
+ return Arg.getAsType();
+ }
+ return {};
+ }
+
+ llvm::DenseMap<const IdentifierInfo *, Member> Results;
+};
+} // namespace
+
void Sema::CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base,
Expr *OtherOpBase,
SourceLocation OpLoc, bool IsArrow,
@@ -4767,37 +5179,46 @@ void Sema::CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base,
Base = ConvertedBase.get();
QualType BaseType = Base->getType();
+ if (BaseType.isNull())
+ return false;
ExprValueKind BaseKind = Base->getValueKind();
if (IsArrow) {
if (const PointerType *Ptr = BaseType->getAs<PointerType>()) {
BaseType = Ptr->getPointeeType();
BaseKind = VK_LValue;
- } else if (BaseType->isObjCObjectPointerType())
- /*Do nothing*/;
- else
+ } else if (BaseType->isObjCObjectPointerType() ||
+ BaseType->isTemplateTypeParmType()) {
+ // Both cases (dot/arrow) handled below.
+ } else {
return false;
+ }
}
- if (const RecordType *Record = BaseType->getAs<RecordType>()) {
+ if (RecordDecl *RD = getAsRecordDecl(BaseType)) {
AddRecordMembersCompletionResults(*this, Results, S, BaseType, BaseKind,
- Record->getDecl(),
- std::move(AccessOpFixIt));
- } else if (const auto *TST =
- BaseType->getAs<TemplateSpecializationType>()) {
- TemplateName TN = TST->getTemplateName();
- if (const auto *TD =
- dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl())) {
- CXXRecordDecl *RD = TD->getTemplatedDecl();
- AddRecordMembersCompletionResults(*this, Results, S, BaseType, BaseKind,
- RD, std::move(AccessOpFixIt));
+ RD, std::move(AccessOpFixIt));
+ } else if (const auto *TTPT =
+ dyn_cast<TemplateTypeParmType>(BaseType.getTypePtr())) {
+ auto Operator =
+ IsArrow ? ConceptInfo::Member::Arrow : ConceptInfo::Member::Dot;
+ for (const auto &R : ConceptInfo(*TTPT, S).members()) {
+ if (R.Operator != Operator)
+ continue;
+ CodeCompletionResult Result(
+ R.render(*this, CodeCompleter->getAllocator(),
+ CodeCompleter->getCodeCompletionTUInfo()));
+ if (AccessOpFixIt)
+ Result.FixIts.push_back(*AccessOpFixIt);
+ Results.AddResult(std::move(Result));
}
- } else if (const auto *ICNT = BaseType->getAs<InjectedClassNameType>()) {
- if (auto *RD = ICNT->getDecl())
- AddRecordMembersCompletionResults(*this, Results, S, BaseType, BaseKind,
- RD, std::move(AccessOpFixIt));
} else if (!IsArrow && BaseType->isObjCObjectPointerType()) {
- // Objective-C property reference.
+ // Objective-C property reference. Bail if we're performing fix-it code
+ // completion since Objective-C properties are normally backed by ivars,
+ // most Objective-C fix-its here would have little value.
+ if (AccessOpFixIt.hasValue()) {
+ return false;
+ }
AddedPropertiesSet AddedProperties;
if (const ObjCObjectPointerType *ObjCPtr =
@@ -4817,7 +5238,12 @@ void Sema::CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base,
/*InOriginalClass*/ false);
} else if ((IsArrow && BaseType->isObjCObjectPointerType()) ||
(!IsArrow && BaseType->isObjCObjectType())) {
- // Objective-C instance variable access.
+ // Objective-C instance variable access. Bail if we're performing fix-it
+ // code completion since Objective-C properties are normally backed by
+ // ivars, most Objective-C fix-its here would have little value.
+ if (AccessOpFixIt.hasValue()) {
+ return false;
+ }
ObjCInterfaceDecl *Class = nullptr;
if (const ObjCObjectPointerType *ObjCPtr =
BaseType->getAs<ObjCObjectPointerType>())
@@ -5282,6 +5708,44 @@ QualType Sema::ProduceCtorInitMemberSignatureHelp(
return QualType();
}
+void Sema::CodeCompleteDesignator(const QualType BaseType,
+ llvm::ArrayRef<Expr *> InitExprs,
+ const Designation &D) {
+ if (BaseType.isNull())
+ return;
+ // FIXME: Handle nested designations, e.g. : .x.^
+ if (!D.empty())
+ return;
+
+ const auto *RD = getAsRecordDecl(BaseType);
+ if (!RD)
+ return;
+ if (const auto *CTSD = llvm::dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
+ // Template might not be instantiated yet, fall back to primary template in
+ // such cases.
+ if (CTSD->getTemplateSpecializationKind() == TSK_Undeclared)
+ RD = CTSD->getSpecializedTemplate()->getTemplatedDecl();
+ }
+ if (RD->fields().empty())
+ return;
+
+ CodeCompletionContext CCC(CodeCompletionContext::CCC_DotMemberAccess,
+ BaseType);
+ ResultBuilder Results(*this, CodeCompleter->getAllocator(),
+ CodeCompleter->getCodeCompletionTUInfo(), CCC);
+
+ Results.EnterNewScope();
+ for (const auto *FD : RD->fields()) {
+ // FIXME: Make use of previous designators to mark any fields before those
+ // inaccessible, and also compute the next initializer priority.
+ ResultBuilder::Result Result(FD, Results.getBasePriority(FD));
+ Results.AddResult(Result, CurContext, /*Hiding=*/nullptr);
+ }
+ Results.ExitScope();
+ HandleCodeCompleteResults(this, CodeCompleter, Results.getCompletionContext(),
+ Results.data(), Results.size());
+}
+
void Sema::CodeCompleteInitializer(Scope *S, Decl *D) {
ValueDecl *VD = dyn_cast_or_null<ValueDecl>(D);
if (!VD) {
@@ -5297,7 +5761,7 @@ void Sema::CodeCompleteInitializer(Scope *S, Decl *D) {
CodeCompleteExpression(S, Data);
}
-void Sema::CodeCompleteAfterIf(Scope *S) {
+void Sema::CodeCompleteAfterIf(Scope *S, bool IsBracedThen) {
ResultBuilder Results(*this, CodeCompleter->getAllocator(),
CodeCompleter->getCodeCompletionTUInfo(),
mapCodeCompletionContext(*this, PCC_Statement));
@@ -5314,15 +5778,25 @@ void Sema::CodeCompleteAfterIf(Scope *S) {
// "else" block
CodeCompletionBuilder Builder(Results.getAllocator(),
Results.getCodeCompletionTUInfo());
+
+ auto AddElseBodyPattern = [&] {
+ if (IsBracedThen) {
+ Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
+ Builder.AddChunk(CodeCompletionString::CK_LeftBrace);
+ Builder.AddChunk(CodeCompletionString::CK_VerticalSpace);
+ Builder.AddPlaceholderChunk("statements");
+ Builder.AddChunk(CodeCompletionString::CK_VerticalSpace);
+ Builder.AddChunk(CodeCompletionString::CK_RightBrace);
+ } else {
+ Builder.AddChunk(CodeCompletionString::CK_VerticalSpace);
+ Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
+ Builder.AddPlaceholderChunk("statement");
+ Builder.AddChunk(CodeCompletionString::CK_SemiColon);
+ }
+ };
Builder.AddTypedTextChunk("else");
- if (Results.includeCodePatterns()) {
- Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
- Builder.AddChunk(CodeCompletionString::CK_LeftBrace);
- Builder.AddChunk(CodeCompletionString::CK_VerticalSpace);
- Builder.AddPlaceholderChunk("statements");
- Builder.AddChunk(CodeCompletionString::CK_VerticalSpace);
- Builder.AddChunk(CodeCompletionString::CK_RightBrace);
- }
+ if (Results.includeCodePatterns())
+ AddElseBodyPattern();
Results.AddResult(Builder.TakeString());
// "else if" block
@@ -5335,12 +5809,7 @@ void Sema::CodeCompleteAfterIf(Scope *S) {
Builder.AddPlaceholderChunk("expression");
Builder.AddChunk(CodeCompletionString::CK_RightParen);
if (Results.includeCodePatterns()) {
- Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
- Builder.AddChunk(CodeCompletionString::CK_LeftBrace);
- Builder.AddChunk(CodeCompletionString::CK_VerticalSpace);
- Builder.AddPlaceholderChunk("statements");
- Builder.AddChunk(CodeCompletionString::CK_VerticalSpace);
- Builder.AddChunk(CodeCompletionString::CK_RightBrace);
+ AddElseBodyPattern();
}
Results.AddResult(Builder.TakeString());
@@ -5393,13 +5862,14 @@ void Sema::CodeCompleteQualifiedId(Scope *S, CXXScopeSpec &SS,
// Always pretend to enter a context to ensure that a dependent type
// resolves to a dependent record.
DeclContext *Ctx = computeDeclContext(SS, /*EnteringContext=*/true);
- if (!Ctx)
- return;
// Try to instantiate any non-dependent declaration contexts before
- // we look in them.
- if (!isDependentScopeSpecifier(SS) && RequireCompleteDeclContext(SS, Ctx))
- return;
+ // we look in them. Bail out if we fail.
+ NestedNameSpecifier *NNS = SS.getScopeRep();
+ if (NNS != nullptr && SS.isValid() && !NNS->isDependent()) {
+ if (Ctx == nullptr || RequireCompleteDeclContext(SS, Ctx))
+ return;
+ }
ResultBuilder Results(*this, CodeCompleter->getAllocator(),
CodeCompleter->getCodeCompletionTUInfo(), CC);
@@ -5409,21 +5879,34 @@ void Sema::CodeCompleteQualifiedId(Scope *S, CXXScopeSpec &SS,
// The "template" keyword can follow "::" in the grammar, but only
// put it into the grammar if the nested-name-specifier is dependent.
- NestedNameSpecifier *NNS = SS.getScopeRep();
+ // FIXME: results is always empty, this appears to be dead.
if (!Results.empty() && NNS->isDependent())
Results.AddResult("template");
+ // If the scope is a concept-constrained type parameter, infer nested
+ // members based on the constraints.
+ if (const auto *TTPT =
+ dyn_cast_or_null<TemplateTypeParmType>(NNS->getAsType())) {
+ for (const auto &R : ConceptInfo(*TTPT, S).members()) {
+ if (R.Operator != ConceptInfo::Member::Colons)
+ continue;
+ Results.AddResult(CodeCompletionResult(
+ R.render(*this, CodeCompleter->getAllocator(),
+ CodeCompleter->getCodeCompletionTUInfo())));
+ }
+ }
+
// Add calls to overridden virtual functions, if there are any.
//
// FIXME: This isn't wonderful, because we don't know whether we're actually
// in a context that permits expressions. This is a general issue with
// qualified-id completions.
- if (!EnteringContext)
+ if (Ctx && !EnteringContext)
MaybeAddOverrideCalls(*this, Ctx, Results);
Results.ExitScope();
- if (CodeCompleter->includeNamespaceLevelDecls() ||
- (!Ctx->isNamespace() && !Ctx->isTranslationUnit())) {
+ if (Ctx &&
+ (CodeCompleter->includeNamespaceLevelDecls() || !Ctx->isFileContext())) {
CodeCompletionDeclConsumer Consumer(Results, Ctx, BaseType);
LookupVisibleDecls(Ctx, LookupOrdinaryName, Consumer,
/*IncludeGlobalScope=*/true,
@@ -5785,6 +6268,53 @@ void Sema::CodeCompleteLambdaIntroducer(Scope *S, LambdaIntroducer &Intro,
Results.data(), Results.size());
}
+void Sema::CodeCompleteAfterFunctionEquals(Declarator &D) {
+ if (!LangOpts.CPlusPlus11)
+ return;
+ ResultBuilder Results(*this, CodeCompleter->getAllocator(),
+ CodeCompleter->getCodeCompletionTUInfo(),
+ CodeCompletionContext::CCC_Other);
+ auto ShouldAddDefault = [&D, this]() {
+ if (!D.isFunctionDeclarator())
+ return false;
+ auto &Id = D.getName();
+ if (Id.getKind() == UnqualifiedIdKind::IK_DestructorName)
+ return true;
+ // FIXME(liuhui): Ideally, we should check the constructor parameter list to
+ // verify that it is the default, copy or move constructor?
+ if (Id.getKind() == UnqualifiedIdKind::IK_ConstructorName &&
+ D.getFunctionTypeInfo().NumParams <= 1)
+ return true;
+ if (Id.getKind() == UnqualifiedIdKind::IK_OperatorFunctionId) {
+ auto Op = Id.OperatorFunctionId.Operator;
+ // FIXME(liuhui): Ideally, we should check the function parameter list to
+ // verify that it is the copy or move assignment?
+ if (Op == OverloadedOperatorKind::OO_Equal)
+ return true;
+ if (LangOpts.CPlusPlus20 &&
+ (Op == OverloadedOperatorKind::OO_EqualEqual ||
+ Op == OverloadedOperatorKind::OO_ExclaimEqual ||
+ Op == OverloadedOperatorKind::OO_Less ||
+ Op == OverloadedOperatorKind::OO_LessEqual ||
+ Op == OverloadedOperatorKind::OO_Greater ||
+ Op == OverloadedOperatorKind::OO_GreaterEqual ||
+ Op == OverloadedOperatorKind::OO_Spaceship))
+ return true;
+ }
+ return false;
+ };
+
+ Results.EnterNewScope();
+ if (ShouldAddDefault())
+ Results.AddResult("default");
+ // FIXME(liuhui): Ideally, we should only provide `delete` completion for the
+ // first function declaration.
+ Results.AddResult("delete");
+ Results.ExitScope();
+ HandleCodeCompleteResults(this, CodeCompleter, Results.getCompletionContext(),
+ Results.data(), Results.size());
+}
+
/// Macro that optionally prepends an "@" to the string literal passed in via
/// Keyword, depending on whether NeedAt is true or false.
#define OBJC_AT_KEYWORD_NAME(NeedAt, Keyword) ((NeedAt) ? "@" Keyword : Keyword)
@@ -6063,22 +6593,24 @@ static bool ObjCPropertyFlagConflicts(unsigned Attributes, unsigned NewFlag) {
Attributes |= NewFlag;
// Check for collisions with "readonly".
- if ((Attributes & ObjCDeclSpec::DQ_PR_readonly) &&
- (Attributes & ObjCDeclSpec::DQ_PR_readwrite))
+ if ((Attributes & ObjCPropertyAttribute::kind_readonly) &&
+ (Attributes & ObjCPropertyAttribute::kind_readwrite))
return true;
// Check for more than one of { assign, copy, retain, strong, weak }.
unsigned AssignCopyRetMask =
Attributes &
- (ObjCDeclSpec::DQ_PR_assign | ObjCDeclSpec::DQ_PR_unsafe_unretained |
- ObjCDeclSpec::DQ_PR_copy | ObjCDeclSpec::DQ_PR_retain |
- ObjCDeclSpec::DQ_PR_strong | ObjCDeclSpec::DQ_PR_weak);
- if (AssignCopyRetMask && AssignCopyRetMask != ObjCDeclSpec::DQ_PR_assign &&
- AssignCopyRetMask != ObjCDeclSpec::DQ_PR_unsafe_unretained &&
- AssignCopyRetMask != ObjCDeclSpec::DQ_PR_copy &&
- AssignCopyRetMask != ObjCDeclSpec::DQ_PR_retain &&
- AssignCopyRetMask != ObjCDeclSpec::DQ_PR_strong &&
- AssignCopyRetMask != ObjCDeclSpec::DQ_PR_weak)
+ (ObjCPropertyAttribute::kind_assign |
+ ObjCPropertyAttribute::kind_unsafe_unretained |
+ ObjCPropertyAttribute::kind_copy | ObjCPropertyAttribute::kind_retain |
+ ObjCPropertyAttribute::kind_strong | ObjCPropertyAttribute::kind_weak);
+ if (AssignCopyRetMask &&
+ AssignCopyRetMask != ObjCPropertyAttribute::kind_assign &&
+ AssignCopyRetMask != ObjCPropertyAttribute::kind_unsafe_unretained &&
+ AssignCopyRetMask != ObjCPropertyAttribute::kind_copy &&
+ AssignCopyRetMask != ObjCPropertyAttribute::kind_retain &&
+ AssignCopyRetMask != ObjCPropertyAttribute::kind_strong &&
+ AssignCopyRetMask != ObjCPropertyAttribute::kind_weak)
return true;
return false;
@@ -6094,32 +6626,41 @@ void Sema::CodeCompleteObjCPropertyFlags(Scope *S, ObjCDeclSpec &ODS) {
CodeCompleter->getCodeCompletionTUInfo(),
CodeCompletionContext::CCC_Other);
Results.EnterNewScope();
- if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_readonly))
+ if (!ObjCPropertyFlagConflicts(Attributes,
+ ObjCPropertyAttribute::kind_readonly))
Results.AddResult(CodeCompletionResult("readonly"));
- if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_assign))
+ if (!ObjCPropertyFlagConflicts(Attributes,
+ ObjCPropertyAttribute::kind_assign))
Results.AddResult(CodeCompletionResult("assign"));
if (!ObjCPropertyFlagConflicts(Attributes,
- ObjCDeclSpec::DQ_PR_unsafe_unretained))
+ ObjCPropertyAttribute::kind_unsafe_unretained))
Results.AddResult(CodeCompletionResult("unsafe_unretained"));
- if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_readwrite))
+ if (!ObjCPropertyFlagConflicts(Attributes,
+ ObjCPropertyAttribute::kind_readwrite))
Results.AddResult(CodeCompletionResult("readwrite"));
- if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_retain))
+ if (!ObjCPropertyFlagConflicts(Attributes,
+ ObjCPropertyAttribute::kind_retain))
Results.AddResult(CodeCompletionResult("retain"));
- if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_strong))
+ if (!ObjCPropertyFlagConflicts(Attributes,
+ ObjCPropertyAttribute::kind_strong))
Results.AddResult(CodeCompletionResult("strong"));
- if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_copy))
+ if (!ObjCPropertyFlagConflicts(Attributes, ObjCPropertyAttribute::kind_copy))
Results.AddResult(CodeCompletionResult("copy"));
- if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_nonatomic))
+ if (!ObjCPropertyFlagConflicts(Attributes,
+ ObjCPropertyAttribute::kind_nonatomic))
Results.AddResult(CodeCompletionResult("nonatomic"));
- if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_atomic))
+ if (!ObjCPropertyFlagConflicts(Attributes,
+ ObjCPropertyAttribute::kind_atomic))
Results.AddResult(CodeCompletionResult("atomic"));
// Only suggest "weak" if we're compiling for ARC-with-weak-references or GC.
if (getLangOpts().ObjCWeak || getLangOpts().getGC() != LangOptions::NonGC)
- if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_weak))
+ if (!ObjCPropertyFlagConflicts(Attributes,
+ ObjCPropertyAttribute::kind_weak))
Results.AddResult(CodeCompletionResult("weak"));
- if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_setter)) {
+ if (!ObjCPropertyFlagConflicts(Attributes,
+ ObjCPropertyAttribute::kind_setter)) {
CodeCompletionBuilder Setter(Results.getAllocator(),
Results.getCodeCompletionTUInfo());
Setter.AddTypedTextChunk("setter");
@@ -6127,7 +6668,8 @@ void Sema::CodeCompleteObjCPropertyFlags(Scope *S, ObjCDeclSpec &ODS) {
Setter.AddPlaceholderChunk("method");
Results.AddResult(CodeCompletionResult(Setter.TakeString()));
}
- if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_getter)) {
+ if (!ObjCPropertyFlagConflicts(Attributes,
+ ObjCPropertyAttribute::kind_getter)) {
CodeCompletionBuilder Getter(Results.getAllocator(),
Results.getCodeCompletionTUInfo());
Getter.AddTypedTextChunk("getter");
@@ -6135,7 +6677,8 @@ void Sema::CodeCompleteObjCPropertyFlags(Scope *S, ObjCDeclSpec &ODS) {
Getter.AddPlaceholderChunk("method");
Results.AddResult(CodeCompletionResult(Getter.TakeString()));
}
- if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_nullability)) {
+ if (!ObjCPropertyFlagConflicts(Attributes,
+ ObjCPropertyAttribute::kind_nullability)) {
Results.AddResult(CodeCompletionResult("nonnull"));
Results.AddResult(CodeCompletionResult("nullable"));
Results.AddResult(CodeCompletionResult("null_unspecified"));
@@ -7602,7 +8145,7 @@ static void AddObjCKeyValueCompletions(ObjCPropertyDecl *Property,
} Key(Allocator, PropName->getName());
// The uppercased name of the property name.
- std::string UpperKey = PropName->getName();
+ std::string UpperKey = std::string(PropName->getName());
if (!UpperKey.empty())
UpperKey[0] = toUppercase(UpperKey[0]);
@@ -7660,8 +8203,8 @@ static void AddObjCKeyValueCompletions(ObjCPropertyDecl *Property,
Builder.AddChunk(CodeCompletionString::CK_RightParen);
}
- Builder.AddTypedTextChunk(Allocator.CopyString(SelectorId->getName()));
- Builder.AddTypedTextChunk(":");
+ Builder.AddTypedTextChunk(
+ Allocator.CopyString(SelectorId->getName() + ":"));
AddObjCPassingTypeChunk(Property->getType(), /*Quals=*/0, Context, Policy,
Builder);
Builder.AddTextChunk(Key);
@@ -8249,39 +8792,43 @@ void Sema::CodeCompleteObjCMethodDecl(Scope *S, Optional<bool> IsInstanceMethod,
Selector Sel = Method->getSelector();
- // Add the first part of the selector to the pattern.
- Builder.AddTypedTextChunk(
- Builder.getAllocator().CopyString(Sel.getNameForSlot(0)));
-
- // Add parameters to the pattern.
- unsigned I = 0;
- for (ObjCMethodDecl::param_iterator P = Method->param_begin(),
- PEnd = Method->param_end();
- P != PEnd; (void)++P, ++I) {
- // Add the part of the selector name.
- if (I == 0)
- Builder.AddTypedTextChunk(":");
- else if (I < Sel.getNumArgs()) {
- Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
- Builder.AddTypedTextChunk(
- Builder.getAllocator().CopyString(Sel.getNameForSlot(I) + ":"));
- } else
- break;
-
- // Add the parameter type.
- QualType ParamType;
- if ((*P)->getObjCDeclQualifier() & Decl::OBJC_TQ_CSNullability)
- ParamType = (*P)->getType();
- else
- ParamType = (*P)->getOriginalType();
- ParamType = ParamType.substObjCTypeArgs(
- Context, {}, ObjCSubstitutionContext::Parameter);
- AttributedType::stripOuterNullability(ParamType);
- AddObjCPassingTypeChunk(ParamType, (*P)->getObjCDeclQualifier(), Context,
- Policy, Builder);
+ if (Sel.isUnarySelector()) {
+ // Unary selectors have no arguments.
+ Builder.AddTypedTextChunk(
+ Builder.getAllocator().CopyString(Sel.getNameForSlot(0)));
+ } else {
+ // Add all parameters to the pattern.
+ unsigned I = 0;
+ for (ObjCMethodDecl::param_iterator P = Method->param_begin(),
+ PEnd = Method->param_end();
+ P != PEnd; (void)++P, ++I) {
+ // Add the part of the selector name.
+ if (I == 0)
+ Builder.AddTypedTextChunk(
+ Builder.getAllocator().CopyString(Sel.getNameForSlot(I) + ":"));
+ else if (I < Sel.getNumArgs()) {
+ Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace);
+ Builder.AddTypedTextChunk(
+ Builder.getAllocator().CopyString(Sel.getNameForSlot(I) + ":"));
+ } else
+ break;
- if (IdentifierInfo *Id = (*P)->getIdentifier())
- Builder.AddTextChunk(Builder.getAllocator().CopyString(Id->getName()));
+ // Add the parameter type.
+ QualType ParamType;
+ if ((*P)->getObjCDeclQualifier() & Decl::OBJC_TQ_CSNullability)
+ ParamType = (*P)->getType();
+ else
+ ParamType = (*P)->getOriginalType();
+ ParamType = ParamType.substObjCTypeArgs(
+ Context, {}, ObjCSubstitutionContext::Parameter);
+ AttributedType::stripOuterNullability(ParamType);
+ AddObjCPassingTypeChunk(ParamType, (*P)->getObjCDeclQualifier(),
+ Context, Policy, Builder);
+
+ if (IdentifierInfo *Id = (*P)->getIdentifier())
+ Builder.AddTextChunk(
+ Builder.getAllocator().CopyString(Id->getName()));
+ }
}
if (Method->isVariadic()) {
@@ -8723,7 +9270,16 @@ void Sema::CodeCompleteIncludedFile(llvm::StringRef Dir, bool Angled) {
if (++Count == 2500) // If we happen to hit a huge directory,
break; // bail out early so we're not too slow.
StringRef Filename = llvm::sys::path::filename(It->path());
- switch (It->type()) {
+
+ // To know whether a symlink should be treated as file or a directory, we
+ // have to stat it. This should be cheap enough as there shouldn't be many
+ // symlinks.
+ llvm::sys::fs::file_type Type = It->type();
+ if (Type == llvm::sys::fs::file_type::symlink_file) {
+ if (auto FileStatus = FS.status(It->path()))
+ Type = FileStatus->getType();
+ }
+ switch (Type) {
case llvm::sys::fs::file_type::directory_file:
// All entries in a framework directory must have a ".framework" suffix,
// but the suffix does not appear in the source code's include/import.