diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2020-07-31 21:22:58 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2020-07-31 21:22:58 +0000 |
commit | 5ffd83dbcc34f10e07f6d3e968ae6365869615f4 (patch) | |
tree | 0e9f5cf729dde39f949698fddef45a34e2bc7f44 /contrib/llvm-project/clang/lib/Sema/SemaCodeComplete.cpp | |
parent | 1799696096df87b52968b8996d00c91e0a5de8d9 (diff) | |
parent | cfca06d7963fa0909f90483b42a6d7d194d01e08 (diff) |
Notes
Diffstat (limited to 'contrib/llvm-project/clang/lib/Sema/SemaCodeComplete.cpp')
-rw-r--r-- | contrib/llvm-project/clang/lib/Sema/SemaCodeComplete.cpp | 782 |
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. |