| //===--- CodeCompletion.cpp - Code completion implementation --------------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "swift/IDE/CodeCompletion.h" |
| #include "CodeCompletionResultBuilder.h" |
| #include "ExprContextAnalysis.h" |
| #include "swift/AST/ASTPrinter.h" |
| #include "swift/AST/ASTWalker.h" |
| #include "swift/AST/Comment.h" |
| #include "swift/AST/ImportCache.h" |
| #include "swift/AST/Initializer.h" |
| #include "swift/AST/GenericSignature.h" |
| #include "swift/AST/LazyResolver.h" |
| #include "swift/AST/NameLookup.h" |
| #include "swift/AST/ParameterList.h" |
| #include "swift/AST/ProtocolConformance.h" |
| #include "swift/AST/SourceFile.h" |
| #include "swift/AST/SubstitutionMap.h" |
| #include "swift/AST/USRGeneration.h" |
| #include "swift/Basic/Defer.h" |
| #include "swift/Basic/LLVM.h" |
| #include "swift/ClangImporter/ClangImporter.h" |
| #include "swift/ClangImporter/ClangModule.h" |
| #include "swift/IDE/CodeCompletionCache.h" |
| #include "swift/IDE/CodeCompletionResultPrinter.h" |
| #include "swift/IDE/Utils.h" |
| #include "swift/Parse/CodeCompletionCallbacks.h" |
| #include "swift/Sema/IDETypeChecking.h" |
| #include "swift/Sema/CodeCompletionTypeChecking.h" |
| #include "swift/Syntax/SyntaxKind.h" |
| #include "swift/Strings.h" |
| #include "swift/Subsystems.h" |
| #include "clang/AST/ASTContext.h" |
| #include "clang/AST/Attr.h" |
| #include "clang/AST/Comment.h" |
| #include "clang/AST/CommentVisitor.h" |
| #include "clang/AST/Decl.h" |
| #include "clang/Basic/Module.h" |
| #include "clang/Index/USRGeneration.h" |
| #include "llvm/ADT/SmallString.h" |
| #include "llvm/ADT/StringRef.h" |
| #include "llvm/Support/Compiler.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include "llvm/Support/SaveAndRestore.h" |
| #include <algorithm> |
| #include <string> |
| |
| using namespace swift; |
| using namespace ide; |
| |
| using CommandWordsPairs = std::vector<std::pair<StringRef, StringRef>>; |
| |
| enum CodeCompletionCommandKind { |
| none, |
| keyword, |
| recommended, |
| recommendedover, |
| mutatingvariant, |
| nonmutatingvariant, |
| }; |
| |
| CodeCompletionCommandKind getCommandKind(StringRef Command) { |
| #define CHECK_CASE(KIND) \ |
| if (Command == #KIND) \ |
| return CodeCompletionCommandKind::KIND; |
| CHECK_CASE(keyword); |
| CHECK_CASE(recommended); |
| CHECK_CASE(recommendedover); |
| CHECK_CASE(mutatingvariant); |
| CHECK_CASE(nonmutatingvariant); |
| #undef CHECK_CASE |
| return CodeCompletionCommandKind::none; |
| } |
| |
| StringRef getCommandName(CodeCompletionCommandKind Kind) { |
| #define CHECK_CASE(KIND) \ |
| if (CodeCompletionCommandKind::KIND == Kind) { \ |
| static std::string Name(#KIND); \ |
| return Name; \ |
| } |
| CHECK_CASE(keyword) |
| CHECK_CASE(recommended) |
| CHECK_CASE(recommendedover) |
| CHECK_CASE(mutatingvariant); |
| CHECK_CASE(nonmutatingvariant); |
| #undef CHECK_CASE |
| llvm_unreachable("Cannot handle this Kind."); |
| } |
| |
| bool containsInterestedWords(StringRef Content, StringRef Splitter, |
| bool AllowWhitespace) { |
| do { |
| Content = Content.split(Splitter).second; |
| Content = AllowWhitespace ? Content.trim() : Content; |
| #define CHECK_CASE(KIND) \ |
| if (Content.startswith(#KIND)) \ |
| return true; |
| CHECK_CASE(keyword) |
| CHECK_CASE(recommended) |
| CHECK_CASE(recommendedover) |
| CHECK_CASE(mutatingvariant); |
| CHECK_CASE(nonmutatingvariant); |
| #undef CHECK_CASE |
| } while (!Content.empty()); |
| return false; |
| } |
| |
| void splitTextByComma(StringRef Text, std::vector<StringRef>& Subs) { |
| do { |
| auto Pair = Text.split(','); |
| auto Key = Pair.first.trim(); |
| if (!Key.empty()) |
| Subs.push_back(Key); |
| Text = Pair.second; |
| } while (!Text.empty()); |
| } |
| |
| namespace clang { |
| namespace comments { |
| class WordPairsArrangedViewer { |
| ArrayRef<std::pair<StringRef, StringRef>> Content; |
| std::vector<StringRef> ViewedText; |
| std::vector<StringRef> Words; |
| StringRef Key; |
| |
| bool isKeyViewed(StringRef K) { |
| return std::find(ViewedText.begin(), ViewedText.end(), K) != ViewedText.end(); |
| } |
| |
| public: |
| WordPairsArrangedViewer(ArrayRef<std::pair<StringRef, StringRef>> Content): |
| Content(Content) {} |
| |
| bool hasNext() { |
| Words.clear(); |
| bool Found = false; |
| for (auto P : Content) { |
| if (!Found && !isKeyViewed(P.first)) { |
| Key = P.first; |
| Found = true; |
| } |
| if (Found && P.first == Key) |
| Words.push_back(P.second); |
| } |
| return Found; |
| } |
| |
| std::pair<StringRef, ArrayRef<StringRef>> next() { |
| bool HasNext = hasNext(); |
| (void) HasNext; |
| assert(HasNext && "Have no more data."); |
| ViewedText.push_back(Key); |
| return std::make_pair(Key, llvm::makeArrayRef(Words)); |
| } |
| }; |
| |
| class ClangCommentExtractor : public ConstCommentVisitor<ClangCommentExtractor> { |
| CommandWordsPairs &Words; |
| const CommandTraits &Traits; |
| std::vector<const Comment *> Parents; |
| |
| void visitChildren(const Comment* C) { |
| Parents.push_back(C); |
| for (auto It = C->child_begin(); It != C->child_end(); ++ It) |
| visit(*It); |
| Parents.pop_back(); |
| } |
| |
| public: |
| ClangCommentExtractor(CommandWordsPairs &Words, |
| const CommandTraits &Traits) : Words(Words), |
| Traits(Traits) {} |
| #define CHILD_VISIT(NAME) \ |
| void visit##NAME(const NAME *C) {\ |
| visitChildren(C);\ |
| } |
| CHILD_VISIT(FullComment) |
| CHILD_VISIT(ParagraphComment) |
| #undef CHILD_VISIT |
| |
| void visitInlineCommandComment(const InlineCommandComment *C) { |
| auto Command = C->getCommandName(Traits); |
| auto CommandKind = getCommandKind(Command); |
| if (CommandKind == CodeCompletionCommandKind::none) |
| return; |
| auto &Parent = Parents.back(); |
| for (auto CIT = std::find(Parent->child_begin(), Parent->child_end(), C) + 1; |
| CIT != Parent->child_end(); ++CIT) { |
| if (auto TC = dyn_cast<TextComment>(*CIT)) { |
| auto Text = TC->getText(); |
| std::vector<StringRef> Subs; |
| splitTextByComma(Text, Subs); |
| auto Kind = getCommandName(CommandKind); |
| for (auto S : Subs) |
| Words.push_back(std::make_pair(Kind, S)); |
| } else |
| break; |
| } |
| } |
| }; |
| |
| void getClangDocKeyword(ClangImporter &Importer, const Decl *D, |
| CommandWordsPairs &Words) { |
| ClangCommentExtractor Extractor(Words, Importer.getClangASTContext(). |
| getCommentCommandTraits()); |
| if (auto RC = Importer.getClangASTContext().getRawCommentForAnyRedecl(D)) { |
| auto RT = RC->getRawText(Importer.getClangASTContext().getSourceManager()); |
| if (containsInterestedWords(RT, "@", /*AllowWhitespace*/false)) { |
| FullComment* Comment = Importer.getClangASTContext(). |
| getLocalCommentForDeclUncached(D); |
| Extractor.visit(Comment); |
| } |
| } |
| } |
| } // end namespace comments |
| } // end namespace clang |
| |
| namespace swift { |
| namespace markup { |
| class SwiftDocWordExtractor : public MarkupASTWalker { |
| CommandWordsPairs &Pairs; |
| CodeCompletionCommandKind Kind; |
| public: |
| SwiftDocWordExtractor(CommandWordsPairs &Pairs) : |
| Pairs(Pairs), Kind(CodeCompletionCommandKind::none) {} |
| void visitKeywordField(const KeywordField *Field) override { |
| Kind = CodeCompletionCommandKind::keyword; |
| } |
| void visitRecommendedField(const RecommendedField *Field) override { |
| Kind = CodeCompletionCommandKind::recommended; |
| } |
| void visitRecommendedoverField(const RecommendedoverField *Field) override { |
| Kind = CodeCompletionCommandKind::recommendedover; |
| } |
| void visitMutatingvariantField(const MutatingvariantField *Field) override { |
| Kind = CodeCompletionCommandKind::mutatingvariant; |
| } |
| void visitNonmutatingvariantField(const NonmutatingvariantField *Field) override { |
| Kind = CodeCompletionCommandKind::nonmutatingvariant; |
| } |
| void visitText(const Text *Text) override { |
| if (Kind == CodeCompletionCommandKind::none) |
| return; |
| StringRef CommandName = getCommandName(Kind); |
| std::vector<StringRef> Subs; |
| splitTextByComma(Text->str(), Subs); |
| for (auto S : Subs) |
| Pairs.push_back(std::make_pair(CommandName, S)); |
| } |
| }; |
| |
| void getSwiftDocKeyword(const Decl* D, CommandWordsPairs &Words) { |
| auto Interested = false; |
| for (auto C : D->getRawComment().Comments) { |
| if (containsInterestedWords(C.RawText, "-", /*AllowWhitespace*/true)) { |
| Interested = true; |
| break; |
| } |
| } |
| if (!Interested) |
| return; |
| static swift::markup::MarkupContext MC; |
| auto DC = getSingleDocComment(MC, D); |
| if (!DC) |
| return; |
| SwiftDocWordExtractor Extractor(Words); |
| for (auto Part : DC->getBodyNodes()) { |
| switch (Part->getKind()) { |
| case ASTNodeKind::KeywordField: |
| case ASTNodeKind::RecommendedField: |
| case ASTNodeKind::RecommendedoverField: |
| case ASTNodeKind::MutatingvariantField: |
| case ASTNodeKind::NonmutatingvariantField: |
| Extractor.walk(Part); |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| } // end namespace markup |
| } // end namespace swift |
| |
| using DeclFilter = std::function<bool(ValueDecl *, DeclVisibilityKind)>; |
| static bool DefaultFilter(ValueDecl* VD, DeclVisibilityKind Kind) { |
| return true; |
| } |
| static bool KeyPathFilter(ValueDecl* decl, DeclVisibilityKind) { |
| return isa<TypeDecl>(decl) || |
| (isa<VarDecl>(decl) && decl->getDeclContext()->isTypeContext()); |
| } |
| |
| static bool SwiftKeyPathFilter(ValueDecl* decl, DeclVisibilityKind) { |
| switch(decl->getKind()){ |
| case DeclKind::Var: |
| case DeclKind::Subscript: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| std::string swift::ide::removeCodeCompletionTokens( |
| StringRef Input, StringRef TokenName, unsigned *CompletionOffset) { |
| assert(TokenName.size() >= 1); |
| |
| *CompletionOffset = ~0U; |
| |
| std::string CleanFile; |
| CleanFile.reserve(Input.size()); |
| const std::string Token = std::string("#^") + TokenName.str() + "^#"; |
| |
| for (const char *Ptr = Input.begin(), *End = Input.end(); |
| Ptr != End; ++Ptr) { |
| const char C = *Ptr; |
| if (C == '#' && Ptr <= End - Token.size() && |
| StringRef(Ptr, Token.size()) == Token) { |
| Ptr += Token.size() - 1; |
| *CompletionOffset = CleanFile.size(); |
| CleanFile += '\0'; |
| continue; |
| } |
| if (C == '#' && Ptr <= End - 2 && Ptr[1] == '^') { |
| do { |
| ++Ptr; |
| } while (Ptr < End && *Ptr != '#'); |
| if (Ptr == End) |
| break; |
| continue; |
| } |
| CleanFile += C; |
| } |
| return CleanFile; |
| } |
| |
| llvm::StringRef swift::ide::copyString(llvm::BumpPtrAllocator &Allocator, |
| llvm::StringRef Str) { |
| char *Buffer = Allocator.Allocate<char>(Str.size()); |
| std::copy(Str.begin(), Str.end(), Buffer); |
| return llvm::StringRef(Buffer, Str.size()); |
| } |
| |
| const char *swift::ide::copyCString(llvm::BumpPtrAllocator &Allocator, |
| llvm::StringRef Str) { |
| char *Buffer = Allocator.Allocate<char>(Str.size() + 1); |
| std::copy(Str.begin(), Str.end(), Buffer); |
| Buffer[Str.size()] = '\0'; |
| return Buffer; |
| } |
| |
| CodeCompletionString::CodeCompletionString(ArrayRef<Chunk> Chunks) { |
| std::uninitialized_copy(Chunks.begin(), Chunks.end(), |
| getTrailingObjects<Chunk>()); |
| NumChunks = Chunks.size(); |
| } |
| |
| CodeCompletionString *CodeCompletionString::create(llvm::BumpPtrAllocator &Allocator, |
| ArrayRef<Chunk> Chunks) { |
| void *CCSMem = Allocator.Allocate(totalSizeToAlloc<Chunk>(Chunks.size()), |
| alignof(CodeCompletionString)); |
| return new (CCSMem) CodeCompletionString(Chunks); |
| } |
| |
| void CodeCompletionString::print(raw_ostream &OS) const { |
| unsigned PrevNestingLevel = 0; |
| auto chunks = getChunks(); |
| for (auto I = chunks.begin(), E = chunks.end(); I != E; ++I) { |
| bool AnnotatedTextChunk = false; |
| |
| if (I->getNestingLevel() < PrevNestingLevel) { |
| OS << "#}"; |
| } |
| switch (I->getKind()) { |
| using ChunkKind = Chunk::ChunkKind; |
| case ChunkKind::AccessControlKeyword: |
| case ChunkKind::DeclAttrKeyword: |
| case ChunkKind::DeclAttrParamKeyword: |
| case ChunkKind::OverrideKeyword: |
| case ChunkKind::ThrowsKeyword: |
| case ChunkKind::RethrowsKeyword: |
| case ChunkKind::DeclIntroducer: |
| case ChunkKind::Text: |
| case ChunkKind::LeftParen: |
| case ChunkKind::RightParen: |
| case ChunkKind::LeftBracket: |
| case ChunkKind::RightBracket: |
| case ChunkKind::LeftAngle: |
| case ChunkKind::RightAngle: |
| case ChunkKind::Dot: |
| case ChunkKind::Ellipsis: |
| case ChunkKind::Comma: |
| case ChunkKind::ExclamationMark: |
| case ChunkKind::QuestionMark: |
| case ChunkKind::Ampersand: |
| case ChunkKind::Equal: |
| case ChunkKind::Whitespace: |
| case ChunkKind::Keyword: |
| case ChunkKind::Attribute: |
| case ChunkKind::BaseName: |
| case ChunkKind::TypeIdSystem: |
| case ChunkKind::TypeIdUser: |
| AnnotatedTextChunk = I->isAnnotation(); |
| LLVM_FALLTHROUGH; |
| case ChunkKind::CallParameterName: |
| case ChunkKind::CallParameterInternalName: |
| case ChunkKind::CallParameterColon: |
| case ChunkKind::DeclAttrParamColon: |
| case ChunkKind::CallParameterType: |
| case ChunkKind::CallParameterClosureType: |
| case ChunkKind::GenericParameterName: |
| if (AnnotatedTextChunk) |
| OS << "['"; |
| else if (I->getKind() == ChunkKind::CallParameterInternalName) |
| OS << "("; |
| else if (I->getKind() == ChunkKind::CallParameterClosureType) |
| OS << "##"; |
| for (char Ch : I->getText()) { |
| if (Ch == '\n') |
| OS << "\\n"; |
| else |
| OS << Ch; |
| } |
| if (AnnotatedTextChunk) |
| OS << "']"; |
| else if (I->getKind() == ChunkKind::CallParameterInternalName) |
| OS << ")"; |
| break; |
| case ChunkKind::OptionalBegin: |
| case ChunkKind::CallParameterBegin: |
| case ChunkKind::CallParameterTypeBegin: |
| case ChunkKind::GenericParameterBegin: |
| OS << "{#"; |
| break; |
| case ChunkKind::DynamicLookupMethodCallTail: |
| case ChunkKind::OptionalMethodCallTail: |
| OS << I->getText(); |
| break; |
| case ChunkKind::TypeAnnotationBegin: { |
| OS << "[#"; |
| ++I; |
| auto level = I->getNestingLevel(); |
| for (; I != E && !I->endsPreviousNestedGroup(level); ++I) |
| if (I->hasText()) |
| OS << I->getText(); |
| --I; |
| OS << "#]"; |
| continue; |
| } |
| case ChunkKind::TypeAnnotation: |
| OS << "[#"; |
| OS << I->getText(); |
| OS << "#]"; |
| break; |
| case ChunkKind::CallParameterClosureExpr: |
| OS << " {" << I->getText() << "|}"; |
| break; |
| case ChunkKind::BraceStmtWithCursor: |
| OS << " {|}"; |
| break; |
| } |
| PrevNestingLevel = I->getNestingLevel(); |
| } |
| while (PrevNestingLevel > 0) { |
| OS << "#}"; |
| --PrevNestingLevel; |
| } |
| } |
| |
| void CodeCompletionString::dump() const { |
| print(llvm::errs()); |
| } |
| |
| CodeCompletionDeclKind |
| CodeCompletionResult::getCodeCompletionDeclKind(const Decl *D) { |
| switch (D->getKind()) { |
| case DeclKind::Import: |
| case DeclKind::Extension: |
| case DeclKind::PatternBinding: |
| case DeclKind::EnumCase: |
| case DeclKind::TopLevelCode: |
| case DeclKind::IfConfig: |
| case DeclKind::PoundDiagnostic: |
| case DeclKind::MissingMember: |
| case DeclKind::OpaqueType: |
| llvm_unreachable("not expecting such a declaration result"); |
| case DeclKind::Module: |
| return CodeCompletionDeclKind::Module; |
| case DeclKind::TypeAlias: |
| return CodeCompletionDeclKind::TypeAlias; |
| case DeclKind::AssociatedType: |
| return CodeCompletionDeclKind::AssociatedType; |
| case DeclKind::GenericTypeParam: |
| return CodeCompletionDeclKind::GenericTypeParam; |
| case DeclKind::Enum: |
| return CodeCompletionDeclKind::Enum; |
| case DeclKind::Struct: |
| return CodeCompletionDeclKind::Struct; |
| case DeclKind::Class: |
| return CodeCompletionDeclKind::Class; |
| case DeclKind::Protocol: |
| return CodeCompletionDeclKind::Protocol; |
| case DeclKind::Var: |
| case DeclKind::Param: { |
| auto DC = D->getDeclContext(); |
| if (DC->isTypeContext()) { |
| if (cast<VarDecl>(D)->isStatic()) |
| return CodeCompletionDeclKind::StaticVar; |
| else |
| return CodeCompletionDeclKind::InstanceVar; |
| } |
| if (DC->isLocalContext()) |
| return CodeCompletionDeclKind::LocalVar; |
| return CodeCompletionDeclKind::GlobalVar; |
| } |
| case DeclKind::Constructor: |
| return CodeCompletionDeclKind::Constructor; |
| case DeclKind::Destructor: |
| return CodeCompletionDeclKind::Destructor; |
| case DeclKind::Accessor: |
| case DeclKind::Func: { |
| auto DC = D->getDeclContext(); |
| auto FD = cast<FuncDecl>(D); |
| if (DC->isTypeContext()) { |
| if (FD->isStatic()) |
| return CodeCompletionDeclKind::StaticMethod; |
| return CodeCompletionDeclKind::InstanceMethod; |
| } |
| if (FD->isOperator()) { |
| if (auto op = FD->getOperatorDecl()) { |
| switch (op->getKind()) { |
| case DeclKind::PrefixOperator: |
| return CodeCompletionDeclKind::PrefixOperatorFunction; |
| case DeclKind::PostfixOperator: |
| return CodeCompletionDeclKind::PostfixOperatorFunction; |
| case DeclKind::InfixOperator: |
| return CodeCompletionDeclKind::InfixOperatorFunction; |
| default: |
| llvm_unreachable("unexpected operator kind"); |
| } |
| } else { |
| return CodeCompletionDeclKind::InfixOperatorFunction; |
| } |
| } |
| return CodeCompletionDeclKind::FreeFunction; |
| } |
| case DeclKind::InfixOperator: |
| return CodeCompletionDeclKind::InfixOperatorFunction; |
| case DeclKind::PrefixOperator: |
| return CodeCompletionDeclKind::PrefixOperatorFunction; |
| case DeclKind::PostfixOperator: |
| return CodeCompletionDeclKind::PostfixOperatorFunction; |
| case DeclKind::PrecedenceGroup: |
| return CodeCompletionDeclKind::PrecedenceGroup; |
| case DeclKind::EnumElement: |
| return CodeCompletionDeclKind::EnumElement; |
| case DeclKind::Subscript: |
| return CodeCompletionDeclKind::Subscript; |
| } |
| llvm_unreachable("invalid DeclKind"); |
| } |
| |
| bool CodeCompletionResult::getDeclIsSystem(const Decl *D) { |
| return D->getModuleContext()->isSystemModule(); |
| } |
| |
| void CodeCompletionResult::printPrefix(raw_ostream &OS) const { |
| llvm::SmallString<64> Prefix; |
| switch (getKind()) { |
| case ResultKind::Declaration: |
| Prefix.append("Decl"); |
| switch (getAssociatedDeclKind()) { |
| case CodeCompletionDeclKind::Class: |
| Prefix.append("[Class]"); |
| break; |
| case CodeCompletionDeclKind::Struct: |
| Prefix.append("[Struct]"); |
| break; |
| case CodeCompletionDeclKind::Enum: |
| Prefix.append("[Enum]"); |
| break; |
| case CodeCompletionDeclKind::EnumElement: |
| Prefix.append("[EnumElement]"); |
| break; |
| case CodeCompletionDeclKind::Protocol: |
| Prefix.append("[Protocol]"); |
| break; |
| case CodeCompletionDeclKind::TypeAlias: |
| Prefix.append("[TypeAlias]"); |
| break; |
| case CodeCompletionDeclKind::AssociatedType: |
| Prefix.append("[AssociatedType]"); |
| break; |
| case CodeCompletionDeclKind::GenericTypeParam: |
| Prefix.append("[GenericTypeParam]"); |
| break; |
| case CodeCompletionDeclKind::Constructor: |
| Prefix.append("[Constructor]"); |
| break; |
| case CodeCompletionDeclKind::Destructor: |
| Prefix.append("[Destructor]"); |
| break; |
| case CodeCompletionDeclKind::Subscript: |
| Prefix.append("[Subscript]"); |
| break; |
| case CodeCompletionDeclKind::StaticMethod: |
| Prefix.append("[StaticMethod]"); |
| break; |
| case CodeCompletionDeclKind::InstanceMethod: |
| Prefix.append("[InstanceMethod]"); |
| break; |
| case CodeCompletionDeclKind::PrefixOperatorFunction: |
| Prefix.append("[PrefixOperatorFunction]"); |
| break; |
| case CodeCompletionDeclKind::PostfixOperatorFunction: |
| Prefix.append("[PostfixOperatorFunction]"); |
| break; |
| case CodeCompletionDeclKind::InfixOperatorFunction: |
| Prefix.append("[InfixOperatorFunction]"); |
| break; |
| case CodeCompletionDeclKind::FreeFunction: |
| Prefix.append("[FreeFunction]"); |
| break; |
| case CodeCompletionDeclKind::StaticVar: |
| Prefix.append("[StaticVar]"); |
| break; |
| case CodeCompletionDeclKind::InstanceVar: |
| Prefix.append("[InstanceVar]"); |
| break; |
| case CodeCompletionDeclKind::LocalVar: |
| Prefix.append("[LocalVar]"); |
| break; |
| case CodeCompletionDeclKind::GlobalVar: |
| Prefix.append("[GlobalVar]"); |
| break; |
| case CodeCompletionDeclKind::Module: |
| Prefix.append("[Module]"); |
| break; |
| case CodeCompletionDeclKind::PrecedenceGroup: |
| Prefix.append("[PrecedenceGroup]"); |
| break; |
| } |
| break; |
| case ResultKind::Keyword: |
| Prefix.append("Keyword"); |
| switch (getKeywordKind()) { |
| case CodeCompletionKeywordKind::None: |
| break; |
| #define KEYWORD(X) case CodeCompletionKeywordKind::kw_##X: \ |
| Prefix.append("[" #X "]"); \ |
| break; |
| #define POUND_KEYWORD(X) case CodeCompletionKeywordKind::pound_##X: \ |
| Prefix.append("[#" #X "]"); \ |
| break; |
| #include "swift/Syntax/TokenKinds.def" |
| } |
| break; |
| case ResultKind::Pattern: |
| Prefix.append("Pattern"); |
| break; |
| case ResultKind::Literal: |
| Prefix.append("Literal"); |
| switch (getLiteralKind()) { |
| case CodeCompletionLiteralKind::ArrayLiteral: |
| Prefix.append("[Array]"); |
| break; |
| case CodeCompletionLiteralKind::BooleanLiteral: |
| Prefix.append("[Boolean]"); |
| break; |
| case CodeCompletionLiteralKind::ColorLiteral: |
| Prefix.append("[_Color]"); |
| break; |
| case CodeCompletionLiteralKind::ImageLiteral: |
| Prefix.append("[_Image]"); |
| break; |
| case CodeCompletionLiteralKind::DictionaryLiteral: |
| Prefix.append("[Dictionary]"); |
| break; |
| case CodeCompletionLiteralKind::IntegerLiteral: |
| Prefix.append("[Integer]"); |
| break; |
| case CodeCompletionLiteralKind::NilLiteral: |
| Prefix.append("[Nil]"); |
| break; |
| case CodeCompletionLiteralKind::StringLiteral: |
| Prefix.append("[String]"); |
| break; |
| case CodeCompletionLiteralKind::Tuple: |
| Prefix.append("[Tuple]"); |
| break; |
| } |
| break; |
| case ResultKind::BuiltinOperator: |
| Prefix.append("BuiltinOperator"); |
| break; |
| } |
| Prefix.append("/"); |
| switch (getSemanticContext()) { |
| case SemanticContextKind::None: |
| Prefix.append("None"); |
| break; |
| case SemanticContextKind::ExpressionSpecific: |
| Prefix.append("ExprSpecific"); |
| break; |
| case SemanticContextKind::Local: |
| Prefix.append("Local"); |
| break; |
| case SemanticContextKind::CurrentNominal: |
| Prefix.append("CurrNominal"); |
| break; |
| case SemanticContextKind::Super: |
| Prefix.append("Super"); |
| break; |
| case SemanticContextKind::OutsideNominal: |
| Prefix.append("OutNominal"); |
| break; |
| case SemanticContextKind::CurrentModule: |
| Prefix.append("CurrModule"); |
| break; |
| case SemanticContextKind::OtherModule: |
| Prefix.append("OtherModule"); |
| if (!ModuleName.empty()) |
| Prefix.append((Twine("[") + ModuleName + "]").str()); |
| break; |
| } |
| if (NotRecommended) |
| Prefix.append("/NotRecommended"); |
| if (IsSystem) |
| Prefix.append("/IsSystem"); |
| if (NumBytesToErase != 0) { |
| Prefix.append("/Erase["); |
| Prefix.append(Twine(NumBytesToErase).str()); |
| Prefix.append("]"); |
| } |
| switch (getExpectedTypeRelation()) { |
| case ExpectedTypeRelation::Invalid: |
| Prefix.append("/TypeRelation[Invalid]"); |
| break; |
| case ExpectedTypeRelation::Identical: |
| Prefix.append("/TypeRelation[Identical]"); |
| break; |
| case ExpectedTypeRelation::Convertible: |
| Prefix.append("/TypeRelation[Convertible]"); |
| break; |
| case ExpectedTypeRelation::NotApplicable: |
| case ExpectedTypeRelation::Unknown: |
| case ExpectedTypeRelation::Unrelated: |
| break; |
| } |
| |
| for (clang::comments::WordPairsArrangedViewer Viewer(DocWords); |
| Viewer.hasNext();) { |
| auto Pair = Viewer.next(); |
| Prefix.append("/"); |
| Prefix.append(Pair.first); |
| Prefix.append("["); |
| StringRef Sep = ", "; |
| for (auto KW : Pair.second) { |
| Prefix.append(KW); |
| Prefix.append(Sep); |
| } |
| for (unsigned I = 0, N = Sep.size(); I < N; ++I) |
| Prefix.pop_back(); |
| Prefix.append("]"); |
| } |
| |
| Prefix.append(": "); |
| while (Prefix.size() < 36) { |
| Prefix.append(" "); |
| } |
| OS << Prefix; |
| } |
| |
| void CodeCompletionResult::dump() const { |
| printPrefix(llvm::errs()); |
| CompletionString->print(llvm::errs()); |
| llvm::errs() << "\n"; |
| } |
| |
| void CodeCompletionResultBuilder::withNestedGroup( |
| CodeCompletionString::Chunk::ChunkKind Kind, |
| llvm::function_ref<void()> body) { |
| ++CurrentNestingLevel; |
| addSimpleChunk(Kind); |
| body(); |
| --CurrentNestingLevel; |
| } |
| |
| void CodeCompletionResultBuilder::addChunkWithText( |
| CodeCompletionString::Chunk::ChunkKind Kind, StringRef Text) { |
| addChunkWithTextNoCopy(Kind, copyString(*Sink.Allocator, Text)); |
| } |
| |
| void CodeCompletionResultBuilder::setAssociatedDecl(const Decl *D) { |
| assert(Kind == CodeCompletionResult::ResultKind::Declaration); |
| AssociatedDecl = D; |
| if (auto *ClangD = D->getClangDecl()) |
| CurrentModule = ClangD->getImportedOwningModule(); |
| // FIXME: macros |
| // FIXME: imported header module |
| |
| if (!CurrentModule) { |
| ModuleDecl *MD = D->getModuleContext(); |
| |
| // If this is an underscored cross-import overlay, map it to the underlying |
| // module that declares it instead. |
| if (ModuleDecl *Declaring = MD->getDeclaringModuleIfCrossImportOverlay()) |
| MD = Declaring; |
| |
| CurrentModule = MD; |
| } |
| |
| if (D->getAttrs().getDeprecated(D->getASTContext())) |
| setNotRecommended(CodeCompletionResult::Deprecated); |
| } |
| |
| namespace { |
| class AnnotatedTypePrinter : public ASTPrinter { |
| using ChunkKind = CodeCompletionString::Chunk::ChunkKind; |
| CodeCompletionResultBuilder &Builder; |
| SmallString<16> Buffer; |
| ChunkKind CurrChunkKind = ChunkKind::Text; |
| ChunkKind NextChunkKind = ChunkKind::Text; |
| |
| Optional<ChunkKind> getChunkKindForPrintNameContext(PrintNameContext context) { |
| switch (context) { |
| case PrintNameContext::Keyword: |
| return ChunkKind::Keyword; |
| case PrintNameContext::Attribute: |
| return ChunkKind::Attribute; |
| default: |
| return None; |
| } |
| } |
| |
| void flush() { |
| if (Buffer.empty()) |
| return; |
| Builder.addChunkWithText(CurrChunkKind, Buffer); |
| Buffer.clear(); |
| } |
| |
| public: |
| AnnotatedTypePrinter(CodeCompletionResultBuilder &Builder) : Builder(Builder) {} |
| |
| ~AnnotatedTypePrinter() { |
| // Flush the remainings. |
| flush(); |
| } |
| |
| void printText(StringRef Text) override { |
| if (CurrChunkKind != NextChunkKind) { |
| // If the next desired kind is different from the current buffer, flush |
| // the current buffer. |
| flush(); |
| CurrChunkKind = NextChunkKind; |
| } |
| Buffer.append(Text); |
| } |
| |
| void printTypeRef( |
| Type T, const TypeDecl *TD, Identifier Name, |
| PrintNameContext NameContext = PrintNameContext::Normal) override { |
| |
| NextChunkKind = TD->getModuleContext()->isSystemModule() |
| ? ChunkKind::TypeIdSystem |
| : ChunkKind::TypeIdUser; |
| |
| ASTPrinter::printTypeRef(T, TD, Name, NameContext); |
| NextChunkKind = ChunkKind::Text; |
| } |
| |
| void printNamePre(PrintNameContext context) override { |
| if (auto Kind = getChunkKindForPrintNameContext(context)) |
| NextChunkKind = *Kind; |
| } |
| |
| void printNamePost(PrintNameContext context) override { |
| if (auto Kind = getChunkKindForPrintNameContext(context)) |
| NextChunkKind = ChunkKind::Text; |
| } |
| }; |
| } // namespcae |
| |
| void CodeCompletionResultBuilder::addCallParameter(Identifier Name, |
| Identifier LocalName, |
| Type Ty, |
| Type ContextTy, |
| bool IsVarArg, |
| bool IsInOut, |
| bool IsIUO, |
| bool isAutoClosure, |
| bool useUnderscoreLabel, |
| bool isLabeledTrailingClosure) { |
| ++CurrentNestingLevel; |
| using ChunkKind = CodeCompletionString::Chunk::ChunkKind; |
| |
| addSimpleChunk(ChunkKind::CallParameterBegin); |
| |
| if (shouldAnnotateResults()) { |
| if (!Name.empty() || !LocalName.empty()) { |
| llvm::SmallString<16> EscapedKeyword; |
| |
| if (!Name.empty()) { |
| addChunkWithText( |
| CodeCompletionString::Chunk::ChunkKind::CallParameterName, |
| escapeKeyword(Name.str(), false, EscapedKeyword)); |
| |
| if (!LocalName.empty() && Name != LocalName) { |
| addChunkWithTextNoCopy(ChunkKind::Text, " "); |
| getLastChunk().setIsAnnotation(); |
| addChunkWithText(ChunkKind::CallParameterInternalName, |
| escapeKeyword(LocalName.str(), false, EscapedKeyword)); |
| getLastChunk().setIsAnnotation(); |
| } |
| } else { |
| assert(!LocalName.empty()); |
| addChunkWithTextNoCopy(ChunkKind::CallParameterName, "_"); |
| getLastChunk().setIsAnnotation(); |
| addChunkWithTextNoCopy(ChunkKind::Text, " "); |
| getLastChunk().setIsAnnotation(); |
| addChunkWithText(ChunkKind::CallParameterInternalName, |
| escapeKeyword(LocalName.str(), false, EscapedKeyword)); |
| } |
| addChunkWithTextNoCopy(ChunkKind::CallParameterColon, ": "); |
| } |
| } else { |
| if (!Name.empty()) { |
| llvm::SmallString<16> EscapedKeyword; |
| addChunkWithText( |
| CodeCompletionString::Chunk::ChunkKind::CallParameterName, |
| escapeKeyword(Name.str(), false, EscapedKeyword)); |
| addChunkWithTextNoCopy( |
| CodeCompletionString::Chunk::ChunkKind::CallParameterColon, ": "); |
| } else if (useUnderscoreLabel) { |
| addChunkWithTextNoCopy( |
| CodeCompletionString::Chunk::ChunkKind::CallParameterName, "_"); |
| addChunkWithTextNoCopy( |
| CodeCompletionString::Chunk::ChunkKind::CallParameterColon, ": "); |
| } else if (!LocalName.empty()) { |
| // Use local (non-API) parameter name if we have nothing else. |
| llvm::SmallString<16> EscapedKeyword; |
| addChunkWithText( |
| CodeCompletionString::Chunk::ChunkKind::CallParameterInternalName, |
| escapeKeyword(LocalName.str(), false, EscapedKeyword)); |
| addChunkWithTextNoCopy( |
| CodeCompletionString::Chunk::ChunkKind::CallParameterColon, ": "); |
| } |
| } |
| |
| // 'inout' arguments are printed specially. |
| if (IsInOut) { |
| addChunkWithTextNoCopy( |
| CodeCompletionString::Chunk::ChunkKind::Ampersand, "&"); |
| Ty = Ty->getInOutObjectType(); |
| } |
| |
| // If the parameter is of the type @autoclosure ()->output, then the |
| // code completion should show the parameter of the output type |
| // instead of the function type ()->output. |
| if (isAutoClosure) { |
| // 'Ty' may be ErrorType. |
| if (auto funcTy = Ty->getAs<FunctionType>()) |
| Ty = funcTy->getResult(); |
| } |
| |
| PrintOptions PO; |
| PO.SkipAttributes = true; |
| PO.PrintOptionalAsImplicitlyUnwrapped = IsIUO; |
| PO.OpaqueReturnTypePrinting = |
| PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; |
| if (ContextTy) |
| PO.setBaseType(ContextTy); |
| if (shouldAnnotateResults()) { |
| withNestedGroup(ChunkKind::CallParameterTypeBegin, [&]() { |
| AnnotatedTypePrinter printer(*this); |
| Ty->print(printer, PO); |
| }); |
| } else { |
| std::string TypeName = Ty->getString(PO); |
| addChunkWithText(ChunkKind::CallParameterType, TypeName); |
| } |
| |
| // Look through optional types and type aliases to find out if we have |
| // function type. |
| Ty = Ty->lookThroughAllOptionalTypes(); |
| if (auto AFT = Ty->getAs<AnyFunctionType>()) { |
| // If this is a closure type, add ChunkKind::CallParameterClosureType or |
| // ChunkKind::CallParameterClosureExpr for labeled trailing closures. |
| PrintOptions PO; |
| PO.PrintFunctionRepresentationAttrs = |
| PrintOptions::FunctionRepresentationMode::None; |
| PO.SkipAttributes = true; |
| PO.OpaqueReturnTypePrinting = |
| PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; |
| if (ContextTy) |
| PO.setBaseType(ContextTy); |
| |
| if (isLabeledTrailingClosure) { |
| // Expand the closure body. |
| SmallString<32> buffer; |
| llvm::raw_svector_ostream OS(buffer); |
| |
| bool firstParam = true; |
| for (const auto ¶m : AFT->getParams()) { |
| if (!firstParam) |
| OS << ", "; |
| firstParam = false; |
| |
| if (param.hasLabel()) { |
| OS << param.getLabel(); |
| } else { |
| OS << "<#"; |
| if (param.isInOut()) |
| OS << "inout "; |
| OS << param.getPlainType()->getString(PO); |
| if (param.isVariadic()) |
| OS << "..."; |
| OS << "#>"; |
| } |
| } |
| |
| if (!firstParam) |
| OS << " in"; |
| |
| addChunkWithText( |
| CodeCompletionString::Chunk::ChunkKind::CallParameterClosureExpr, |
| OS.str()); |
| } else { |
| // Add the closure type. |
| addChunkWithText( |
| CodeCompletionString::Chunk::ChunkKind::CallParameterClosureType, |
| AFT->getString(PO)); |
| } |
| } |
| |
| if (IsVarArg) |
| addEllipsis(); |
| --CurrentNestingLevel; |
| } |
| |
| void CodeCompletionResultBuilder::addTypeAnnotation(Type T, PrintOptions PO, |
| StringRef suffix) { |
| T = T->getReferenceStorageReferent(); |
| |
| // Replace '()' with 'Void'. |
| if (T->isVoid()) |
| T = T->getASTContext().getVoidDecl()->getDeclaredInterfaceType(); |
| |
| if (shouldAnnotateResults()) { |
| withNestedGroup(CodeCompletionString::Chunk::ChunkKind::TypeAnnotationBegin, |
| [&]() { |
| AnnotatedTypePrinter printer(*this); |
| T->print(printer, PO); |
| if (!suffix.empty()) |
| printer.printText(suffix); |
| }); |
| } else { |
| auto str = T.getString(PO); |
| if (!suffix.empty()) |
| str += suffix.str(); |
| addTypeAnnotation(str); |
| } |
| } |
| |
| StringRef CodeCompletionContext::copyString(StringRef Str) { |
| return ::copyString(*CurrentResults.Allocator, Str); |
| } |
| |
| bool shouldCopyAssociatedUSRForDecl(const ValueDecl *VD) { |
| // Avoid trying to generate a USR for some declaration types. |
| if (isa<AbstractTypeParamDecl>(VD) && !isa<AssociatedTypeDecl>(VD)) |
| return false; |
| if (isa<ParamDecl>(VD)) |
| return false; |
| if (isa<ModuleDecl>(VD)) |
| return false; |
| if (VD->hasClangNode() && !VD->getClangDecl()) |
| return false; |
| |
| return true; |
| } |
| |
| template <typename FnTy> |
| static void walkValueDeclAndOverriddenDecls(const Decl *D, const FnTy &Fn) { |
| if (auto *VD = dyn_cast<ValueDecl>(D)) { |
| Fn(VD); |
| walkOverriddenDecls(VD, Fn); |
| } |
| } |
| |
| ArrayRef<StringRef> copyAssociatedUSRs(llvm::BumpPtrAllocator &Allocator, |
| const Decl *D) { |
| llvm::SmallVector<StringRef, 4> USRs; |
| walkValueDeclAndOverriddenDecls(D, [&](llvm::PointerUnion<const ValueDecl*, |
| const clang::NamedDecl*> OD) { |
| llvm::SmallString<128> SS; |
| bool Ignored = true; |
| if (auto *OVD = OD.dyn_cast<const ValueDecl*>()) { |
| if (shouldCopyAssociatedUSRForDecl(OVD)) { |
| llvm::raw_svector_ostream OS(SS); |
| Ignored = printValueDeclUSR(OVD, OS); |
| } |
| } else if (auto *OND = OD.dyn_cast<const clang::NamedDecl*>()) { |
| Ignored = clang::index::generateUSRForDecl(OND, SS); |
| } |
| |
| if (!Ignored) |
| USRs.push_back(copyString(Allocator, SS)); |
| }); |
| |
| if (!USRs.empty()) |
| return copyArray(Allocator, ArrayRef<StringRef>(USRs)); |
| |
| return ArrayRef<StringRef>(); |
| } |
| |
| static CodeCompletionResult::ExpectedTypeRelation |
| calculateTypeRelation(Type Ty, Type ExpectedTy, const DeclContext *DC) { |
| if (Ty.isNull() || ExpectedTy.isNull() || |
| Ty->is<ErrorType>() || |
| ExpectedTy->is<ErrorType>()) |
| return CodeCompletionResult::ExpectedTypeRelation::Unrelated; |
| |
| // Equality/Conversion of GenericTypeParameterType won't account for |
| // requirements – ignore them |
| if (!Ty->hasTypeParameter() && !ExpectedTy->hasTypeParameter()) { |
| if (Ty->isEqual(ExpectedTy)) |
| return CodeCompletionResult::ExpectedTypeRelation::Identical; |
| bool isAny = false; |
| isAny |= ExpectedTy->isAny(); |
| isAny |= ExpectedTy->is<ArchetypeType>() && |
| !ExpectedTy->castTo<ArchetypeType>()->hasRequirements(); |
| |
| if (!isAny && isConvertibleTo(Ty, ExpectedTy, /*openArchetypes=*/true, |
| *const_cast<DeclContext *>(DC))) |
| return CodeCompletionResult::ExpectedTypeRelation::Convertible; |
| } |
| if (auto FT = Ty->getAs<AnyFunctionType>()) { |
| if (FT->getResult()->isVoid()) |
| return CodeCompletionResult::ExpectedTypeRelation::Invalid; |
| } |
| return CodeCompletionResult::ExpectedTypeRelation::Unrelated; |
| } |
| |
| static CodeCompletionResult::ExpectedTypeRelation |
| calculateMaxTypeRelation(Type Ty, const ExpectedTypeContext &typeContext, |
| const DeclContext *DC) { |
| if (typeContext.empty()) |
| return CodeCompletionResult::ExpectedTypeRelation::Unknown; |
| |
| if (auto funcTy = Ty->getAs<AnyFunctionType>()) |
| Ty = funcTy->removeArgumentLabels(1); |
| |
| auto Result = CodeCompletionResult::ExpectedTypeRelation::Unrelated; |
| for (auto expectedTy : typeContext.possibleTypes) { |
| // Do not use Void type context for a single-expression body, since the |
| // implicit return does not constrain the expression. |
| // |
| // { ... -> () in x } // x can be anything |
| // |
| // This behaves differently from explicit return, and from non-Void: |
| // |
| // { ... -> Int in x } // x must be Int |
| // { ... -> () in return x } // x must be Void |
| if (typeContext.isImplicitSingleExpressionReturn && expectedTy->isVoid()) |
| continue; |
| |
| Result = std::max(Result, calculateTypeRelation(Ty, expectedTy, DC)); |
| |
| // Map invalid -> unrelated when in a single-expression body, since the |
| // input may be incomplete. |
| if (typeContext.isImplicitSingleExpressionReturn && |
| Result == CodeCompletionResult::ExpectedTypeRelation::Invalid) |
| Result = CodeCompletionResult::ExpectedTypeRelation::Unrelated; |
| } |
| return Result; |
| } |
| |
| CodeCompletionOperatorKind |
| CodeCompletionResult::getCodeCompletionOperatorKind(StringRef name) { |
| using CCOK = CodeCompletionOperatorKind; |
| using OpPair = std::pair<StringRef, CCOK>; |
| |
| // This list must be kept in alphabetical order. |
| static OpPair ops[] = { |
| std::make_pair("!", CCOK::Bang), |
| std::make_pair("!=", CCOK::NotEq), |
| std::make_pair("!==", CCOK::NotEqEq), |
| std::make_pair("%", CCOK::Modulo), |
| std::make_pair("%=", CCOK::ModuloEq), |
| std::make_pair("&", CCOK::Amp), |
| std::make_pair("&&", CCOK::AmpAmp), |
| std::make_pair("&*", CCOK::AmpStar), |
| std::make_pair("&+", CCOK::AmpPlus), |
| std::make_pair("&-", CCOK::AmpMinus), |
| std::make_pair("&=", CCOK::AmpEq), |
| std::make_pair("(", CCOK::LParen), |
| std::make_pair("*", CCOK::Star), |
| std::make_pair("*=", CCOK::StarEq), |
| std::make_pair("+", CCOK::Plus), |
| std::make_pair("+=", CCOK::PlusEq), |
| std::make_pair("-", CCOK::Minus), |
| std::make_pair("-=", CCOK::MinusEq), |
| std::make_pair(".", CCOK::Dot), |
| std::make_pair("...", CCOK::DotDotDot), |
| std::make_pair("..<", CCOK::DotDotLess), |
| std::make_pair("/", CCOK::Slash), |
| std::make_pair("/=", CCOK::SlashEq), |
| std::make_pair("<", CCOK::Less), |
| std::make_pair("<<", CCOK::LessLess), |
| std::make_pair("<<=", CCOK::LessLessEq), |
| std::make_pair("<=", CCOK::LessEq), |
| std::make_pair("=", CCOK::Eq), |
| std::make_pair("==", CCOK::EqEq), |
| std::make_pair("===", CCOK::EqEqEq), |
| std::make_pair(">", CCOK::Greater), |
| std::make_pair(">=", CCOK::GreaterEq), |
| std::make_pair(">>", CCOK::GreaterGreater), |
| std::make_pair(">>=", CCOK::GreaterGreaterEq), |
| std::make_pair("?.", CCOK::QuestionDot), |
| std::make_pair("^", CCOK::Caret), |
| std::make_pair("^=", CCOK::CaretEq), |
| std::make_pair("|", CCOK::Pipe), |
| std::make_pair("|=", CCOK::PipeEq), |
| std::make_pair("||", CCOK::PipePipe), |
| std::make_pair("~=", CCOK::TildeEq), |
| }; |
| static auto opsSize = sizeof(ops) / sizeof(ops[0]); |
| |
| auto I = std::lower_bound( |
| ops, &ops[opsSize], std::make_pair(name, CCOK::None), |
| [](const OpPair &a, const OpPair &b) { return a.first < b.first; }); |
| |
| if (I == &ops[opsSize] || I->first != name) |
| return CCOK::Unknown; |
| return I->second; |
| } |
| |
| static StringRef getOperatorName(CodeCompletionString *str) { |
| return str->getFirstTextChunk(/*includeLeadingPunctuation=*/true); |
| } |
| |
| CodeCompletionOperatorKind |
| CodeCompletionResult::getCodeCompletionOperatorKind(CodeCompletionString *str) { |
| return getCodeCompletionOperatorKind(getOperatorName(str)); |
| } |
| |
| CodeCompletionResult *CodeCompletionResultBuilder::takeResult() { |
| auto *CCS = CodeCompletionString::create(*Sink.Allocator, Chunks); |
| |
| switch (Kind) { |
| case CodeCompletionResult::ResultKind::Declaration: { |
| StringRef BriefComment; |
| auto MaybeClangNode = AssociatedDecl->getClangNode(); |
| if (MaybeClangNode) { |
| if (auto *D = MaybeClangNode.getAsDecl()) { |
| const auto &ClangContext = D->getASTContext(); |
| if (const clang::RawComment *RC = |
| ClangContext.getRawCommentForAnyRedecl(D)) |
| BriefComment = RC->getBriefText(ClangContext); |
| } |
| } else { |
| BriefComment = AssociatedDecl->getBriefComment(); |
| } |
| |
| StringRef ModuleName; |
| if (CurrentModule) { |
| if (Sink.LastModule.first == CurrentModule.getOpaqueValue()) { |
| ModuleName = Sink.LastModule.second; |
| } else { |
| if (auto *C = CurrentModule.dyn_cast<const clang::Module *>()) { |
| ModuleName = copyString(*Sink.Allocator, C->getFullModuleName()); |
| } else { |
| ModuleName = copyString( |
| *Sink.Allocator, |
| CurrentModule.get<const swift::ModuleDecl *>()->getName().str()); |
| } |
| Sink.LastModule.first = CurrentModule.getOpaqueValue(); |
| Sink.LastModule.second = ModuleName; |
| } |
| } |
| |
| return new (*Sink.Allocator) CodeCompletionResult( |
| SemanticContext, NumBytesToErase, CCS, AssociatedDecl, ModuleName, |
| /*NotRecommended=*/IsNotRecommended, NotRecReason, |
| copyString(*Sink.Allocator, BriefComment), |
| copyAssociatedUSRs(*Sink.Allocator, AssociatedDecl), |
| copyArray(*Sink.Allocator, CommentWords), ExpectedTypeRelation); |
| } |
| |
| case CodeCompletionResult::ResultKind::Keyword: |
| return new (*Sink.Allocator) |
| CodeCompletionResult( |
| KeywordKind, SemanticContext, NumBytesToErase, |
| CCS, ExpectedTypeRelation, |
| copyString(*Sink.Allocator, BriefDocComment)); |
| |
| case CodeCompletionResult::ResultKind::BuiltinOperator: |
| case CodeCompletionResult::ResultKind::Pattern: |
| return new (*Sink.Allocator) CodeCompletionResult( |
| Kind, SemanticContext, NumBytesToErase, CCS, ExpectedTypeRelation, |
| CodeCompletionOperatorKind::None, |
| copyString(*Sink.Allocator, BriefDocComment)); |
| |
| case CodeCompletionResult::ResultKind::Literal: |
| assert(LiteralKind.hasValue()); |
| return new (*Sink.Allocator) |
| CodeCompletionResult(*LiteralKind, SemanticContext, NumBytesToErase, |
| CCS, ExpectedTypeRelation); |
| } |
| |
| llvm_unreachable("Unhandled CodeCompletionResult in switch."); |
| } |
| |
| void CodeCompletionResultBuilder::finishResult() { |
| if (!Cancelled) |
| Sink.Results.push_back(takeResult()); |
| } |
| |
| |
| MutableArrayRef<CodeCompletionResult *> CodeCompletionContext::takeResults() { |
| // Copy pointers to the results. |
| const size_t Count = CurrentResults.Results.size(); |
| CodeCompletionResult **Results = |
| CurrentResults.Allocator->Allocate<CodeCompletionResult *>(Count); |
| std::copy(CurrentResults.Results.begin(), CurrentResults.Results.end(), |
| Results); |
| CurrentResults.Results.clear(); |
| return MutableArrayRef<CodeCompletionResult *>(Results, Count); |
| } |
| |
| Optional<unsigned> CodeCompletionString::getFirstTextChunkIndex( |
| bool includeLeadingPunctuation) const { |
| for (auto i : indices(getChunks())) { |
| auto &C = getChunks()[i]; |
| switch (C.getKind()) { |
| using ChunkKind = Chunk::ChunkKind; |
| case ChunkKind::Text: |
| case ChunkKind::CallParameterName: |
| case ChunkKind::CallParameterInternalName: |
| case ChunkKind::GenericParameterName: |
| case ChunkKind::LeftParen: |
| case ChunkKind::LeftBracket: |
| case ChunkKind::Equal: |
| case ChunkKind::DeclAttrParamKeyword: |
| case ChunkKind::DeclAttrKeyword: |
| case ChunkKind::Keyword: |
| case ChunkKind::Attribute: |
| case ChunkKind::BaseName: |
| case ChunkKind::TypeIdSystem: |
| case ChunkKind::TypeIdUser: |
| case ChunkKind::CallParameterBegin: |
| return i; |
| case ChunkKind::Dot: |
| case ChunkKind::ExclamationMark: |
| case ChunkKind::QuestionMark: |
| if (includeLeadingPunctuation) |
| return i; |
| continue; |
| case ChunkKind::RightParen: |
| case ChunkKind::RightBracket: |
| case ChunkKind::LeftAngle: |
| case ChunkKind::RightAngle: |
| case ChunkKind::Ellipsis: |
| case ChunkKind::Comma: |
| case ChunkKind::Ampersand: |
| case ChunkKind::Whitespace: |
| case ChunkKind::AccessControlKeyword: |
| case ChunkKind::OverrideKeyword: |
| case ChunkKind::ThrowsKeyword: |
| case ChunkKind::RethrowsKeyword: |
| case ChunkKind::DeclIntroducer: |
| case ChunkKind::CallParameterColon: |
| case ChunkKind::CallParameterTypeBegin: |
| case ChunkKind::DeclAttrParamColon: |
| case ChunkKind::CallParameterType: |
| case ChunkKind::CallParameterClosureType: |
| case ChunkKind::CallParameterClosureExpr: |
| case ChunkKind::OptionalBegin: |
| case ChunkKind::GenericParameterBegin: |
| case ChunkKind::DynamicLookupMethodCallTail: |
| case ChunkKind::OptionalMethodCallTail: |
| case ChunkKind::TypeAnnotation: |
| case ChunkKind::TypeAnnotationBegin: |
| continue; |
| |
| case ChunkKind::BraceStmtWithCursor: |
| llvm_unreachable("should have already extracted the text"); |
| } |
| } |
| return None; |
| } |
| |
| StringRef |
| CodeCompletionString::getFirstTextChunk(bool includeLeadingPunctuation) const { |
| Optional<unsigned> Idx = getFirstTextChunkIndex(includeLeadingPunctuation); |
| if (Idx.hasValue()) |
| return getChunks()[*Idx].getText(); |
| return StringRef(); |
| } |
| |
| void CodeCompletionString::getName(raw_ostream &OS) const { |
| auto FirstTextChunk = getFirstTextChunkIndex(); |
| int TextSize = 0; |
| if (FirstTextChunk.hasValue()) { |
| auto chunks = getChunks().slice(*FirstTextChunk); |
| |
| for (auto i = chunks.begin(), e = chunks.end(); i != e; ++i) { |
| using ChunkKind = Chunk::ChunkKind; |
| |
| bool shouldPrint = !i->isAnnotation(); |
| switch (i->getKind()) { |
| case ChunkKind::TypeAnnotation: |
| case ChunkKind::CallParameterClosureType: |
| case ChunkKind::CallParameterClosureExpr: |
| case ChunkKind::DeclAttrParamColon: |
| case ChunkKind::OptionalMethodCallTail: |
| continue; |
| case ChunkKind::TypeAnnotationBegin: { |
| auto level = i->getNestingLevel(); |
| do { ++i; } while (i != e && !i->endsPreviousNestedGroup(level)); |
| --i; |
| continue; |
| } |
| case ChunkKind::ThrowsKeyword: |
| case ChunkKind::RethrowsKeyword: |
| shouldPrint = true; // Even when they're annotations. |
| break; |
| default: |
| break; |
| } |
| |
| if (i->hasText() && shouldPrint) { |
| TextSize += i->getText().size(); |
| OS << i->getText(); |
| } |
| } |
| } |
| } |
| |
| void CodeCompletionContext::sortCompletionResults( |
| MutableArrayRef<CodeCompletionResult *> Results) { |
| struct ResultAndName { |
| CodeCompletionResult *result; |
| std::string name; |
| }; |
| |
| // Caching the name of each field is important to avoid unnecessary calls to |
| // CodeCompletionString::getName(). |
| std::vector<ResultAndName> nameCache(Results.size()); |
| for (unsigned i = 0, n = Results.size(); i < n; ++i) { |
| auto *result = Results[i]; |
| nameCache[i].result = result; |
| llvm::raw_string_ostream OS(nameCache[i].name); |
| result->getCompletionString()->getName(OS); |
| OS.flush(); |
| } |
| |
| // Sort nameCache, and then transform Results to return the pointers in order. |
| std::sort(nameCache.begin(), nameCache.end(), |
| [](const ResultAndName &LHS, const ResultAndName &RHS) { |
| int Result = StringRef(LHS.name).compare_lower(RHS.name); |
| // If the case insensitive comparison is equal, then secondary sort order |
| // should be case sensitive. |
| if (Result == 0) |
| Result = LHS.name.compare(RHS.name); |
| return Result < 0; |
| }); |
| |
| std::transform(nameCache.begin(), nameCache.end(), Results.begin(), |
| [](const ResultAndName &entry) { return entry.result; }); |
| } |
| |
| namespace { |
| |
| class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { |
| CodeCompletionContext &CompletionContext; |
| CodeCompletionConsumer &Consumer; |
| CodeCompletionExpr *CodeCompleteTokenExpr = nullptr; |
| CompletionKind Kind = CompletionKind::None; |
| Expr *ParsedExpr = nullptr; |
| SourceLoc DotLoc; |
| TypeLoc ParsedTypeLoc; |
| DeclContext *CurDeclContext = nullptr; |
| DeclAttrKind AttrKind; |
| |
| /// In situations when \c SyntaxKind hints or determines |
| /// completions, i.e. a precedence group attribute, this |
| /// can be set and used to control the code completion scenario. |
| SyntaxKind SyntxKind; |
| |
| int AttrParamIndex; |
| bool IsInSil = false; |
| bool HasSpace = false; |
| bool ShouldCompleteCallPatternAfterParen = true; |
| bool PreferFunctionReferencesToCalls = false; |
| bool AttTargetIsIndependent = false; |
| bool IsAtStartOfLine = false; |
| Optional<DeclKind> AttTargetDK; |
| Optional<StmtKind> ParentStmtKind; |
| |
| SmallVector<StringRef, 3> ParsedKeywords; |
| SourceLoc introducerLoc; |
| |
| std::vector<std::pair<std::string, bool>> SubModuleNameVisibilityPairs; |
| |
| void addSuperKeyword(CodeCompletionResultSink &Sink) { |
| auto *DC = CurDeclContext->getInnermostTypeContext(); |
| if (!DC) |
| return; |
| auto *CD = DC->getSelfClassDecl(); |
| if (!CD) |
| return; |
| Type ST = CD->getSuperclass(); |
| if (ST.isNull() || ST->is<ErrorType>()) |
| return; |
| |
| CodeCompletionResultBuilder Builder(Sink, |
| CodeCompletionResult::ResultKind::Keyword, |
| SemanticContextKind::CurrentNominal, |
| {}); |
| Builder.setKeywordKind(CodeCompletionKeywordKind::kw_super); |
| Builder.addKeyword("super"); |
| Builder.addTypeAnnotation(ST, PrintOptions()); |
| } |
| |
| Optional<std::pair<Type, ConcreteDeclRef>> typeCheckParsedExpr() { |
| assert(ParsedExpr && "should have an expression"); |
| |
| // Figure out the kind of type-check we'll be performing. |
| auto CheckKind = CompletionTypeCheckKind::Normal; |
| if (Kind == CompletionKind::KeyPathExprObjC) |
| CheckKind = CompletionTypeCheckKind::KeyPath; |
| |
| // If we've already successfully type-checked the expression for some |
| // reason, just return the type. |
| // FIXME: if it's ErrorType but we've already typechecked we shouldn't |
| // typecheck again. rdar://21466394 |
| if (CheckKind == CompletionTypeCheckKind::Normal && |
| ParsedExpr->getType() && !ParsedExpr->getType()->is<ErrorType>()) { |
| return getReferencedDecl(ParsedExpr); |
| } |
| |
| ConcreteDeclRef ReferencedDecl = nullptr; |
| Expr *ModifiedExpr = ParsedExpr; |
| if (auto T = getTypeOfCompletionContextExpr(P.Context, CurDeclContext, |
| CheckKind, ModifiedExpr, |
| ReferencedDecl)) { |
| // FIXME: even though we don't apply the solution, the type checker may |
| // modify the original expression. We should understand what effect that |
| // may have on code completion. |
| ParsedExpr = ModifiedExpr; |
| |
| return std::make_pair(*T, ReferencedDecl); |
| } |
| return None; |
| } |
| |
| /// \returns true on success, false on failure. |
| bool typecheckParsedType() { |
| assert(ParsedTypeLoc.getTypeRepr() && "should have a TypeRepr"); |
| if (ParsedTypeLoc.wasValidated() && !ParsedTypeLoc.isError()) { |
| return true; |
| } |
| |
| const auto ty = swift::performTypeResolution( |
| ParsedTypeLoc.getTypeRepr(), P.Context, |
| /*isSILMode=*/false, |
| /*isSILType=*/false, |
| CurDeclContext->getGenericEnvironmentOfContext(), |
| /*GenericParams=*/nullptr, |
| CurDeclContext, |
| /*ProduceDiagnostics=*/false); |
| ParsedTypeLoc.setType(ty); |
| if (!ParsedTypeLoc.isError()) { |
| return true; |
| } |
| |
| // It doesn't type check as a type, so see if it's a qualifying module name. |
| if (auto *ITR = dyn_cast<IdentTypeRepr>(ParsedTypeLoc.getTypeRepr())) { |
| const auto &componentRange = ITR->getComponentRange(); |
| // If it has more than one component, it can't be a module name. |
| if (std::distance(componentRange.begin(), componentRange.end()) != 1) |
| return false; |
| |
| const auto &component = componentRange.front(); |
| ImportPath::Module::Builder builder( |
| component->getNameRef().getBaseIdentifier(), |
| component->getLoc()); |
| |
| if (auto Module = Context.getLoadedModule(builder.get())) |
| ParsedTypeLoc.setType(ModuleType::get(Module)); |
| return true; |
| } |
| return false; |
| } |
| |
| public: |
| CodeCompletionCallbacksImpl(Parser &P, |
| CodeCompletionContext &CompletionContext, |
| CodeCompletionConsumer &Consumer) |
| : CodeCompletionCallbacks(P), CompletionContext(CompletionContext), |
| Consumer(Consumer) { |
| } |
| |
| void setAttrTargetDeclKind(Optional<DeclKind> DK) override { |
| if (DK == DeclKind::PatternBinding) |
| DK = DeclKind::Var; |
| else if (DK == DeclKind::Param) |
| // For params, consider the attribute is always for the decl. |
| AttTargetIsIndependent = false; |
| |
| if (!AttTargetIsIndependent) |
| AttTargetDK = DK; |
| } |
| |
| void completeDotExpr(CodeCompletionExpr *E, SourceLoc DotLoc) override; |
| void completeStmtOrExpr(CodeCompletionExpr *E) override; |
| void completePostfixExprBeginning(CodeCompletionExpr *E) override; |
| void completeForEachSequenceBeginning(CodeCompletionExpr *E) override; |
| void completePostfixExpr(Expr *E, bool hasSpace) override; |
| void completePostfixExprParen(Expr *E, Expr *CodeCompletionE) override; |
| void completeExprKeyPath(KeyPathExpr *KPE, SourceLoc DotLoc) override; |
| |
| void completeTypeDeclResultBeginning() override; |
| void completeTypeSimpleBeginning() override; |
| void completeTypeIdentifierWithDot(IdentTypeRepr *ITR) override; |
| void completeTypeIdentifierWithoutDot(IdentTypeRepr *ITR) override; |
| |
| void completeCaseStmtKeyword() override; |
| void completeCaseStmtBeginning(CodeCompletionExpr *E) override; |
| void completeDeclAttrBeginning(bool Sil, bool isIndependent) override; |
| void completeDeclAttrParam(DeclAttrKind DK, int Index) override; |
| void completeEffectsSpecifier(bool hasAsync, bool hasThrows) override; |
| void completeInPrecedenceGroup(SyntaxKind SK) override; |
| void completeNominalMemberBeginning( |
| SmallVectorImpl<StringRef> &Keywords, SourceLoc introducerLoc) override; |
| void completeAccessorBeginning(CodeCompletionExpr *E) override; |
| |
| void completePoundAvailablePlatform() override; |
| void completeImportDecl(ImportPath::Builder &Path) override; |
| void completeUnresolvedMember(CodeCompletionExpr *E, |
| SourceLoc DotLoc) override; |
| void completeCallArg(CodeCompletionExpr *E, bool isFirst) override; |
| void completeLabeledTrailingClosure(CodeCompletionExpr *E, |
| bool isAtStartOfLine) override; |
| |
| bool canPerformCompleteLabeledTrailingClosure() const override { |
| return true; |
| } |
| |
| void completeReturnStmt(CodeCompletionExpr *E) override; |
| void completeYieldStmt(CodeCompletionExpr *E, |
| Optional<unsigned> yieldIndex) override; |
| void completeAfterPoundExpr(CodeCompletionExpr *E, |
| Optional<StmtKind> ParentKind) override; |
| void completeAfterPoundDirective() override; |
| void completePlatformCondition() override; |
| void completeGenericRequirement() override; |
| void completeAfterIfStmt(bool hasElse) override; |
| void completeStmtLabel(StmtKind ParentKind) override; |
| |
| void doneParsing() override; |
| |
| private: |
| void addKeywords(CodeCompletionResultSink &Sink, bool MaybeFuncBody); |
| bool trySolverCompletion(bool MaybeFuncBody); |
| }; |
| } // end anonymous namespace |
| |
| namespace { |
| static bool isTopLevelSubcontext(const DeclContext *DC) { |
| for (; DC && DC->isLocalContext(); DC = DC->getParent()) { |
| switch (DC->getContextKind()) { |
| case DeclContextKind::TopLevelCodeDecl: |
| return true; |
| case DeclContextKind::AbstractFunctionDecl: |
| case DeclContextKind::SubscriptDecl: |
| case DeclContextKind::EnumElementDecl: |
| return false; |
| default: |
| continue; |
| } |
| } |
| return false; |
| } |
| |
| static KnownProtocolKind |
| protocolForLiteralKind(CodeCompletionLiteralKind kind) { |
| switch (kind) { |
| case CodeCompletionLiteralKind::ArrayLiteral: |
| return KnownProtocolKind::ExpressibleByArrayLiteral; |
| case CodeCompletionLiteralKind::BooleanLiteral: |
| return KnownProtocolKind::ExpressibleByBooleanLiteral; |
| case CodeCompletionLiteralKind::ColorLiteral: |
| return KnownProtocolKind::ExpressibleByColorLiteral; |
| case CodeCompletionLiteralKind::ImageLiteral: |
| return KnownProtocolKind::ExpressibleByImageLiteral; |
| case CodeCompletionLiteralKind::DictionaryLiteral: |
| return KnownProtocolKind::ExpressibleByDictionaryLiteral; |
| case CodeCompletionLiteralKind::IntegerLiteral: |
| return KnownProtocolKind::ExpressibleByIntegerLiteral; |
| case CodeCompletionLiteralKind::NilLiteral: |
| return KnownProtocolKind::ExpressibleByNilLiteral; |
| case CodeCompletionLiteralKind::StringLiteral: |
| return KnownProtocolKind::ExpressibleByUnicodeScalarLiteral; |
| case CodeCompletionLiteralKind::Tuple: |
| llvm_unreachable("no such protocol kind"); |
| } |
| |
| llvm_unreachable("Unhandled CodeCompletionLiteralKind in switch."); |
| } |
| |
| static Type |
| defaultTypeLiteralKind(CodeCompletionLiteralKind kind, ASTContext &Ctx) { |
| switch (kind) { |
| case CodeCompletionLiteralKind::BooleanLiteral: |
| return Ctx.getBoolDecl()->getDeclaredInterfaceType(); |
| case CodeCompletionLiteralKind::IntegerLiteral: |
| return Ctx.getIntDecl()->getDeclaredInterfaceType(); |
| case CodeCompletionLiteralKind::StringLiteral: |
| return Ctx.getStringDecl()->getDeclaredInterfaceType(); |
| case CodeCompletionLiteralKind::ArrayLiteral: |
| return Ctx.getArrayDecl()->getDeclaredType(); |
| case CodeCompletionLiteralKind::DictionaryLiteral: |
| return Ctx.getDictionaryDecl()->getDeclaredType(); |
| case CodeCompletionLiteralKind::NilLiteral: |
| case CodeCompletionLiteralKind::ColorLiteral: |
| case CodeCompletionLiteralKind::ImageLiteral: |
| case CodeCompletionLiteralKind::Tuple: |
| return Type(); |
| } |
| |
| llvm_unreachable("Unhandled CodeCompletionLiteralKind in switch."); |
| } |
| |
| /// Whether funcType has a single argument (not including defaulted arguments) |
| /// that is of type () -> (). |
| static bool hasTrivialTrailingClosure(const FuncDecl *FD, |
| AnyFunctionType *funcType) { |
| ParameterListInfo paramInfo(funcType->getParams(), FD, |
| /*skipCurriedSelf*/ FD->hasCurriedSelf()); |
| |
| if (paramInfo.size() - paramInfo.numNonDefaultedParameters() == 1) { |
| auto param = funcType->getParams().back(); |
| if (!param.isAutoClosure()) { |
| if (auto Fn = param.getOldType()->getAs<AnyFunctionType>()) { |
| return Fn->getParams().empty() && Fn->getResult()->isVoid(); |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| /// Build completions by doing visible decl lookup from a context. |
| class CompletionLookup final : public swift::VisibleDeclConsumer { |
| CodeCompletionResultSink &Sink; |
| ASTContext &Ctx; |
| const DeclContext *CurrDeclContext; |
| ModuleDecl *CurrModule; |
| ClangImporter *Importer; |
| CodeCompletionContext *CompletionContext; |
| |
| enum class LookupKind { |
| ValueExpr, |
| ValueInDeclContext, |
| EnumElement, |
| Type, |
| TypeInDeclContext, |
| ImportFromModule, |
| GenericRequirement, |
| }; |
| |
| LookupKind Kind; |
| |
| /// Type of the user-provided expression for LookupKind::ValueExpr |
| /// completions. |
| Type ExprType; |
| |
| /// Whether the expr is of statically inferred metatype. |
| bool IsStaticMetatype = false; |
| |
| /// User-provided base type for LookupKind::Type completions. |
| Type BaseType; |
| |
| /// Expected types of the code completion expression. |
| ExpectedTypeContext expectedTypeContext; |
| |
| bool HaveDot = false; |
| bool IsUnwrappedOptional = false; |
| SourceLoc DotLoc; |
| bool NeedLeadingDot = false; |
| |
| bool NeedOptionalUnwrap = false; |
| unsigned NumBytesToEraseForOptionalUnwrap = 0; |
| |
| bool HaveLParen = false; |
| bool IsSuperRefExpr = false; |
| bool IsSelfRefExpr = false; |
| bool IsKeyPathExpr = false; |
| bool IsSwiftKeyPathExpr = false; |
| bool IsAfterSwiftKeyPathRoot = false; |
| bool IsDynamicLookup = false; |
| bool PreferFunctionReferencesToCalls = false; |
| bool HaveLeadingSpace = false; |
| |
| bool CheckForDuplicates = false; |
| llvm::DenseSet<std::pair<const Decl *, Type>> PreviouslySeen; |
| |
| bool IncludeInstanceMembers = false; |
| |
| /// True if we are code completing inside a static method. |
| bool InsideStaticMethod = false; |
| |
| /// Innermost method that the code completion point is in. |
| const AbstractFunctionDecl *CurrentMethod = nullptr; |
| |
| Optional<SemanticContextKind> ForcedSemanticContext = None; |
| bool IsUnresolvedMember = false; |
| |
| public: |
| bool FoundFunctionCalls = false; |
| bool FoundFunctionsWithoutFirstKeyword = false; |
| |
| private: |
| void foundFunction(const AbstractFunctionDecl *AFD) { |
| FoundFunctionCalls = true; |
| const DeclName Name = AFD->getName(); |
| auto ArgNames = Name.getArgumentNames(); |
| if (ArgNames.empty()) |
| return; |
| if (ArgNames[0].empty()) |
| FoundFunctionsWithoutFirstKeyword = true; |
| } |
| |
| void foundFunction(const AnyFunctionType *AFT) { |
| FoundFunctionCalls = true; |
| auto Params = AFT->getParams(); |
| if (Params.empty()) |
| return; |
| if (Params.size() == 1 && !Params[0].hasLabel()) { |
| FoundFunctionsWithoutFirstKeyword = true; |
| return; |
| } |
| if (!Params[0].hasLabel()) |
| FoundFunctionsWithoutFirstKeyword = true; |
| } |
| |
| void setClangDeclKeywords(const ValueDecl *VD, CommandWordsPairs &Pairs, |
| CodeCompletionResultBuilder &Builder) { |
| if (auto *CD = VD->getClangDecl()) { |
| clang::comments::getClangDocKeyword(*Importer, CD, Pairs); |
| } else { |
| swift::markup::getSwiftDocKeyword(VD, Pairs); |
| } |
| Builder.addDeclDocCommentWords(llvm::makeArrayRef(Pairs)); |
| } |
| |
| /// Returns \c true if \p TAD is usable as a first type of a requirement in |
| /// \c where clause for a context. |
| /// \p selfTy must be a \c Self type of the context. |
| static bool canBeUsedAsRequirementFirstType(Type selfTy, TypeAliasDecl *TAD) { |
| auto T = TAD->getDeclaredInterfaceType(); |
| auto subMap = selfTy->getMemberSubstitutionMap(TAD->getParentModule(), TAD); |
| T = T.subst(subMap)->getCanonicalType(); |
| |
| ArchetypeType *archeTy = T->getAs<ArchetypeType>(); |
| if (!archeTy) |
| return false; |
| archeTy = archeTy->getRoot(); |
| |
| // For protocol, the 'archeTy' should match with the 'baseTy' which is the |
| // dynamic 'Self' type of the protocol. For nominal decls, 'archTy' should |
| // be one of the generic params in 'selfTy'. Search 'archeTy' in 'baseTy'. |
| return selfTy.findIf([&](Type T) { return archeTy->isEqual(T); }); |
| } |
| |
| public: |
| struct RequestedResultsTy { |
| const ModuleDecl *TheModule; |
| bool OnlyTypes; |
| bool OnlyPrecedenceGroups; |
| bool NeedLeadingDot; |
| bool IncludeModuleQualifier; |
| |
| static RequestedResultsTy fromModule(const ModuleDecl *TheModule) { |
| return { TheModule, false, false, false, true }; |
| } |
| |
| RequestedResultsTy onlyTypes() const { |
| return { TheModule, true, false, NeedLeadingDot, IncludeModuleQualifier }; |
| } |
| |
| RequestedResultsTy onlyPrecedenceGroups() const { |
| assert(!OnlyTypes && "onlyTypes() already includes precedence groups"); |
| return { TheModule, false, true, false, true }; |
| } |
| |
| RequestedResultsTy needLeadingDot(bool NeedDot) const { |
| return { |
| TheModule, OnlyTypes, OnlyPrecedenceGroups, NeedDot, |
| IncludeModuleQualifier |
| }; |
| } |
| |
| RequestedResultsTy withModuleQualifier(bool IncludeModule) const { |
| return { |
| TheModule, OnlyTypes, OnlyPrecedenceGroups, NeedLeadingDot, |
| IncludeModule |
| }; |
| } |
| |
| static RequestedResultsTy toplevelResults() { |
| return { nullptr, false, false, false, true }; |
| } |
| }; |
| |
| std::vector<RequestedResultsTy> RequestedCachedResults; |
| |
| public: |
| CompletionLookup(CodeCompletionResultSink &Sink, |
| ASTContext &Ctx, |
| const DeclContext *CurrDeclContext, |
| CodeCompletionContext *CompletionContext = nullptr) |
| : Sink(Sink), Ctx(Ctx), CurrDeclContext(CurrDeclContext), |
| CurrModule(CurrDeclContext ? CurrDeclContext->getParentModule() |
| : nullptr), |
| Importer(static_cast<ClangImporter *>(CurrDeclContext->getASTContext(). |
| getClangModuleLoader())), |
| CompletionContext(CompletionContext) { |
| // Determine if we are doing code completion inside a static method. |
| if (CurrDeclContext) { |
| CurrentMethod = CurrDeclContext->getInnermostMethodContext(); |
| if (auto *FD = dyn_cast_or_null<FuncDecl>(CurrentMethod)) |
| InsideStaticMethod = FD->isStatic(); |
| } |
| } |
| |
| void setHaveDot(SourceLoc DotLoc) { |
| HaveDot = true; |
| this->DotLoc = DotLoc; |
| } |
| |
| void setIsUnwrappedOptional(bool value) { |
| IsUnwrappedOptional = value; |
| } |
| |
| void setIsStaticMetatype(bool value) { |
| IsStaticMetatype = value; |
| } |
| |
| void setExpectedTypes(ArrayRef<Type> Types, |
| bool isImplicitSingleExpressionReturn, |
| bool preferNonVoid = false) { |
| expectedTypeContext.isImplicitSingleExpressionReturn = |
| isImplicitSingleExpressionReturn; |
| expectedTypeContext.preferNonVoid = preferNonVoid; |
| expectedTypeContext.possibleTypes.clear(); |
| expectedTypeContext.possibleTypes.reserve(Types.size()); |
| for (auto T : Types) |
| if (T) |
| expectedTypeContext.possibleTypes.push_back(T); |
| } |
| |
| void setIdealExpectedType(Type Ty) { |
| expectedTypeContext.idealType = Ty; |
| } |
| |
| CodeCompletionContext::TypeContextKind typeContextKind() const { |
| if (expectedTypeContext.empty() && !expectedTypeContext.preferNonVoid) { |
| return CodeCompletionContext::TypeContextKind::None; |
| } else if (expectedTypeContext.isImplicitSingleExpressionReturn) { |
| return CodeCompletionContext::TypeContextKind::SingleExpressionBody; |
| } else { |
| return CodeCompletionContext::TypeContextKind::Required; |
| } |
| } |
| |
| bool needDot() const { |
| return NeedLeadingDot; |
| } |
| |
| void setHaveLParen(bool Value) { |
| HaveLParen = Value; |
| } |
| |
| void setIsSuperRefExpr(bool Value = true) { |
| IsSuperRefExpr = Value; |
| } |
| |
| void setIsSelfRefExpr(bool value) { IsSelfRefExpr = value; } |
| |
| void setIsKeyPathExpr() { |
| IsKeyPathExpr = true; |
| } |
| |
| void shouldCheckForDuplicates(bool value = true) { |
| CheckForDuplicates = value; |
| } |
| |
| void setIsSwiftKeyPathExpr(bool onRoot) { |
| IsSwiftKeyPathExpr = true; |
| IsAfterSwiftKeyPathRoot = onRoot; |
| } |
| |
| void setIsDynamicLookup() { |
| IsDynamicLookup = true; |
| } |
| |
| void setPreferFunctionReferencesToCalls() { |
| PreferFunctionReferencesToCalls = true; |
| } |
| |
| void setHaveLeadingSpace(bool value) { HaveLeadingSpace = value; } |
| |
| void includeInstanceMembers() { |
| IncludeInstanceMembers = true; |
| } |
| |
| void addSubModuleNames(std::vector<std::pair<std::string, bool>> |
| &SubModuleNameVisibilityPairs) { |
| for (auto &Pair : SubModuleNameVisibilityPairs) { |
| CodeCompletionResultBuilder Builder(Sink, |
| CodeCompletionResult::ResultKind:: |
| Declaration, |
| SemanticContextKind::None, |
| expectedTypeContext); |
| auto MD = ModuleDecl::create(Ctx.getIdentifier(Pair.first), Ctx); |
| Builder.setAssociatedDecl(MD); |
| Builder.addBaseName(MD->getNameStr()); |
| Builder.addTypeAnnotation("Module"); |
| if (Pair.second) |
| Builder.setNotRecommended(CodeCompletionResult::NotRecommendedReason:: |
| Redundant); |
| } |
| } |
| |
| void collectImportedModules(llvm::StringSet<> &ImportedModules) { |
| SmallVector<ImportedModule, 16> Imported; |
| SmallVector<ImportedModule, 16> FurtherImported; |
| CurrDeclContext->getParentSourceFile()->getImportedModules( |
| Imported, |
| {ModuleDecl::ImportFilterKind::Exported, |
| ModuleDecl::ImportFilterKind::Default, |
| ModuleDecl::ImportFilterKind::ImplementationOnly}); |
| while (!Imported.empty()) { |
| ModuleDecl *MD = Imported.back().importedModule; |
| Imported.pop_back(); |
| if (!ImportedModules.insert(MD->getNameStr()).second) |
| continue; |
| FurtherImported.clear(); |
| MD->getImportedModules(FurtherImported, |
| ModuleDecl::ImportFilterKind::Exported); |
| Imported.append(FurtherImported.begin(), FurtherImported.end()); |
| } |
| } |
| |
| void addModuleName( |
| ModuleDecl *MD, |
| Optional<CodeCompletionResult::NotRecommendedReason> R = None) { |
| |
| // Don't add underscored cross-import overlay modules. |
| if (MD->getDeclaringModuleIfCrossImportOverlay()) |
| return; |
| |
| CodeCompletionResultBuilder Builder( |
| Sink, |
| CodeCompletionResult::ResultKind::Declaration, |
| SemanticContextKind::None, |
| expectedTypeContext); |
| Builder.setAssociatedDecl(MD); |
| Builder.addBaseName(MD->getNameStr()); |
| Builder.addTypeAnnotation("Module"); |
| if (R) |
| Builder.setNotRecommended(*R); |
| } |
| |
| void addImportModuleNames() { |
| SmallVector<Identifier, 0> ModuleNames; |
| Ctx.getVisibleTopLevelModuleNames(ModuleNames); |
| |
| llvm::StringSet<> ImportedModules; |
| collectImportedModules(ImportedModules); |
| |
| auto mainModuleName = CurrModule->getName(); |
| for (auto ModuleName : ModuleNames) { |
| if (ModuleName.str().startswith("_") || |
| ModuleName == mainModuleName || |
| ModuleName == Ctx.SwiftShimsModuleName || |
| ModuleName.str() == SWIFT_ONONE_SUPPORT) |
| continue; |
| |
| auto MD = ModuleDecl::create(ModuleName, Ctx); |
| Optional<CodeCompletionResult::NotRecommendedReason> Reason = None; |
| |
| // Imported modules are not recommended. |
| if (ImportedModules.count(MD->getNameStr()) != 0) |
| Reason = CodeCompletionResult::NotRecommendedReason::Redundant; |
| |
| addModuleName(MD, Reason); |
| } |
| } |
| |
| SemanticContextKind getSemanticContext(const Decl *D, |
| DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo) { |
| if (ForcedSemanticContext) |
| return *ForcedSemanticContext; |
| |
| switch (Reason) { |
| case DeclVisibilityKind::LocalVariable: |
| case DeclVisibilityKind::FunctionParameter: |
| case DeclVisibilityKind::GenericParameter: |
| return SemanticContextKind::Local; |
| |
| case DeclVisibilityKind::MemberOfCurrentNominal: |
| if (IsSuperRefExpr && |
| CurrentMethod && CurrentMethod->getOverriddenDecl() == D) |
| return SemanticContextKind::ExpressionSpecific; |
| return SemanticContextKind::CurrentNominal; |
| |
| case DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal: |
| case DeclVisibilityKind::MemberOfSuper: |
| return SemanticContextKind::Super; |
| |
| case DeclVisibilityKind::MemberOfOutsideNominal: |
| return SemanticContextKind::OutsideNominal; |
| |
| case DeclVisibilityKind::VisibleAtTopLevel: |
| if (CurrDeclContext && D->getModuleContext() == CurrModule) { |
| // Treat global variables from the same source file as local when |
| // completing at top-level. |
| if (isa<VarDecl>(D) && isTopLevelSubcontext(CurrDeclContext) && |
| D->getDeclContext()->getParentSourceFile() == |
| CurrDeclContext->getParentSourceFile()) { |
| return SemanticContextKind::Local; |
| } else { |
| return SemanticContextKind::CurrentModule; |
| } |
| } else { |
| return SemanticContextKind::OtherModule; |
| } |
| |
| case DeclVisibilityKind::DynamicLookup: |
| switch (dynamicLookupInfo.getKind()) { |
| case DynamicLookupInfo::None: |
| llvm_unreachable("invalid DynamicLookupInfo::Kind for dynamic lookup"); |
| |
| case DynamicLookupInfo::AnyObject: |
| // AnyObject results can come from different modules, including the |
| // current module, but we always assign them the OtherModule semantic |
| // context. These declarations are uniqued by signature, so it is |
| // totally random (determined by the hash function) which of the |
| // equivalent declarations (across multiple modules) we will get. |
| return SemanticContextKind::OtherModule; |
| |
| case DynamicLookupInfo::KeyPathDynamicMember: |
| // Use the visibility of the underlying declaration. |
| // FIXME: KeyPath<AnyObject, U> !?!? |
| assert(dynamicLookupInfo.getKeyPathDynamicMember().originalVisibility != |
| DeclVisibilityKind::DynamicLookup); |
| return getSemanticContext( |
| D, dynamicLookupInfo.getKeyPathDynamicMember().originalVisibility, |
| {}); |
| } |
| |
| case DeclVisibilityKind::MemberOfProtocolDerivedByCurrentNominal: |
| llvm_unreachable("should not see this kind"); |
| } |
| llvm_unreachable("unhandled kind"); |
| } |
| |
| bool isUnresolvedMemberIdealType(Type Ty) { |
| assert(Ty); |
| if (!IsUnresolvedMember) |
| return false; |
| Type idealTy = expectedTypeContext.idealType; |
| if (!idealTy) |
| return false; |
| /// Consider optional object type is the ideal. |
| /// For exmaple: |
| /// enum MyEnum { case foo, bar } |
| /// func foo(_: MyEnum?) |
| /// fooo(.<HERE>) |
| /// Prefer '.foo' and '.bar' over '.some' and '.none'. |
| idealTy = idealTy->lookThroughAllOptionalTypes(); |
| return idealTy->isEqual(Ty); |
| } |
| |
| void addValueBaseName(CodeCompletionResultBuilder &Builder, |
| DeclBaseName Name) { |
| auto NameStr = Name.userFacingName(); |
| bool shouldEscapeKeywords; |
| if (Name.isSpecial()) { |
| // Special names (i.e. 'init') are always displayed as its user facing |
| // name. |
| shouldEscapeKeywords = false; |
| } else if (ExprType) { |
| // After dot. User can write any keyword after '.' except for `init` and |
| // `self`. E.g. 'func `init`()' must be called by 'expr.`init`()'. |
| shouldEscapeKeywords = NameStr == "self" || NameStr == "init"; |
| } else { |
| // As primary expresson. We have to escape almost every keywords except |
| // for 'self' and 'Self'. |
| shouldEscapeKeywords = NameStr != "self" && NameStr != "Self"; |
| } |
| |
| if (!shouldEscapeKeywords) { |
| Builder.addBaseName(NameStr); |
| } else { |
| SmallString<16> buffer; |
| Builder.addBaseName(Builder.escapeKeyword(NameStr, true, buffer)); |
| } |
| } |
| |
| void addLeadingDot(CodeCompletionResultBuilder &Builder) { |
| if (NeedOptionalUnwrap) { |
| Builder.setNumBytesToErase(NumBytesToEraseForOptionalUnwrap); |
| Builder.addQuestionMark(); |
| Builder.addLeadingDot(); |
| return; |
| } |
| if (needDot()) |
| Builder.addLeadingDot(); |
| } |
| |
| void addTypeAnnotation(CodeCompletionResultBuilder &Builder, Type T, |
| GenericSignature genericSig = GenericSignature()) { |
| PrintOptions PO; |
| PO.OpaqueReturnTypePrinting = |
| PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; |
| if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) |
| PO.setBaseType(typeContext->getDeclaredTypeInContext()); |
| Builder.addTypeAnnotation(eraseArchetypes(T, genericSig), PO); |
| Builder.setExpectedTypeRelation( |
| calculateMaxTypeRelation(T, expectedTypeContext, CurrDeclContext)); |
| } |
| |
| void addTypeAnnotationForImplicitlyUnwrappedOptional( |
| CodeCompletionResultBuilder &Builder, Type T, |
| GenericSignature genericSig = GenericSignature(), |
| bool dynamicOrOptional = false) { |
| |
| std::string suffix; |
| // FIXME: This retains previous behavior, but in reality the type of dynamic |
| // lookups is IUO, not Optional as it is for the @optional attribute. |
| if (dynamicOrOptional) { |
| T = T->getOptionalObjectType(); |
| suffix = "?"; |
| } |
| |
| PrintOptions PO; |
| PO.PrintOptionalAsImplicitlyUnwrapped = true; |
| PO.OpaqueReturnTypePrinting = |
| PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; |
| if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) |
| PO.setBaseType(typeContext->getDeclaredTypeInContext()); |
| Builder.addTypeAnnotation(eraseArchetypes(T, genericSig), PO, suffix); |
| Builder.setExpectedTypeRelation( |
| calculateMaxTypeRelation(T, expectedTypeContext, CurrDeclContext)); |
| } |
| |
| /// For printing in code completion results, replace archetypes with |
| /// protocol compositions. |
| /// |
| /// FIXME: Perhaps this should be an option in PrintOptions instead. |
| Type eraseArchetypes(Type type, GenericSignature genericSig) { |
| if (!genericSig) |
| return type; |
| |
| auto buildProtocolComposition = [&](ArrayRef<ProtocolDecl *> protos) -> Type { |
| SmallVector<Type, 2> types; |
| for (auto proto : protos) |
| types.push_back(proto->getDeclaredInterfaceType()); |
| return ProtocolCompositionType::get(Ctx, types, |
| /*HasExplicitAnyObject=*/false); |
| }; |
| |
| if (auto *genericFuncType = type->getAs<GenericFunctionType>()) { |
| SmallVector<AnyFunctionType::Param, 8> erasedParams; |
| for (const auto ¶m : genericFuncType->getParams()) { |
| auto erasedTy = eraseArchetypes(param.getPlainType(), genericSig); |
| erasedParams.emplace_back(erasedTy, param.getLabel(), |
| param.getParameterFlags()); |
| } |
| return GenericFunctionType::get(genericSig, |
| erasedParams, |
| eraseArchetypes(genericFuncType->getResult(), genericSig), |
| genericFuncType->getExtInfo()); |
| } |
| |
| return type.transform([&](Type t) -> Type { |
| // FIXME: Code completion should only deal with one or the other, |
| // and not both. |
| if (auto *archetypeType = t->getAs<ArchetypeType>()) { |
| // Don't erase opaque archetype. |
| if (isa<OpaqueTypeArchetypeType>(archetypeType)) |
| return t; |
| |
| auto protos = archetypeType->getConformsTo(); |
| if (!protos.empty()) |
| return buildProtocolComposition(protos); |
| } |
| |
| if (t->isTypeParameter()) { |
| const auto protos = genericSig->getRequiredProtocols(t); |
| if (!protos.empty()) |
| return buildProtocolComposition(protos); |
| } |
| |
| return t; |
| }); |
| } |
| |
| Type getTypeOfMember(const ValueDecl *VD, |
| DynamicLookupInfo dynamicLookupInfo) { |
| switch (dynamicLookupInfo.getKind()) { |
| case DynamicLookupInfo::None: |
| return getTypeOfMember(VD, this->ExprType); |
| case DynamicLookupInfo::AnyObject: |
| return getTypeOfMember(VD, Type()); |
| case DynamicLookupInfo::KeyPathDynamicMember: { |
| auto &keyPathInfo = dynamicLookupInfo.getKeyPathDynamicMember(); |
| |
| // Map the result of VD to keypath member lookup results. |
| // Given: |
| // struct Wrapper<T> { |
| // subscript<U>(dynamicMember: KeyPath<T, U>) -> Wrapped<U> { get } |
| // } |
| // struct Circle { |
| // var center: Point { get } |
| // var radius: Length { get } |
| // } |
| // |
| // Consider 'Wrapper<Circle>.center'. |
| // 'VD' is 'Circle.center' decl. |
| // 'keyPathInfo.subscript' is 'Wrapper<T>.subscript' decl. |
| // 'keyPathInfo.baseType' is 'Wrapper<Circle>' type. |
| |
| // FIXME: Handle nested keypath member lookup. |
| // i.e. cases where 'ExprType' != 'keyPathInfo.baseType'. |
| |
| auto *SD = keyPathInfo.subscript; |
| const auto elementTy = SD->getElementInterfaceType(); |
| if (!elementTy->hasTypeParameter()) |
| return elementTy; |
| |
| // Map is: |
| // { Ï„_0_0(T) => Circle |
| // Ï„_1_0(U) => U } |
| auto subs = keyPathInfo.baseType->getMemberSubstitutions(SD); |
| |
| // If the keyPath result type has type parameters, that might affect the |
| // subscript result type. |
| auto keyPathResultTy = getResultTypeOfKeypathDynamicMember(SD)-> |
| mapTypeOutOfContext(); |
| if (keyPathResultTy->hasTypeParameter()) { |
| auto keyPathRootTy = getRootTypeOfKeypathDynamicMember(SD). |
| subst(QueryTypeSubstitutionMap{subs}, |
| LookUpConformanceInModule(CurrModule)); |
| |
| // The result type of the VD. |
| // i.e. 'Circle.center' => 'Point'. |
| auto innerResultTy = getTypeOfMember(VD, keyPathRootTy); |
| |
| if (auto paramTy = keyPathResultTy->getAs<GenericTypeParamType>()) { |
| // Replace keyPath result type in the map with the inner result type. |
| // i.e. Make the map as: |
| // { Ï„_0_0(T) => Circle |
| // Ï„_1_0(U) => Point } |
| auto key = |
| paramTy->getCanonicalType()->castTo<GenericTypeParamType>(); |
| subs[key] = innerResultTy; |
| } else { |
| // FIXME: Handle the case where the KeyPath result is generic. |
| // e.g. 'subscript<U>(dynamicMember: KeyPath<T, Box<U>>) -> Bag<U>' |
| // For now, just return the inner type. |
| return innerResultTy; |
| } |
| } |
| |
| // Substitute the element type of the subscript using modified map. |
| // i.e. 'Wrapped<U>' => 'Wrapped<Point>'. |
| return elementTy.subst(QueryTypeSubstitutionMap{subs}, |
| LookUpConformanceInModule(CurrModule)); |
| } |
| } |
| llvm_unreachable("Unhandled DynamicLookupInfo Kind in switch"); |
| } |
| |
| Type getTypeOfMember(const ValueDecl *VD, Type ExprType) { |
| Type T = VD->getInterfaceType(); |
| assert(!T.isNull()); |
| |
| if (ExprType) { |
| Type ContextTy = VD->getDeclContext()->getDeclaredInterfaceType(); |
| if (ContextTy) { |
| // Look through lvalue types and metatypes |
| Type MaybeNominalType = ExprType->getRValueType(); |
| |
| if (auto Metatype = MaybeNominalType->getAs<MetatypeType>()) |
| MaybeNominalType = Metatype->getInstanceType(); |
| |
| if (auto SelfType = MaybeNominalType->getAs<DynamicSelfType>()) |
| MaybeNominalType = SelfType->getSelfType(); |
| |
| // For optional protocol requirements and dynamic dispatch, |
| // strip off optionality from the base type, but only if |
| // we're not actually completing a member of Optional. |
| if (!ContextTy->getOptionalObjectType() && |
| MaybeNominalType->getOptionalObjectType()) |
| MaybeNominalType = MaybeNominalType->getOptionalObjectType(); |
| |
| // For dynamic lookup don't substitute in the base type. |
| if (MaybeNominalType->isAnyObject()) |
| return T; |
| |
| // FIXME: Sometimes ExprType is the type of the member here, |
| // and not the type of the base. That is inconsistent and |
| // should be cleaned up. |
| if (!MaybeNominalType->mayHaveMembers()) |
| return T; |
| |
| // We can't do anything if the base type has unbound generic parameters. |
| if (MaybeNominalType->hasUnboundGenericType()) |
| return T; |
| |
| // For everything else, substitute in the base type. |
| auto Subs = MaybeNominalType->getMemberSubstitutionMap(CurrModule, VD); |
| |
| // Pass in DesugarMemberTypes so that we see the actual |
| // concrete type witnesses instead of type alias types. |
| T = T.subst(Subs, SubstFlags::DesugarMemberTypes); |
| } |
| } |
| |
| return T; |
| } |
| |
| Type getAssociatedTypeType(const AssociatedTypeDecl *ATD) { |
| Type BaseTy = BaseType; |
| if (!BaseTy) |
| BaseTy = ExprType; |
| if (!BaseTy && CurrDeclContext) |
| BaseTy = CurrDeclContext->getInnermostTypeContext() |
| ->getDeclaredTypeInContext(); |
| if (BaseTy) { |
| BaseTy = BaseTy->getInOutObjectType()->getMetatypeInstanceType(); |
| if (auto NTD = BaseTy->getAnyNominal()) { |
| auto *Module = NTD->getParentModule(); |
| auto Conformance = Module->lookupConformance( |
| BaseTy, ATD->getProtocol()); |
| if (Conformance.isConcrete()) { |
| return Conformance.getConcrete()->getTypeWitness( |
| const_cast<AssociatedTypeDecl *>(ATD)); |
| } |
| } |
| } |
| return Type(); |
| } |
| |
| void addVarDeclRef(const VarDecl *VD, DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo) { |
| if (!VD->hasName()) |
| return; |
| |
| const Identifier Name = VD->getName(); |
| assert(!Name.empty() && "name should not be empty"); |
| |
| CommandWordsPairs Pairs; |
| CodeCompletionResultBuilder Builder( |
| Sink, CodeCompletionResult::ResultKind::Declaration, |
| getSemanticContext(VD, Reason, dynamicLookupInfo), expectedTypeContext); |
| Builder.setAssociatedDecl(VD); |
| addLeadingDot(Builder); |
| addValueBaseName(Builder, Name); |
| setClangDeclKeywords(VD, Pairs, Builder); |
| |
| // "not recommended" in its own getter. |
| if (Kind == LookupKind::ValueInDeclContext) { |
| if (auto accessor = dyn_cast<AccessorDecl>(CurrDeclContext)) { |
| if (accessor->getStorage() == VD && accessor->isGetter()) |
| Builder.setNotRecommended(CodeCompletionResult::NoReason); |
| } |
| } |
| |
| if (!VD->hasInterfaceType()) |
| return; |
| |
| // Add a type annotation. |
| Type VarType = getTypeOfMember(VD, dynamicLookupInfo); |
| if (auto *PD = dyn_cast<ParamDecl>(VD)) { |
| if (Name != Ctx.Id_self && PD->isInOut()) { |
| // It is useful to show inout for function parameters. |
| // But for 'self' it is just noise. |
| VarType = InOutType::get(VarType); |
| } |
| } |
| auto DynamicOrOptional = |
| IsDynamicLookup || VD->getAttrs().hasAttribute<OptionalAttr>(); |
| if (DynamicOrOptional) { |
| // Values of properties that were found on a AnyObject have |
| // Optional<T> type. Same applies to optional members. |
| VarType = OptionalType::get(VarType); |
| } |
| |
| auto genericSig = |
| VD->getInnermostDeclContext()->getGenericSignatureOfContext(); |
| if (VD->isImplicitlyUnwrappedOptional()) |
| addTypeAnnotationForImplicitlyUnwrappedOptional( |
| Builder, VarType, genericSig, DynamicOrOptional); |
| else |
| addTypeAnnotation(Builder, VarType, genericSig); |
| |
| if (isUnresolvedMemberIdealType(VarType)) |
| Builder.setSemanticContext(SemanticContextKind::ExpressionSpecific); |
| } |
| |
| static bool hasInterestingDefaultValues(const AbstractFunctionDecl *func) { |
| if (!func) return false; |
| |
| for (auto param : *func->getParameters()) { |
| switch (param->getDefaultArgumentKind()) { |
| case DefaultArgumentKind::Normal: |
| case DefaultArgumentKind::StoredProperty: |
| case DefaultArgumentKind::Inherited: // FIXME: include this? |
| return true; |
| default: |
| break; |
| } |
| } |
| return false; |
| } |
| |
| /// Build argument patterns for calling. Returns \c true if any content was |
| /// added to \p Builder. If \p declParams is non-empty, the size must match |
| /// with \p typeParams. |
| bool addCallArgumentPatterns(CodeCompletionResultBuilder &Builder, |
| ArrayRef<AnyFunctionType::Param> typeParams, |
| ArrayRef<const ParamDecl *> declParams, |
| GenericSignature genericSig, |
| bool includeDefaultArgs = true) { |
| assert(declParams.empty() || typeParams.size() == declParams.size()); |
| |
| bool modifiedBuilder = false; |
| |
| // Determine whether we should skip this argument because it is defaulted. |
| auto shouldSkipArg = [&](const ParamDecl *PD) -> bool { |
| switch (PD->getDefaultArgumentKind()) { |
| case DefaultArgumentKind::None: |
| return false; |
| |
| case DefaultArgumentKind::Normal: |
| case DefaultArgumentKind::StoredProperty: |
| case DefaultArgumentKind::Inherited: |
| case DefaultArgumentKind::NilLiteral: |
| case DefaultArgumentKind::EmptyArray: |
| case DefaultArgumentKind::EmptyDictionary: |
| return !includeDefaultArgs; |
| |
| #define MAGIC_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ |
| case DefaultArgumentKind::NAME: |
| #include "swift/AST/MagicIdentifierKinds.def" |
| // Skip parameters that are defaulted to source location or other |
| // caller context information. Users typically don't want to specify |
| // these parameters. |
| return true; |
| } |
| |
| llvm_unreachable("Unhandled DefaultArgumentKind in switch."); |
| }; |
| |
| bool NeedComma = false; |
| // Iterate over each parameter. |
| for (unsigned i = 0; i != typeParams.size(); ++i) { |
| auto &typeParam = typeParams[i]; |
| |
| Identifier argName; |
| Identifier bodyName; |
| bool isIUO = false; |
| |
| if (!declParams.empty()) { |
| auto *PD = declParams[i]; |
| if (shouldSkipArg(PD)) |
| continue; |
| argName = PD->getArgumentName(); |
| bodyName = PD->getParameterName(); |
| isIUO = PD->isImplicitlyUnwrappedOptional(); |
| } else { |
| isIUO = false; |
| argName = typeParam.getLabel(); |
| } |
| |
| bool isVariadic = typeParam.isVariadic(); |
| bool isInOut = typeParam.isInOut(); |
| bool isAutoclosure = typeParam.isAutoClosure(); |
| Type paramTy = typeParam.getPlainType(); |
| if (isVariadic) |
| paramTy = ParamDecl::getVarargBaseTy(paramTy); |
| |
| if (NeedComma) |
| Builder.addComma(); |
| Type contextTy; |
| if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) |
| contextTy = typeContext->getDeclaredTypeInContext(); |
| |
| Builder.addCallParameter(argName, bodyName, |
| eraseArchetypes(paramTy, genericSig), contextTy, |
| isVariadic, isInOut, isIUO, isAutoclosure, |
| /*useUnderscoreLabel=*/false, |
| /*isLabeledTrailingClosure=*/false); |
| |
| modifiedBuilder = true; |
| NeedComma = true; |
| } |
| return modifiedBuilder; |
| } |
| |
| /// Build argument patterns for calling. Returns \c true if any content was |
| /// added to \p Builder. If \p Params is non-nullptr, \F |
| bool addCallArgumentPatterns(CodeCompletionResultBuilder &Builder, |
| const AnyFunctionType *AFT, |
| const ParameterList *Params, |
| GenericSignature genericSig, |
| bool includeDefaultArgs = true) { |
| ArrayRef<const ParamDecl *> declParams; |
| if (Params) |
| declParams = Params->getArray(); |
| return addCallArgumentPatterns(Builder, AFT->getParams(), declParams, |
| genericSig, includeDefaultArgs); |
| } |
| |
| static void addThrows(CodeCompletionResultBuilder &Builder, |
| const AnyFunctionType *AFT, |
| const AbstractFunctionDecl *AFD) { |
| if (AFD && AFD->getAttrs().hasAttribute<RethrowsAttr>()) |
| Builder.addAnnotatedRethrows(); |
| else if (AFT->isThrowing()) |
| Builder.addAnnotatedThrows(); |
| } |
| |
| void addPoundAvailable(Optional<StmtKind> ParentKind) { |
| if (ParentKind != StmtKind::If && ParentKind != StmtKind::Guard) |
| return; |
| CodeCompletionResultBuilder Builder(Sink, CodeCompletionResult::ResultKind::Keyword, |
| SemanticContextKind::ExpressionSpecific, expectedTypeContext); |
| Builder.addBaseName("available"); |
| Builder.addLeftParen(); |
| Builder.addSimpleTypedParameter("Platform", /*IsVarArg=*/true); |
| Builder.addComma(); |
| Builder.addTextChunk("*"); |
| Builder.addRightParen(); |
| } |
| |
| void addPoundSelector(bool needPound) { |
| // #selector is only available when the Objective-C runtime is. |
| if (!Ctx.LangOpts.EnableObjCInterop) return; |
| |
| CodeCompletionResultBuilder Builder( |
| Sink, CodeCompletionResult::ResultKind::Keyword, |
| SemanticContextKind::None, {}); |
| if (needPound) |
| Builder.addTextChunk("#selector"); |
| else |
| Builder.addTextChunk("selector"); |
| Builder.addLeftParen(); |
| Builder.addSimpleTypedParameter("@objc method", /*IsVarArg=*/false); |
| Builder.addRightParen(); |
| Builder.addTypeAnnotation("Selector"); |
| // This function is called only if the context type is 'Selector'. |
| Builder.setExpectedTypeRelation( |
| CodeCompletionResult::ExpectedTypeRelation::Identical); |
| } |
| |
| void addPoundKeyPath(bool needPound) { |
| // #keyPath is only available when the Objective-C runtime is. |
| if (!Ctx.LangOpts.EnableObjCInterop) return; |
| |
| CodeCompletionResultBuilder Builder( |
| Sink, CodeCompletionResult::ResultKind::Keyword, |
| SemanticContextKind::None, {}); |
| if (needPound) |
| Builder.addTextChunk("#keyPath"); |
| else |
| Builder.addTextChunk("keyPath"); |
| Builder.addLeftParen(); |
| Builder.addSimpleTypedParameter("@objc property sequence", |
| /*IsVarArg=*/false); |
| Builder.addRightParen(); |
| Builder.addTypeAnnotation("String"); |
| // This function is called only if the context type is 'String'. |
| Builder.setExpectedTypeRelation( |
| CodeCompletionResult::ExpectedTypeRelation::Identical); |
| } |
| |
| SemanticContextKind getSemanticContextKind(const ValueDecl *VD) { |
| // FIXME: to get the corect semantic context we need to know how lookup |
| // would have found the VD. For now, just infer a reasonable semantics. |
| |
| if (!VD) |
| return SemanticContextKind::CurrentModule; |
| |
| DeclContext *calleeDC = VD->getDeclContext(); |
| |
| if (calleeDC->isTypeContext()) |
| // FIXME: We should distinguish CurrentNominal and Super. We need to |
| // propagate the base type to do that. |
| return SemanticContextKind::CurrentNominal; |
| |
| if (calleeDC->isLocalContext()) |
| return SemanticContextKind::Local; |
| if (calleeDC->getParentModule() == CurrModule) |
| return SemanticContextKind::CurrentModule; |
| |
| return SemanticContextKind::OtherModule; |
| } |
| |
| void addSubscriptCallPattern( |
| const AnyFunctionType *AFT, const SubscriptDecl *SD, |
| const Optional<SemanticContextKind> SemanticContext = None) { |
| foundFunction(AFT); |
| GenericSignature genericSig; |
| if (SD) |
| genericSig = SD->getGenericSignatureOfContext(); |
| |
| CommandWordsPairs Pairs; |
| CodeCompletionResultBuilder Builder( |
| Sink, |
| SD ? CodeCompletionResult::ResultKind::Declaration |
| : CodeCompletionResult::ResultKind::Pattern, |
| SemanticContext ? *SemanticContext : getSemanticContextKind(SD), |
| expectedTypeContext); |
| if (SD) { |
| Builder.setAssociatedDecl(SD); |
| setClangDeclKeywords(SD, Pairs, Builder); |
| } |
| if (!HaveLParen) |
| Builder.addLeftBracket(); |
| else |
| Builder.addAnnotatedLeftBracket(); |
| ArrayRef<const ParamDecl *> declParams; |
| if (SD) |
| declParams = SD->getIndices()->getArray(); |
| addCallArgumentPatterns(Builder, AFT->getParams(), declParams, genericSig); |
| if (!HaveLParen) |
| Builder.addRightBracket(); |
| else |
| Builder.addAnnotatedRightBracket(); |
| if (SD && SD->isImplicitlyUnwrappedOptional()) |
| addTypeAnnotationForImplicitlyUnwrappedOptional(Builder, |
| AFT->getResult(), |
| genericSig); |
| else |
| addTypeAnnotation(Builder, AFT->getResult(), genericSig); |
| } |
| |
| void addFunctionCallPattern( |
| const AnyFunctionType *AFT, const AbstractFunctionDecl *AFD = nullptr, |
| const Optional<SemanticContextKind> SemanticContext = None) { |
| GenericSignature genericSig; |
| if (AFD) |
| genericSig = AFD->getGenericSignatureOfContext(); |
| |
| // Add the pattern, possibly including any default arguments. |
| auto addPattern = [&](ArrayRef<const ParamDecl *> declParams = {}, |
| bool includeDefaultArgs = true) { |
| CommandWordsPairs Pairs; |
| CodeCompletionResultBuilder Builder( |
| Sink, |
| AFD ? CodeCompletionResult::ResultKind::Declaration |
| : CodeCompletionResult::ResultKind::Pattern, |
| SemanticContext ? *SemanticContext : getSemanticContextKind(AFD), |
| expectedTypeContext); |
| if (AFD) { |
| Builder.setAssociatedDecl(AFD); |
| setClangDeclKeywords(AFD, Pairs, Builder); |
| } |
| |
| if (!HaveLParen) |
| Builder.addLeftParen(); |
| else |
| Builder.addAnnotatedLeftParen(); |
| |
| addCallArgumentPatterns(Builder, AFT->getParams(), declParams, |
| genericSig, includeDefaultArgs); |
| |
| // The rparen matches the lparen here so that we insert both or neither. |
| if (!HaveLParen) |
| Builder.addRightParen(); |
| else |
| Builder.addAnnotatedRightParen(); |
| |
| addThrows(Builder, AFT, AFD); |
| |
| if (AFD && |
| AFD->isImplicitlyUnwrappedOptional()) |
| addTypeAnnotationForImplicitlyUnwrappedOptional(Builder, |
| AFT->getResult(), |
| genericSig); |
| else |
| addTypeAnnotation(Builder, AFT->getResult(), genericSig); |
| }; |
| |
| if (!AFD || !AFD->getInterfaceType()->is<AnyFunctionType>()) { |
| // Probably, calling closure type expression. |
| foundFunction(AFT); |
| addPattern(); |
| return; |
| } else { |
| // Calling function or method. |
| foundFunction(AFD); |
| |
| // FIXME: Hack because we don't know we are calling instance |
| // method or not. There's invariant that funcTy is derived from AFD. |
| // Only if we are calling instance method on meta type, AFT is still |
| // curried. So we should be able to detect that by comparing curried level |
| // of AFT and the interface type of AFD. |
| auto getCurriedLevel = [](const AnyFunctionType *funcTy) -> unsigned { |
| unsigned level = 0; |
| while ((funcTy = funcTy->getResult()->getAs<AnyFunctionType>())) |
| ++level; |
| return level; |
| }; |
| bool isImplicitlyCurriedInstanceMethod = |
| (AFD->hasImplicitSelfDecl() && |
| getCurriedLevel(AFT) == |
| getCurriedLevel( |
| AFD->getInterfaceType()->castTo<AnyFunctionType>()) && |
| // NOTE: shouldn't be necessary, but just in case curried level check |
| // is insufficient. |
| AFT->getParams().size() == 1 && |
| AFT->getParams()[0].getLabel().empty()); |
| |
| if (isImplicitlyCurriedInstanceMethod) { |
| addPattern({AFD->getImplicitSelfDecl()}, /*includeDefaultArgs=*/true); |
| } else { |
| if (hasInterestingDefaultValues(AFD)) |
| addPattern(AFD->getParameters()->getArray(), |
| /*includeDefaultArgs=*/false); |
| addPattern(AFD->getParameters()->getArray(), |
| /*includeDefaultArgs=*/true); |
| } |
| } |
| } |
| |
| bool isImplicitlyCurriedInstanceMethod(const AbstractFunctionDecl *FD) { |
| if (FD->isStatic()) |
| return false; |
| |
| switch (Kind) { |
| case LookupKind::ValueExpr: |
| return ExprType->is<AnyMetatypeType>(); |
| case LookupKind::ValueInDeclContext: |
| if (InsideStaticMethod) |
| return FD->getDeclContext() == CurrentMethod->getDeclContext(); |
| if (auto Init = dyn_cast<Initializer>(CurrDeclContext)) { |
| if (auto PatInit = dyn_cast<PatternBindingInitializer>(Init)) { |
| if (PatInit->getInitializedLazyVar()) |
| return false; |
| } |
| return FD->getDeclContext() == Init->getInnermostTypeContext(); |
| } |
| return false; |
| case LookupKind::EnumElement: |
| case LookupKind::Type: |
| case LookupKind::TypeInDeclContext: |
| case LookupKind::GenericRequirement: |
| llvm_unreachable("cannot have a method call while doing a " |
| "type completion"); |
| case LookupKind::ImportFromModule: |
| return false; |
| } |
| |
| llvm_unreachable("Unhandled LookupKind in switch."); |
| } |
| |
| void addMethodCall(const FuncDecl *FD, DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo) { |
| if (FD->getBaseIdentifier().empty()) |
| return; |
| |
| // Suppress "sequenced" as a result, because it crashes completions. |
| // TODO(TF-315): Fix properly and then remove this. |
| if (FD->getBaseIdentifier().str() == "sequenced") |
| return; |
| |
| foundFunction(FD); |
| |
| const Identifier Name = FD->getBaseIdentifier(); |
| assert(!Name.empty() && "name should not be empty"); |
| |
| Type FunctionType = getTypeOfMember(FD, dynamicLookupInfo); |
| assert(FunctionType); |
| |
| auto AFT = FunctionType->getAs<AnyFunctionType>(); |
| |
| bool IsImplicitlyCurriedInstanceMethod = false; |
| if (FD->hasImplicitSelfDecl()) { |
| IsImplicitlyCurriedInstanceMethod = isImplicitlyCurriedInstanceMethod(FD); |
| |
| // Strip off '(_ self: Self)' if needed. |
| if (AFT && !IsImplicitlyCurriedInstanceMethod) { |
| AFT = AFT->getResult()->getAs<AnyFunctionType>(); |
| |
| // Check for duplicates with the adjusted type too. |
| if (isDuplicate(FD, AFT)) |
| return; |
| } |
| } |
| |
| bool trivialTrailingClosure = false; |
| if (AFT && !IsImplicitlyCurriedInstanceMethod) |
| trivialTrailingClosure = hasTrivialTrailingClosure(FD, AFT); |
| |
| // Add the method, possibly including any default arguments. |
| auto addMethodImpl = [&](bool includeDefaultArgs = true, |
| bool trivialTrailingClosure = false) { |
| CommandWordsPairs Pairs; |
| CodeCompletionResultBuilder Builder( |
| Sink, CodeCompletionResult::ResultKind::Declaration, |
| getSemanticContext(FD, Reason, dynamicLookupInfo), |
| expectedTypeContext); |
| setClangDeclKeywords(FD, Pairs, Builder); |
| Builder.setAssociatedDecl(FD); |
| addLeadingDot(Builder); |
| addValueBaseName(Builder, Name); |
| if (IsDynamicLookup) |
| Builder.addDynamicLookupMethodCallTail(); |
| else if (FD->getAttrs().hasAttribute<OptionalAttr>()) |
| Builder.addOptionalMethodCallTail(); |
| |
| if (!AFT) { |
| addTypeAnnotation(Builder, FunctionType, |
| FD->getGenericSignatureOfContext()); |
| return; |
| } |
| if (IsImplicitlyCurriedInstanceMethod) { |
| Builder.addLeftParen(); |
| addCallArgumentPatterns(Builder, AFT->getParams(), |
| {FD->getImplicitSelfDecl()}, |
| FD->getGenericSignatureOfContext(), |
| includeDefaultArgs); |
| Builder.addRightParen(); |
| } else if (trivialTrailingClosure) { |
| Builder.addBraceStmtWithCursor(" { code }"); |
| addThrows(Builder, AFT, FD); |
| } else { |
| Builder.addLeftParen(); |
| addCallArgumentPatterns(Builder, AFT, FD->getParameters(), |
| FD->getGenericSignatureOfContext(), |
| includeDefaultArgs); |
| Builder.addRightParen(); |
| addThrows(Builder, AFT, FD); |
| } |
| |
| // Build type annotation. |
| Type ResultType = AFT->getResult(); |
| // As we did with parameters in addParamPatternFromFunction, |
| // for regular methods we'll print '!' after implicitly |
| // unwrapped optional results. |
| bool IsIUO = |
| !IsImplicitlyCurriedInstanceMethod && |
| FD->isImplicitlyUnwrappedOptional(); |
| |
| PrintOptions PO; |
| PO.OpaqueReturnTypePrinting = |
| PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; |
| PO.PrintOptionalAsImplicitlyUnwrapped = IsIUO; |
| if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) |
| PO.setBaseType(typeContext->getDeclaredTypeInContext()); |
| Type AnnotationTy = |
| eraseArchetypes(ResultType, FD->getGenericSignatureOfContext()); |
| if (Builder.shouldAnnotateResults()) { |
| Builder.withNestedGroup( |
| CodeCompletionString::Chunk::ChunkKind::TypeAnnotationBegin, [&] { |
| AnnotatedTypePrinter printer(Builder); |
| if (IsImplicitlyCurriedInstanceMethod) { |
| auto *FnType = AnnotationTy->castTo<AnyFunctionType>(); |
| AnyFunctionType::printParams(FnType->getParams(), printer, |
| PrintOptions()); |
| AnnotationTy = FnType->getResult(); |
| printer.printText(" -> "); |
| } |
| |
| // What's left is the result type. |
| if (AnnotationTy->isVoid()) |
| AnnotationTy = Ctx.getVoidDecl()->getDeclaredInterfaceType(); |
| AnnotationTy.print(printer, PO); |
| }); |
| } else { |
| llvm::SmallString<32> TypeStr; |
| llvm::raw_svector_ostream OS(TypeStr); |
| if (IsImplicitlyCurriedInstanceMethod) { |
| auto *FnType = AnnotationTy->castTo<AnyFunctionType>(); |
| AnyFunctionType::printParams(FnType->getParams(), OS); |
| AnnotationTy = FnType->getResult(); |
| OS << " -> "; |
| } |
| |
| // What's left is the result type. |
| if (AnnotationTy->isVoid()) |
| AnnotationTy = Ctx.getVoidDecl()->getDeclaredInterfaceType(); |
| AnnotationTy.print(OS, PO); |
| Builder.addTypeAnnotation(TypeStr); |
| } |
| |
| Builder.setExpectedTypeRelation(calculateMaxTypeRelation( |
| ResultType, expectedTypeContext, CurrDeclContext)); |
| |
| if (isUnresolvedMemberIdealType(ResultType)) |
| Builder.setSemanticContext(SemanticContextKind::ExpressionSpecific); |
| |
| if (!IsImplicitlyCurriedInstanceMethod && |
| expectedTypeContext.requiresNonVoid() && |
| ResultType->isVoid()) { |
| Builder.setExpectedTypeRelation(CodeCompletionResult::Invalid); |
| } |
| }; |
| |
| if (!AFT || IsImplicitlyCurriedInstanceMethod) { |
| addMethodImpl(); |
| } else { |
| if (trivialTrailingClosure) |
| addMethodImpl(/*includeDefaultArgs=*/false, |
| /*trivialTrailingClosure=*/true); |
| if (hasInterestingDefaultValues(FD)) |
| addMethodImpl(/*includeDefaultArgs=*/false); |
| addMethodImpl(/*includeDefaultArgs=*/true); |
| } |
| } |
| |
| void addConstructorCall(const ConstructorDecl *CD, DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo, |
| Optional<Type> BaseType, Optional<Type> Result, |
| bool IsOnType = true, |
| Identifier addName = Identifier()) { |
| foundFunction(CD); |
| Type MemberType = getTypeOfMember(CD, BaseType.getValueOr(ExprType)); |
| AnyFunctionType *ConstructorType = nullptr; |
| if (auto MemberFuncType = MemberType->getAs<AnyFunctionType>()) |
| ConstructorType = MemberFuncType->getResult() |
| ->castTo<AnyFunctionType>(); |
| |
| bool needInit = false; |
| if (!IsOnType) { |
| assert(addName.empty()); |
| needInit = true; |
| } else if (addName.empty() && HaveDot) { |
| needInit = true; |
| } |
| |
| // If we won't be able to provide a result, bail out. |
| if (!ConstructorType && addName.empty() && !needInit) |
| return; |
| |
| // Add the constructor, possibly including any default arguments. |
| auto addConstructorImpl = [&](bool includeDefaultArgs = true) { |
| CommandWordsPairs Pairs; |
| CodeCompletionResultBuilder Builder( |
| Sink, CodeCompletionResult::ResultKind::Declaration, |
| getSemanticContext(CD, Reason, dynamicLookupInfo), |
| expectedTypeContext); |
| setClangDeclKeywords(CD, Pairs, Builder); |
| Builder.setAssociatedDecl(CD); |
| if (needInit) { |
| assert(addName.empty()); |
| addLeadingDot(Builder); |
| Builder.addBaseName("init"); |
| } else if (!addName.empty()) { |
| Builder.addBaseName(addName.str()); |
| } |
| |
| if (!ConstructorType) { |
| addTypeAnnotation(Builder, MemberType, |
| CD->getGenericSignatureOfContext()); |
| return; |
| } |
| |
| if (!HaveLParen) |
| Builder.addLeftParen(); |
| else |
| Builder.addAnnotatedLeftParen(); |
| |
| addCallArgumentPatterns(Builder, ConstructorType, CD->getParameters(), |
| CD->getGenericSignatureOfContext(), |
| includeDefaultArgs); |
| |
| // The rparen matches the lparen here so that we insert both or neither. |
| if (!HaveLParen) |
| Builder.addRightParen(); |
| else |
| Builder.addAnnotatedRightParen(); |
| |
| addThrows(Builder, ConstructorType, CD); |
| |
| if (!Result.hasValue()) |
| Result = ConstructorType->getResult(); |
| if (CD->isImplicitlyUnwrappedOptional()) { |
| addTypeAnnotationForImplicitlyUnwrappedOptional( |
| Builder, *Result, CD->getGenericSignatureOfContext()); |
| } else { |
| addTypeAnnotation(Builder, *Result, CD->getGenericSignatureOfContext()); |
| } |
| }; |
| |
| if (ConstructorType && hasInterestingDefaultValues(CD)) |
| addConstructorImpl(/*includeDefaultArgs*/ false); |
| addConstructorImpl(); |
| } |
| |
| void addConstructorCallsForType(Type type, Identifier name, |
| DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo) { |
| if (!Ctx.LangOpts.CodeCompleteInitsInPostfixExpr && !IsUnresolvedMember) |
| return; |
| |
| assert(CurrDeclContext); |
| |
| auto results = |
| swift::lookupSemanticMember(const_cast<DeclContext *>(CurrDeclContext), |
| type, DeclBaseName::createConstructor()); |
| for (const auto &entry : results.allResults()) { |
| auto *init = cast<ConstructorDecl>(entry.getValueDecl()); |
| if (init->shouldHideFromEditor()) |
| continue; |
| if (IsUnresolvedMember && init->isFailable() && |
| !init->isImplicitlyUnwrappedOptional()) { |
| continue; |
| } |
| addConstructorCall(cast<ConstructorDecl>(init), Reason, |
| dynamicLookupInfo, type, None, |
| /*IsOnType=*/true, name); |
| } |
| } |
| |
| void addSubscriptCall(const SubscriptDecl *SD, DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo) { |
| // Don't add subscript call to unqualified completion. |
| if (!ExprType) |
| return; |
| |
| // Subscript after '.' is valid only after type part of Swift keypath |
| // expression. (e.g. '\TyName.SubTy.[0]) |
| if (HaveDot && !IsAfterSwiftKeyPathRoot) |
| return; |
| |
| auto subscriptType = |
| getTypeOfMember(SD, dynamicLookupInfo)->getAs<AnyFunctionType>(); |
| if (!subscriptType) |
| return; |
| |
| CommandWordsPairs Pairs; |
| CodeCompletionResultBuilder Builder( |
| Sink, CodeCompletionResult::ResultKind::Declaration, |
| getSemanticContext(SD, Reason, dynamicLookupInfo), expectedTypeContext); |
| Builder.setAssociatedDecl(SD); |
| setClangDeclKeywords(SD, Pairs, Builder); |
| |
| // '\TyName#^TOKEN^#' requires leading dot. |
| if (!HaveDot && IsAfterSwiftKeyPathRoot) |
| Builder.addLeadingDot(); |
| |
| if (NeedOptionalUnwrap) { |
| Builder.setNumBytesToErase(NumBytesToEraseForOptionalUnwrap); |
| Builder.addQuestionMark(); |
| } |
| |
| Builder.addLeftBracket(); |
| addCallArgumentPatterns(Builder, subscriptType, SD->getIndices(), |
| SD->getGenericSignatureOfContext(), true); |
| Builder.addRightBracket(); |
| |
| // Add a type annotation. |
| Type resultTy = subscriptType->getResult(); |
| if (IsDynamicLookup) { |
| // Values of properties that were found on a AnyObject have |
| // Optional<T> type. |
| resultTy = OptionalType::get(resultTy); |
| } |
| addTypeAnnotation(Builder, resultTy, SD->getGenericSignatureOfContext()); |
| } |
| |
| void addNominalTypeRef(const NominalTypeDecl *NTD, DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo) { |
| if (IsUnresolvedMember) |
| return; |
| CommandWordsPairs Pairs; |
| CodeCompletionResultBuilder Builder( |
| Sink, CodeCompletionResult::ResultKind::Declaration, |
| getSemanticContext(NTD, Reason, dynamicLookupInfo), |
| expectedTypeContext); |
| Builder.setAssociatedDecl(NTD); |
| setClangDeclKeywords(NTD, Pairs, Builder); |
| addLeadingDot(Builder); |
| Builder.addBaseName(NTD->getName().str()); |
| |
| addTypeAnnotation(Builder, NTD->getDeclaredType()); |
| |
| // Override the type relation for NominalTypes. Use the better relation |
| // for the metatypes and the instance type. For example, |
| // |
| // func receiveInstance(_: Int) {} |
| // func receiveMetatype(_: Int.Type) {} |
| // |
| // We want to suggest 'Int' as 'Identical' for both arguments. |
| Builder.setExpectedTypeRelation(std::max( |
| calculateMaxTypeRelation(NTD->getDeclaredInterfaceType(), |
| expectedTypeContext, CurrDeclContext), |
| calculateMaxTypeRelation(NTD->getInterfaceType(), expectedTypeContext, |
| CurrDeclContext))); |
| } |
| |
| void addTypeAliasRef(const TypeAliasDecl *TAD, DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo) { |
| if (IsUnresolvedMember) |
| return; |
| CommandWordsPairs Pairs; |
| CodeCompletionResultBuilder Builder( |
| Sink, CodeCompletionResult::ResultKind::Declaration, |
| getSemanticContext(TAD, Reason, dynamicLookupInfo), |
| expectedTypeContext); |
| Builder.setAssociatedDecl(TAD); |
| setClangDeclKeywords(TAD, Pairs, Builder); |
| addLeadingDot(Builder); |
| Builder.addBaseName(TAD->getName().str()); |
| if (auto underlyingType = TAD->getUnderlyingType()) { |
| if (underlyingType->hasError()) { |
| Type parentType; |
| if (auto nominal = TAD->getDeclContext()->getSelfNominalTypeDecl()) { |
| parentType = nominal->getDeclaredInterfaceType(); |
| } |
| addTypeAnnotation( |
| Builder, |
| TypeAliasType::get(const_cast<TypeAliasDecl *>(TAD), |
| parentType, SubstitutionMap(), |
| underlyingType)); |
| |
| } else { |
| addTypeAnnotation(Builder, underlyingType); |
| } |
| } |
| } |
| |
| void addGenericTypeParamRef(const GenericTypeParamDecl *GP, |
| DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo) { |
| CommandWordsPairs Pairs; |
| CodeCompletionResultBuilder Builder( |
| Sink, CodeCompletionResult::ResultKind::Declaration, |
| getSemanticContext(GP, Reason, dynamicLookupInfo), expectedTypeContext); |
| setClangDeclKeywords(GP, Pairs, Builder); |
| Builder.setAssociatedDecl(GP); |
| addLeadingDot(Builder); |
| Builder.addBaseName(GP->getName().str()); |
| addTypeAnnotation(Builder, GP->getDeclaredInterfaceType()); |
| } |
| |
| void addAssociatedTypeRef(const AssociatedTypeDecl *AT, |
| DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo) { |
| CommandWordsPairs Pairs; |
| CodeCompletionResultBuilder Builder( |
| Sink, CodeCompletionResult::ResultKind::Declaration, |
| getSemanticContext(AT, Reason, dynamicLookupInfo), expectedTypeContext); |
| setClangDeclKeywords(AT, Pairs, Builder); |
| Builder.setAssociatedDecl(AT); |
| addLeadingDot(Builder); |
| Builder.addBaseName(AT->getName().str()); |
| if (Type T = getAssociatedTypeType(AT)) |
| addTypeAnnotation(Builder, T); |
| } |
| |
| void addPrecedenceGroupRef(PrecedenceGroupDecl *PGD) { |
| auto semanticContext = |
| getSemanticContext(PGD, DeclVisibilityKind::VisibleAtTopLevel, {}); |
| CodeCompletionResultBuilder builder( |
| Sink, CodeCompletionResult::ResultKind::Declaration, |
| semanticContext, {}); |
| |
| builder.addBaseName(PGD->getName().str()); |
| builder.setAssociatedDecl(PGD); |
| }; |
| |
| void addEnumElementRef(const EnumElementDecl *EED, DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo, |
| bool HasTypeContext) { |
| if (!EED->hasName() || |
| !EED->isAccessibleFrom(CurrDeclContext) || |
| EED->shouldHideFromEditor()) |
| return; |
| |
| CommandWordsPairs Pairs; |
| CodeCompletionResultBuilder Builder( |
| Sink, CodeCompletionResult::ResultKind::Declaration, |
| HasTypeContext ? SemanticContextKind::ExpressionSpecific |
| : getSemanticContext(EED, Reason, dynamicLookupInfo), |
| expectedTypeContext); |
| Builder.setAssociatedDecl(EED); |
| setClangDeclKeywords(EED, Pairs, Builder); |
| addLeadingDot(Builder); |
| addValueBaseName(Builder, EED->getBaseIdentifier()); |
| |
| // Enum element is of function type; (Self.type) -> Self or |
| // (Self.Type) -> (Args...) -> Self. |
| Type EnumType = getTypeOfMember(EED, dynamicLookupInfo); |
| if (EnumType->is<AnyFunctionType>()) |
| EnumType = EnumType->castTo<AnyFunctionType>()->getResult(); |
| |
| if (EnumType->is<FunctionType>()) { |
| Builder.addLeftParen(); |
| addCallArgumentPatterns(Builder, EnumType->castTo<FunctionType>(), |
| EED->getParameterList(), |
| EED->getGenericSignatureOfContext()); |
| Builder.addRightParen(); |
| |
| // Extract result as the enum type. |
| EnumType = EnumType->castTo<FunctionType>()->getResult(); |
| } |
| |
| addTypeAnnotation(Builder, EnumType, EED->getGenericSignatureOfContext()); |
| |
| if (isUnresolvedMemberIdealType(EnumType)) |
| Builder.setSemanticContext(SemanticContextKind::ExpressionSpecific); |
| } |
| |
| void addKeyword(StringRef Name, Type TypeAnnotation = Type(), |
| SemanticContextKind SK = SemanticContextKind::None, |
| CodeCompletionKeywordKind KeyKind |
| = CodeCompletionKeywordKind::None, |
| unsigned NumBytesToErase = 0) { |
| CodeCompletionResultBuilder Builder( |
| Sink, CodeCompletionResult::ResultKind::Keyword, SK, |
| expectedTypeContext); |
| addLeadingDot(Builder); |
| Builder.addKeyword(Name); |
| Builder.setKeywordKind(KeyKind); |
| if (TypeAnnotation) |
| addTypeAnnotation(Builder, TypeAnnotation); |
| if (NumBytesToErase > 0) |
| Builder.setNumBytesToErase(NumBytesToErase); |
| } |
| |
| void addKeyword(StringRef Name, StringRef TypeAnnotation, |
| CodeCompletionKeywordKind KeyKind |
| = CodeCompletionKeywordKind::None) { |
| CodeCompletionResultBuilder Builder( |
| Sink, |
| CodeCompletionResult::ResultKind::Keyword, |
| SemanticContextKind::None, expectedTypeContext); |
| addLeadingDot(Builder); |
| Builder.addKeyword(Name); |
| Builder.setKeywordKind(KeyKind); |
| if (!TypeAnnotation.empty()) |
| Builder.addTypeAnnotation(TypeAnnotation); |
| } |
| |
| void addDeclAttrParamKeyword(StringRef Name, StringRef Annotation, |
| bool NeedSpecify) { |
| CodeCompletionResultBuilder Builder( |
| Sink, |
| CodeCompletionResult::ResultKind::Keyword, |
| SemanticContextKind::None, expectedTypeContext); |
| Builder.addDeclAttrParamKeyword(Name, Annotation, NeedSpecify); |
| } |
| |
| void addDeclAttrKeyword(StringRef Name, StringRef Annotation) { |
| CodeCompletionResultBuilder Builder( |
| Sink, |
| CodeCompletionResult::ResultKind::Keyword, |
| SemanticContextKind::None, expectedTypeContext); |
| Builder.addDeclAttrKeyword(Name, Annotation); |
| } |
| |
| /// Add the compound function name for the given function. |
| /// Returns \c true if the compound function name is actually used. |
| bool addCompoundFunctionNameIfDesiable(AbstractFunctionDecl *AFD, |
| DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo) { |
| auto funcTy = |
| getTypeOfMember(AFD, dynamicLookupInfo)->getAs<AnyFunctionType>(); |
| bool dropCurryLevel = funcTy && AFD->getDeclContext()->isTypeContext() && |
| !isImplicitlyCurriedInstanceMethod(AFD); |
| if (dropCurryLevel) |
| funcTy = funcTy->getResult()->getAs<AnyFunctionType>(); |
| |
| bool useFunctionReference = PreferFunctionReferencesToCalls; |
| if (!useFunctionReference && funcTy) { |
| auto maxRel = calculateMaxTypeRelation(funcTy, expectedTypeContext, |
| CurrDeclContext); |
| useFunctionReference = |
| maxRel >= CodeCompletionResult::ExpectedTypeRelation::Convertible; |
| } |
| if (!useFunctionReference) |
| return false; |
| |
| // Check for duplicates with the adjusted type too. |
| if (dropCurryLevel && isDuplicate(AFD, funcTy)) |
| return true; |
| |
| CommandWordsPairs Pairs; |
| CodeCompletionResultBuilder Builder( |
| Sink, CodeCompletionResult::ResultKind::Declaration, |
| getSemanticContext(AFD, Reason, dynamicLookupInfo), |
| expectedTypeContext); |
| setClangDeclKeywords(AFD, Pairs, Builder); |
| Builder.setAssociatedDecl(AFD); |
| |
| // Base name |
| addLeadingDot(Builder); |
| addValueBaseName(Builder, AFD->getBaseName()); |
| |
| // Add the argument labels. |
| const auto ArgLabels = AFD->getName().getArgumentNames(); |
| if (!ArgLabels.empty()) { |
| if (!HaveLParen) |
| Builder.addLeftParen(); |
| else |
| Builder.addAnnotatedLeftParen(); |
| |
| for (auto ArgLabel : ArgLabels) { |
| if (ArgLabel.empty()) |
| Builder.addTextChunk("_"); |
| else |
| Builder.addTextChunk(ArgLabel.str()); |
| Builder.addTextChunk(":"); |
| } |
| |
| Builder.addRightParen(); |
| } |
| |
| if (funcTy) |
| addTypeAnnotation(Builder, funcTy, AFD->getGenericSignatureOfContext()); |
| |
| return true; |
| } |
| |
| private: |
| |
| /// Returns true if duplicate checking is enabled (via |
| /// \c shouldCheckForDuplicates) and this decl + type combination has been |
| /// checked previously. Returns false otherwise. |
| bool isDuplicate(const ValueDecl *D, Type Ty) { |
| if (!CheckForDuplicates) |
| return false; |
| return !PreviouslySeen.insert({D, Ty}).second; |
| } |
| |
| /// Returns true if duplicate checking is enabled (via |
| /// \c shouldCheckForDuplicates) and this decl has been checked previously |
| /// with the type according to \c getTypeOfMember. Returns false otherwise. |
| bool isDuplicate(const ValueDecl *D, DynamicLookupInfo dynamicLookupInfo) { |
| if (!CheckForDuplicates) |
| return false; |
| Type Ty = getTypeOfMember(D, dynamicLookupInfo); |
| return !PreviouslySeen.insert({D, Ty}).second; |
| } |
| |
| public: |
| |
| // Implement swift::VisibleDeclConsumer. |
| void foundDecl(ValueDecl *D, DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo) override { |
| assert(Reason != |
| DeclVisibilityKind::MemberOfProtocolDerivedByCurrentNominal && |
| "Including derived requirement in non-override lookup"); |
| |
| if (D->shouldHideFromEditor()) |
| return; |
| |
| if (IsKeyPathExpr && !KeyPathFilter(D, Reason)) |
| return; |
| |
| if (IsSwiftKeyPathExpr && !SwiftKeyPathFilter(D, Reason)) |
| return; |
| |
| // If we've seen this decl+type before (possible when multiple lookups are |
| // performed e.g. because of ambiguous base types), bail. |
| if (isDuplicate(D, dynamicLookupInfo)) |
| return; |
| |
| // FIXME(InterfaceTypeRequest): Remove this. |
| (void)D->getInterfaceType(); |
| switch (Kind) { |
| case LookupKind::ValueExpr: |
| if (auto *CD = dyn_cast<ConstructorDecl>(D)) { |
| // Do we want compound function names here? |
| if (addCompoundFunctionNameIfDesiable(CD, Reason, dynamicLookupInfo)) |
| return; |
| |
| if (auto MT = ExprType->getAs<AnyMetatypeType>()) { |
| Type Ty = MT->getInstanceType(); |
| assert(Ty && "Cannot find instance type."); |
| |
| // If instance type is type alias, show users that the constructed |
| // type is the typealias instead of the underlying type of the alias. |
| Optional<Type> Result = None; |
| if (!CD->getInterfaceType()->is<ErrorType>() && |
| isa<TypeAliasType>(Ty.getPointer()) && |
| Ty->getDesugaredType() == |
| CD->getResultInterfaceType().getPointer()) { |
| Result = Ty; |
| } |
| // If the expression type is not a static metatype or an archetype, the base |
| // is not a type. Direct call syntax is illegal on values, so we only add |
| // initializer completions if we do not have a left parenthesis and either |
| // the initializer is required, the base type's instance type is not a class, |
| // or this is a 'self' or 'super' reference. |
| if (IsStaticMetatype || IsUnresolvedMember || Ty->is<ArchetypeType>()) |
| addConstructorCall(CD, Reason, dynamicLookupInfo, None, Result, |
| /*isOnType*/ true); |
| else if ((IsSelfRefExpr || IsSuperRefExpr || !Ty->is<ClassType>() || |
| CD->isRequired()) && !HaveLParen) |
| addConstructorCall(CD, Reason, dynamicLookupInfo, None, Result, |
| /*isOnType*/ false); |
| return; |
| } |
| if (!HaveLParen) { |
| auto CDC = dyn_cast<ConstructorDecl>(CurrDeclContext); |
| if (!CDC) |
| return; |
| |
| // For classes, we do not want 'init' completions for 'self' in |
| // non-convenience initializers and for 'super' in convenience initializers. |
| if (ExprType->is<ClassType>()) { |
| if ((IsSelfRefExpr && !CDC->isConvenienceInit()) || |
| (IsSuperRefExpr && CDC->isConvenienceInit())) |
| return; |
| } |
| if (IsSelfRefExpr || IsSuperRefExpr) |
| addConstructorCall(CD, Reason, dynamicLookupInfo, None, None, |
| /*IsOnType=*/false); |
| } |
| return; |
| } |
| |
| if (HaveLParen) |
| return; |
| |
| LLVM_FALLTHROUGH; |
| |
| case LookupKind::ValueInDeclContext: |
| case LookupKind::ImportFromModule: |
| if (auto *VD = dyn_cast<VarDecl>(D)) { |
| addVarDeclRef(VD, Reason, dynamicLookupInfo); |
| return; |
| } |
| |
| if (auto *FD = dyn_cast<FuncDecl>(D)) { |
| // We cannot call operators with a postfix parenthesis syntax. |
| if (FD->isBinaryOperator() || FD->isUnaryOperator()) |
| return; |
| |
| // We cannot call accessors. We use VarDecls and SubscriptDecls to |
| // produce completions that refer to getters and setters. |
| if (isa<AccessorDecl>(FD)) |
| return; |
| |
| // Do we want compound function names here? |
| if (addCompoundFunctionNameIfDesiable(FD, Reason, dynamicLookupInfo)) |
| return; |
| |
| addMethodCall(FD, Reason, dynamicLookupInfo); |
| |
| // SE-0253: Callable values of user-defined nominal types. |
| if (FD->isCallAsFunctionMethod() && !HaveDot && |
| (!ExprType || !ExprType->is<AnyMetatypeType>())) { |
| Type funcType = getTypeOfMember(FD, dynamicLookupInfo) |
| ->castTo<AnyFunctionType>() |
| ->getResult(); |
| |
| // Check for duplicates with the adjusted type too. |
| if (isDuplicate(FD, funcType)) |
| return; |
| |
| addFunctionCallPattern( |
| funcType->castTo<AnyFunctionType>(), FD, |
| getSemanticContext(FD, Reason, dynamicLookupInfo)); |
| } |
| return; |
| } |
| |
| if (auto *NTD = dyn_cast<NominalTypeDecl>(D)) { |
| addNominalTypeRef(NTD, Reason, dynamicLookupInfo); |
| addConstructorCallsForType(NTD->getDeclaredInterfaceType(), |
| NTD->getName(), Reason, dynamicLookupInfo); |
| return; |
| } |
| |
| if (auto *TAD = dyn_cast<TypeAliasDecl>(D)) { |
| addTypeAliasRef(TAD, Reason, dynamicLookupInfo); |
| auto type = TAD->mapTypeIntoContext(TAD->getDeclaredInterfaceType()); |
| if (type->mayHaveMembers()) |
| addConstructorCallsForType(type, TAD->getName(), Reason, |
| dynamicLookupInfo); |
| return; |
| } |
| |
| if (auto *GP = dyn_cast<GenericTypeParamDecl>(D)) { |
| addGenericTypeParamRef(GP, Reason, dynamicLookupInfo); |
| for (auto *protocol : GP->getConformingProtocols()) |
| addConstructorCallsForType(protocol->getDeclaredInterfaceType(), |
| GP->getName(), Reason, dynamicLookupInfo); |
| return; |
| } |
| |
| if (auto *AT = dyn_cast<AssociatedTypeDecl>(D)) { |
| addAssociatedTypeRef(AT, Reason, dynamicLookupInfo); |
| return; |
| } |
| |
| if (auto *EED = dyn_cast<EnumElementDecl>(D)) { |
| addEnumElementRef(EED, Reason, dynamicLookupInfo, |
| /*HasTypeContext=*/false); |
| return; |
| } |
| |
| // Swift key path allows .[0] |
| if (auto *SD = dyn_cast<SubscriptDecl>(D)) { |
| addSubscriptCall(SD, Reason, dynamicLookupInfo); |
| return; |
| } |
| return; |
| |
| case LookupKind::EnumElement: |
| handleEnumElement(D, Reason, dynamicLookupInfo); |
| return; |
| |
| case LookupKind::Type: |
| case LookupKind::TypeInDeclContext: |
| if (auto *NTD = dyn_cast<NominalTypeDecl>(D)) { |
| addNominalTypeRef(NTD, Reason, dynamicLookupInfo); |
| return; |
| } |
| |
| if (auto *GP = dyn_cast<GenericTypeParamDecl>(D)) { |
| addGenericTypeParamRef(GP, Reason, dynamicLookupInfo); |
| return; |
| } |
| |
| LLVM_FALLTHROUGH; |
| case LookupKind::GenericRequirement: |
| |
| if (TypeAliasDecl *TAD = dyn_cast<TypeAliasDecl>(D)) { |
| if (Kind == LookupKind::GenericRequirement && |
| !canBeUsedAsRequirementFirstType(BaseType, TAD)) |
| return; |
| addTypeAliasRef(TAD, Reason, dynamicLookupInfo); |
| return; |
| } |
| |
| if (auto *AT = dyn_cast<AssociatedTypeDecl>(D)) { |
| addAssociatedTypeRef(AT, Reason, dynamicLookupInfo); |
| return; |
| } |
| |
| return; |
| } |
| } |
| |
| bool handleEnumElement(ValueDecl *D, DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo) { |
| if (auto *EED = dyn_cast<EnumElementDecl>(D)) { |
| addEnumElementRef(EED, Reason, dynamicLookupInfo, |
| /*HasTypeContext=*/true); |
| return true; |
| } else if (auto *ED = dyn_cast<EnumDecl>(D)) { |
| llvm::DenseSet<EnumElementDecl *> Elements; |
| ED->getAllElements(Elements); |
| for (auto *Ele : Elements) { |
| addEnumElementRef(Ele, Reason, dynamicLookupInfo, |
| /*HasTypeContext=*/true); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| bool tryTupleExprCompletions(Type ExprType) { |
| auto *TT = ExprType->getAs<TupleType>(); |
| if (!TT) |
| return false; |
| |
| unsigned Index = 0; |
| for (auto TupleElt : TT->getElements()) { |
| CodeCompletionResultBuilder Builder( |
| Sink, |
| CodeCompletionResult::ResultKind::Pattern, |
| SemanticContextKind::CurrentNominal, expectedTypeContext); |
| addLeadingDot(Builder); |
| if (TupleElt.hasName()) { |
| Builder.addBaseName(TupleElt.getName().str()); |
| } else { |
| llvm::SmallString<4> IndexStr; |
| { |
| llvm::raw_svector_ostream OS(IndexStr); |
| OS << Index; |
| } |
| Builder.addBaseName(IndexStr.str()); |
| } |
| addTypeAnnotation(Builder, TupleElt.getType()); |
| ++Index; |
| } |
| return true; |
| } |
| |
| bool tryFunctionCallCompletions(Type ExprType, const ValueDecl *VD, Optional<SemanticContextKind> SemanticContext = None) { |
| ExprType = ExprType->getRValueType(); |
| if (auto AFT = ExprType->getAs<AnyFunctionType>()) { |
| if (auto *AFD = dyn_cast_or_null<AbstractFunctionDecl>(VD)) { |
| addFunctionCallPattern(AFT, AFD, SemanticContext); |
| } else { |
| addFunctionCallPattern(AFT); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| bool tryModuleCompletions(Type ExprType, bool TypesOnly = false) { |
| if (auto MT = ExprType->getAs<ModuleType>()) { |
| ModuleDecl *M = MT->getModule(); |
| |
| // Only lookup this module's symbols from the cache if it is not the |
| // current module. |
| if (M == CurrModule) |
| return false; |
| |
| // If the module is shadowed by a separately imported overlay(s), look up |
| // the symbols from the overlay(s) instead. |
| SmallVector<ModuleDecl *, 1> ShadowingOrOriginal; |
| if (auto *SF = CurrDeclContext->getParentSourceFile()) { |
| SF->getSeparatelyImportedOverlays(M, ShadowingOrOriginal); |
| if (ShadowingOrOriginal.empty()) |
| ShadowingOrOriginal.push_back(M); |
| } |
| for (ModuleDecl *M: ShadowingOrOriginal) { |
| RequestedResultsTy Request = RequestedResultsTy::fromModule(M) |
| .needLeadingDot(needDot()) |
| .withModuleQualifier(false); |
| if (TypesOnly) |
| Request = Request.onlyTypes(); |
| RequestedCachedResults.push_back(Request); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| /// If the given ExprType is optional, this adds completions for the unwrapped |
| /// type. |
| /// |
| /// \return true if the given type was Optional . |
| bool tryUnwrappedCompletions(Type ExprType, bool isIUO) { |
| // FIXME: consider types convertible to T?. |
| |
| ExprType = ExprType->getRValueType(); |
| // FIXME: We don't always pass down whether a type is from an |
| // unforced IUO. |
| if (isIUO) { |
| if (Type Unwrapped = ExprType->getOptionalObjectType()) { |
| lookupVisibleMemberDecls(*this, Unwrapped, CurrDeclContext, |
| IncludeInstanceMembers, |
| /*includeDerivedRequirements*/false, |
| /*includeProtocolExtensionMembers*/true); |
| return true; |
| } |
| assert(IsUnwrappedOptional && "IUOs should be optional if not bound/forced"); |
| return false; |
| } |
| |
| if (Type Unwrapped = ExprType->getOptionalObjectType()) { |
| llvm::SaveAndRestore<bool> ChangeNeedOptionalUnwrap(NeedOptionalUnwrap, |
| true); |
| if (DotLoc.isValid()) { |
| // Let's not erase the dot if the completion is after a swift key path |
| // root because \A?.?.member is the correct way to access wrapped type |
| // member from an optional key path root. |
| auto loc = IsAfterSwiftKeyPathRoot ? DotLoc.getAdvancedLoc(1) : DotLoc; |
| NumBytesToEraseForOptionalUnwrap = Ctx.SourceMgr.getByteDistance( |
| loc, Ctx.SourceMgr.getCodeCompletionLoc()); |
| } else { |
| NumBytesToEraseForOptionalUnwrap = 0; |
| } |
| if (NumBytesToEraseForOptionalUnwrap <= |
| CodeCompletionResult::MaxNumBytesToErase) { |
| if (!tryTupleExprCompletions(Unwrapped)) { |
| lookupVisibleMemberDecls(*this, Unwrapped, CurrDeclContext, |
| IncludeInstanceMembers, |
| /*includeDerivedRequirements*/false, |
| /*includeProtocolExtensionMembers*/true); |
| } |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| void getPostfixKeywordCompletions(Type ExprType, Expr *ParsedExpr) { |
| if (IsSuperRefExpr) |
| return; |
| |
| if (!ExprType->getAs<ModuleType>()) { |
| addKeyword(getTokenText(tok::kw_self), ExprType->getRValueType(), |
| SemanticContextKind::CurrentNominal, |
| CodeCompletionKeywordKind::kw_self); |
| } |
| |
| if (isa<TypeExpr>(ParsedExpr)) { |
| if (auto *T = ExprType->getAs<AnyMetatypeType>()) { |
| auto instanceTy = T->getInstanceType(); |
| if (instanceTy->isAnyExistentialType()) { |
| addKeyword("Protocol", MetatypeType::get(instanceTy), |
| SemanticContextKind::CurrentNominal); |
| addKeyword("Type", ExistentialMetatypeType::get(instanceTy), |
| SemanticContextKind::CurrentNominal); |
| } else { |
| addKeyword("Type", MetatypeType::get(instanceTy), |
| SemanticContextKind::CurrentNominal); |
| } |
| } |
| } |
| } |
| |
| void getValueExprCompletions(Type ExprType, ValueDecl *VD = nullptr) { |
| Kind = LookupKind::ValueExpr; |
| NeedLeadingDot = !HaveDot; |
| |
| ExprType = ExprType->getRValueType(); |
| assert(!ExprType->hasTypeParameter()); |
| |
| this->ExprType = ExprType; |
| |
| // Open existential types, so that lookupVisibleMemberDecls() can properly |
| // substitute them. |
| bool WasOptional = false; |
| if (auto OptionalType = ExprType->getOptionalObjectType()) { |
| ExprType = OptionalType; |
| WasOptional = true; |
| } |
| |
| if (!ExprType->getMetatypeInstanceType()->isAnyObject()) |
| if (ExprType->isAnyExistentialType()) |
| ExprType = OpenedArchetypeType::getAny(ExprType); |
| |
| if (WasOptional) |
| ExprType = OptionalType::get(ExprType); |
| |
| // Handle special cases |
| bool isIUO = VD && VD->isImplicitlyUnwrappedOptional(); |
| if (tryFunctionCallCompletions(ExprType, VD)) |
| return; |
| if (tryModuleCompletions(ExprType)) |
| return; |
| if (tryTupleExprCompletions(ExprType)) |
| return; |
| // Don't check/return so we still add the members of Optional itself below |
| tryUnwrappedCompletions(ExprType, isIUO); |
| |
| lookupVisibleMemberDecls(*this, ExprType, CurrDeclContext, |
| IncludeInstanceMembers, |
| /*includeDerivedRequirements*/false, |
| /*includeProtocolExtensionMembers*/true); |
| } |
| |
| void collectOperators(SmallVectorImpl<OperatorDecl *> &results) { |
| assert(CurrDeclContext); |
| for (auto import : namelookup::getAllImports(CurrDeclContext)) |
| import.importedModule->getOperatorDecls(results); |
| } |
| |
| void addPostfixBang(Type resultType) { |
| CodeCompletionResultBuilder builder( |
| Sink, CodeCompletionResult::ResultKind::BuiltinOperator, |
| SemanticContextKind::None, {}); |
| // FIXME: we can't use the exclamation mark chunk kind, or it isn't |
| // included in the completion name. |
| builder.addTextChunk("!"); |
| assert(resultType); |
| addTypeAnnotation(builder, resultType); |
| } |
| |
| void addPostfixOperatorCompletion(OperatorDecl *op, Type resultType) { |
| // FIXME: we should get the semantic context of the function, not the |
| // operator decl. |
| auto semanticContext = |
| getSemanticContext(op, DeclVisibilityKind::VisibleAtTopLevel, {}); |
| CodeCompletionResultBuilder builder( |
| Sink, CodeCompletionResult::ResultKind::Declaration, semanticContext, |
| {}); |
| |
| // FIXME: handle variable amounts of space. |
| if (HaveLeadingSpace) |
| builder.setNumBytesToErase(1); |
| builder.setAssociatedDecl(op); |
| builder.addBaseName(op->getName().str()); |
| assert(resultType); |
| addTypeAnnotation(builder, resultType); |
| } |
| |
| void tryPostfixOperator(Expr *expr, PostfixOperatorDecl *op) { |
| ConcreteDeclRef referencedDecl; |
| FunctionType *funcTy = getTypeOfCompletionOperator( |
| const_cast<DeclContext *>(CurrDeclContext), expr, op->getName(), |
| DeclRefKind::PostfixOperator, referencedDecl); |
| if (!funcTy) |
| return; |
| |
| // TODO: Use referencedDecl (FuncDecl) instead of 'op' (OperatorDecl). |
| addPostfixOperatorCompletion(op, funcTy->getResult()); |
| } |
| |
| void addAssignmentOperator(Type RHSType, Type resultType) { |
| CodeCompletionResultBuilder builder( |
| Sink, CodeCompletionResult::ResultKind::BuiltinOperator, |
| SemanticContextKind::None, {}); |
| |
| if (HaveLeadingSpace) |
| builder.addAnnotatedWhitespace(" "); |
| else |
| builder.addWhitespace(" "); |
| builder.addEqual(); |
| builder.addWhitespace(" "); |
| assert(RHSType && resultType); |
| Type contextTy; |
| if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) |
| contextTy = typeContext->getDeclaredTypeInContext(); |
| builder.addCallParameter(Identifier(), RHSType, contextTy); |
| addTypeAnnotation(builder, resultType); |
| } |
| |
| void addInfixOperatorCompletion(OperatorDecl *op, Type resultType, |
| Type RHSType) { |
| // FIXME: we should get the semantic context of the function, not the |
| // operator decl. |
| auto semanticContext = |
| getSemanticContext(op, DeclVisibilityKind::VisibleAtTopLevel, {}); |
| CodeCompletionResultBuilder builder( |
| Sink, CodeCompletionResult::ResultKind::Declaration, semanticContext, |
| {}); |
| builder.setAssociatedDecl(op); |
| |
| if (HaveLeadingSpace) |
| builder.addAnnotatedWhitespace(" "); |
| else |
| builder.addWhitespace(" "); |
| builder.addBaseName(op->getName().str()); |
| builder.addWhitespace(" "); |
| if (RHSType) { |
| Type contextTy; |
| if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) |
| contextTy = typeContext->getDeclaredTypeInContext(); |
| builder.addCallParameter(Identifier(), RHSType, contextTy); |
| } |
| if (resultType) |
| addTypeAnnotation(builder, resultType); |
| } |
| |
| void tryInfixOperatorCompletion(Expr *foldedExpr, InfixOperatorDecl *op) { |
| ConcreteDeclRef referencedDecl; |
| FunctionType *funcTy = getTypeOfCompletionOperator( |
| const_cast<DeclContext *>(CurrDeclContext), foldedExpr, op->getName(), |
| DeclRefKind::BinaryOperator, referencedDecl); |
| if (!funcTy) |
| return; |
| |
| Type lhsTy = funcTy->getParams()[0].getPlainType(); |
| Type rhsTy = funcTy->getParams()[1].getPlainType(); |
| Type resultTy = funcTy->getResult(); |
| |
| // Don't complete optional operators on non-optional types. |
| if (!lhsTy->getRValueType()->getOptionalObjectType()) { |
| // 'T ?? T' |
| if (op->getName().str() == "??") |
| return; |
| // 'T == nil' |
| if (auto NT = rhsTy->getNominalOrBoundGenericNominal()) |
| if (NT->getName() == |
| CurrDeclContext->getASTContext().Id_OptionalNilComparisonType) |
| return; |
| } |
| |
| // If the right-hand side and result type are both type parameters, we're |
| // not providing a useful completion. |
| if (resultTy->isTypeParameter() && rhsTy->isTypeParameter()) |
| return; |
| |
| // TODO: Use referencedDecl (FuncDecl) instead of 'op' (OperatorDecl). |
| addInfixOperatorCompletion(op, funcTy->getResult(), |
| funcTy->getParams()[1].getPlainType()); |
| } |
| |
| Expr *typeCheckLeadingSequence(Expr *LHS, ArrayRef<Expr *> leadingSequence) { |
| if (leadingSequence.empty()) |
| return LHS; |
| |
| SourceRange sequenceRange(leadingSequence.front()->getStartLoc(), |
| LHS->getEndLoc()); |
| auto *expr = findParsedExpr(CurrDeclContext, sequenceRange); |
| if (!expr) |
| return LHS; |
| |
| if (expr->getType() && !expr->getType()->hasError()) |
| return expr; |
| |
| if (!typeCheckExpression(const_cast<DeclContext *>(CurrDeclContext), expr)) |
| return expr; |
| return LHS; |
| } |
| |
| void getOperatorCompletions(Expr *LHS, ArrayRef<Expr *> leadingSequence) { |
| if (IsSuperRefExpr) |
| return; |
| |
| Expr *foldedExpr = typeCheckLeadingSequence(LHS, leadingSequence); |
| |
| SmallVector<OperatorDecl *, 16> operators; |
| collectOperators(operators); |
| // FIXME: this always chooses the first operator with the given name. |
| llvm::DenseSet<Identifier> seenPostfixOperators; |
| llvm::DenseSet<Identifier> seenInfixOperators; |
| |
| for (auto op : operators) { |
| switch (op->getKind()) { |
| case DeclKind::PrefixOperator: |
| // Don't insert prefix operators in postfix position. |
| // FIXME: where should these get completed? |
| break; |
| case DeclKind::PostfixOperator: |
| if (seenPostfixOperators.insert(op->getName()).second) |
| tryPostfixOperator(LHS, cast<PostfixOperatorDecl>(op)); |
| break; |
| case DeclKind::InfixOperator: |
| if (seenInfixOperators.insert(op->getName()).second) |
| tryInfixOperatorCompletion(foldedExpr, cast<InfixOperatorDecl>(op)); |
| break; |
| default: |
| llvm_unreachable("unexpected operator kind"); |
| } |
| } |
| |
| if (leadingSequence.empty() && LHS->getType() && |
| LHS->getType()->hasLValueType()) { |
| addAssignmentOperator(LHS->getType()->getRValueType(), |
| CurrDeclContext->getASTContext().TheEmptyTupleType); |
| } |
| |
| // FIXME: unify this with the ?.member completions. |
| if (auto T = LHS->getType()) |
| if (auto ValueT = T->getRValueType()->getOptionalObjectType()) |
| addPostfixBang(ValueT); |
| } |
| |
| void addTypeRelationFromProtocol(CodeCompletionResultBuilder &builder, |
| CodeCompletionLiteralKind kind) { |
| // Check for matching ExpectedTypes. |
| auto *P = Ctx.getProtocol(protocolForLiteralKind(kind)); |
| for (auto T : expectedTypeContext.possibleTypes) { |
| if (!T) |
| continue; |
| |
| auto typeRelation = CodeCompletionResult::Identical; |
| // Convert through optional types unless we're looking for a protocol |
| // that Optional itself conforms to. |
| if (kind != CodeCompletionLiteralKind::NilLiteral) { |
| if (auto optionalObjT = T->getOptionalObjectType()) { |
| T = optionalObjT; |
| typeRelation = CodeCompletionResult::Convertible; |
| } |
| } |
| |
| // Check for conformance to the literal protocol. |
| if (auto *NTD = T->getAnyNominal()) { |
| SmallVector<ProtocolConformance *, 2> conformances; |
| if (NTD->lookupConformance(CurrModule, P, conformances)) { |
| addTypeAnnotation(builder, T); |
| builder.setExpectedTypeRelation(typeRelation); |
| return; |
| } |
| } |
| } |
| |
| // Fallback to showing the default type. |
| if (auto defaultTy = defaultTypeLiteralKind(kind, Ctx)) { |
| builder.addTypeAnnotation(defaultTy, PrintOptions()); |
| builder.setExpectedTypeRelation( |
| expectedTypeContext.possibleTypes.empty() |
| ? CodeCompletionResult::ExpectedTypeRelation::Unknown |
| : CodeCompletionResult::ExpectedTypeRelation::Unrelated); |
| } |
| } |
| |
| /// Add '#file', '#line', et at. |
| void addPoundLiteralCompletions(bool needPound) { |
| auto addFromProto = [&](MagicIdentifierLiteralExpr::Kind magicKind, |
| Optional<CodeCompletionLiteralKind> literalKind) { |
| CodeCompletionKeywordKind kwKind; |
| switch (magicKind) { |
| case MagicIdentifierLiteralExpr::FileIDSpelledAsFile: |
| kwKind = CodeCompletionKeywordKind::pound_file; |
| break; |
| case MagicIdentifierLiteralExpr::FilePathSpelledAsFile: |
| // Already handled by above case. |
| return; |
| #define MAGIC_IDENTIFIER_TOKEN(NAME, TOKEN) \ |
| case MagicIdentifierLiteralExpr::NAME: \ |
| kwKind = CodeCompletionKeywordKind::TOKEN; \ |
| break; |
| #define MAGIC_IDENTIFIER_DEPRECATED_TOKEN(NAME, TOKEN) |
| #include "swift/AST/MagicIdentifierKinds.def" |
| } |
| |
| StringRef name = MagicIdentifierLiteralExpr::getKindString(magicKind); |
| if (!needPound) |
| name = name.substr(1); |
| |
| if (!literalKind) { |
| // Pointer type |
| addKeyword(name, "UnsafeRawPointer", kwKind); |
| return; |
| } |
| |
| CodeCompletionResultBuilder builder( |
| Sink, CodeCompletionResult::ResultKind::Keyword, |
| SemanticContextKind::None, {}); |
| builder.setLiteralKind(literalKind.getValue()); |
| builder.setKeywordKind(kwKind); |
| builder.addBaseName(name); |
| addTypeRelationFromProtocol(builder, literalKind.getValue()); |
| }; |
| |
| #define MAGIC_STRING_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ |
| addFromProto(MagicIdentifierLiteralExpr::NAME, \ |
| CodeCompletionLiteralKind::StringLiteral); |
| #define MAGIC_INT_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ |
| addFromProto(MagicIdentifierLiteralExpr::NAME, \ |
| CodeCompletionLiteralKind::IntegerLiteral); |
| #define MAGIC_POINTER_IDENTIFIER(NAME, STRING, SYNTAX_KIND) \ |
| addFromProto(MagicIdentifierLiteralExpr::NAME, \ |
| None); |
| #include "swift/AST/MagicIdentifierKinds.def" |
| } |
| |
| void addValueLiteralCompletions() { |
| auto &context = CurrDeclContext->getASTContext(); |
| |
| auto addFromProto = [&]( |
| CodeCompletionLiteralKind kind, |
| llvm::function_ref<void(CodeCompletionResultBuilder &)> consumer, |
| bool isKeyword = false) { |
| |
| CodeCompletionResultBuilder builder(Sink, CodeCompletionResult::Literal, |
| SemanticContextKind::None, {}); |
| builder.setLiteralKind(kind); |
| |
| consumer(builder); |
| addTypeRelationFromProtocol(builder, kind); |
| }; |
| |
| // FIXME: the pedantically correct way is to resolve Swift.*LiteralType. |
| |
| using LK = CodeCompletionLiteralKind; |
| using Builder = CodeCompletionResultBuilder; |
| |
| // Add literal completions that conform to specific protocols. |
| addFromProto(LK::IntegerLiteral, [](Builder &builder) { |
| builder.addTextChunk("0"); |
| }); |
| addFromProto(LK::BooleanLiteral, [](Builder &builder) { |
| builder.addBaseName("true"); |
| }, /*isKeyword=*/true); |
| addFromProto(LK::BooleanLiteral, [](Builder &builder) { |
| builder.addBaseName("false"); |
| }, /*isKeyword=*/true); |
| addFromProto(LK::NilLiteral, [](Builder &builder) { |
| builder.addBaseName("nil"); |
| }, /*isKeyword=*/true); |
| addFromProto(LK::StringLiteral, [&](Builder &builder) { |
| builder.addTextChunk("\""); |
| builder.addSimpleNamedParameter("abc"); |
| builder.addTextChunk("\""); |
| }); |
| addFromProto(LK::ArrayLiteral, [&](Builder &builder) { |
| builder.addLeftBracket(); |
| builder.addSimpleNamedParameter("values"); |
| builder.addRightBracket(); |
| }); |
| addFromProto(LK::DictionaryLiteral, [&](Builder &builder) { |
| builder.addLeftBracket(); |
| builder.addSimpleNamedParameter("key"); |
| builder.addTextChunk(": "); |
| builder.addSimpleNamedParameter("value"); |
| builder.addRightBracket(); |
| }); |
| |
| auto floatType = context.getFloatDecl()->getDeclaredInterfaceType(); |
| addFromProto(LK::ColorLiteral, [&](Builder &builder) { |
| builder.addBaseName("#colorLiteral"); |
| builder.addLeftParen(); |
| builder.addCallParameter(context.getIdentifier("red"), floatType); |
| builder.addComma(); |
| builder.addCallParameter(context.getIdentifier("green"), floatType); |
| builder.addComma(); |
| builder.addCallParameter(context.getIdentifier("blue"), floatType); |
| builder.addComma(); |
| builder.addCallParameter(context.getIdentifier("alpha"), floatType); |
| builder.addRightParen(); |
| }); |
| |
| auto stringType = context.getStringDecl()->getDeclaredInterfaceType(); |
| addFromProto(LK::ImageLiteral, [&](Builder &builder) { |
| builder.addBaseName("#imageLiteral"); |
| builder.addLeftParen(); |
| builder.addCallParameter(context.getIdentifier("resourceName"), |
| stringType); |
| builder.addRightParen(); |
| }); |
| |
| // Add tuple completion (item, item). |
| { |
| CodeCompletionResultBuilder builder(Sink, CodeCompletionResult::Literal, |
| SemanticContextKind::None, {}); |
| builder.setLiteralKind(LK::Tuple); |
| |
| builder.addLeftParen(); |
| builder.addSimpleNamedParameter("values"); |
| builder.addRightParen(); |
| for (auto T : expectedTypeContext.possibleTypes) { |
| if (T && T->is<TupleType>() && !T->isVoid()) { |
| addTypeAnnotation(builder, T); |
| builder.setExpectedTypeRelation(CodeCompletionResult::Identical); |
| break; |
| } |
| } |
| } |
| } |
| |
| void addObjCPoundKeywordCompletions(bool needPound) { |
| if (!Ctx.LangOpts.EnableObjCInterop) |
| return; |
| |
| // If the expected type is ObjectiveC.Selector, add #selector. If |
| // it's String, add #keyPath. |
| bool addedSelector = false; |
| bool addedKeyPath = false; |
| |
| for (auto T : expectedTypeContext.possibleTypes) { |
| T = T->lookThroughAllOptionalTypes(); |
| if (auto structDecl = T->getStructOrBoundGenericStruct()) { |
| if (!addedSelector && structDecl->getName() == Ctx.Id_Selector && |
| structDecl->getParentModule()->getName() == Ctx.Id_ObjectiveC) { |
| addPoundSelector(needPound); |
| if (addedKeyPath) |
| break; |
| addedSelector = true; |
| continue; |
| } |
| } |
| |
| if (!addedKeyPath && T->getAnyNominal() == Ctx.getStringDecl()) { |
| addPoundKeyPath(needPound); |
| if (addedSelector) |
| break; |
| addedKeyPath = true; |
| continue; |
| } |
| } |
| } |
| |
| struct FilteredDeclConsumer : public swift::VisibleDeclConsumer { |
| swift::VisibleDeclConsumer &Consumer; |
| DeclFilter Filter; |
| FilteredDeclConsumer(swift::VisibleDeclConsumer &Consumer, |
| DeclFilter Filter) : Consumer(Consumer), Filter(Filter) {} |
| void foundDecl(ValueDecl *VD, DeclVisibilityKind Kind, |
| DynamicLookupInfo dynamicLookupInfo) override { |
| if (Filter(VD, Kind)) |
| Consumer.foundDecl(VD, Kind, dynamicLookupInfo); |
| } |
| }; |
| |
| void getValueCompletionsInDeclContext(SourceLoc Loc, |
| DeclFilter Filter = DefaultFilter, |
| bool LiteralCompletions = true, |
| bool ModuleQualifier = true) { |
| ExprType = Type(); |
| Kind = LookupKind::ValueInDeclContext; |
| NeedLeadingDot = false; |
| |
| AccessFilteringDeclConsumer AccessFilteringConsumer( |
| CurrDeclContext, *this); |
| FilteredDeclConsumer FilteringConsumer(AccessFilteringConsumer, Filter); |
| |
| lookupVisibleDecls(FilteringConsumer, CurrDeclContext, |
| /*IncludeTopLevel=*/false, Loc); |
| RequestedCachedResults.push_back(RequestedResultsTy::toplevelResults() |
| .withModuleQualifier(ModuleQualifier)); |
| |
| // Manually add any expected nominal types from imported modules so that |
| // they get their expected type relation. Don't include protocols, since |
| // they can't be initialized from the type name. |
| // FIXME: this does not include types that conform to an expected protocol. |
| // FIXME: this creates duplicate results. |
| for (auto T : expectedTypeContext.possibleTypes) { |
| if (auto NT = T->getAs<NominalType>()) { |
| if (auto NTD = NT->getDecl()) { |
| if (!isa<ProtocolDecl>(NTD) && |
| NTD->getModuleContext() != CurrModule) { |
| addNominalTypeRef(NT->getDecl(), |
| DeclVisibilityKind::VisibleAtTopLevel, {}); |
| } |
| } |
| } |
| } |
| |
| if (CompletionContext) { |
| // FIXME: this is an awful simplification that says all and only enums can |
| // use implicit member syntax (leading dot). Computing the accurate answer |
| // using lookup (e.g. getUnresolvedMemberCompletions) is too expensive, |
| // and for some clients this approximation is good enough. |
| CompletionContext->MayUseImplicitMemberExpr = |
| std::any_of(expectedTypeContext.possibleTypes.begin(), |
| expectedTypeContext.possibleTypes.end(), |
| [](Type T) { |
| if (auto *NTD = T->getAnyNominal()) |
| return isa<EnumDecl>(NTD); |
| return false; |
| }); |
| } |
| |
| if (LiteralCompletions) { |
| addValueLiteralCompletions(); |
| addPoundLiteralCompletions(/*needPound=*/true); |
| } |
| |
| addObjCPoundKeywordCompletions(/*needPound=*/true); |
| } |
| |
| void getUnresolvedMemberCompletions(Type T) { |
| if (!T->mayHaveMembers()) |
| return; |
| |
| if (auto objT = T->getOptionalObjectType()) { |
| // Add 'nil' keyword with erasing '.' instruction. |
| unsigned bytesToErase = 0; |
| auto &SM = CurrDeclContext->getASTContext().SourceMgr; |
| if (DotLoc.isValid()) |
| bytesToErase = SM.getByteDistance(DotLoc, SM.getCodeCompletionLoc()); |
| addKeyword("nil", T, SemanticContextKind::None, |
| CodeCompletionKeywordKind::kw_nil, bytesToErase); |
| } |
| |
| // We can only say .foo where foo is a static member of the contextual |
| // type and has the same type (or if the member is a function, then the |
| // same result type) as the contextual type. |
| FilteredDeclConsumer consumer(*this, [=](ValueDecl *VD, |
| DeclVisibilityKind Reason) { |
| if (T->getOptionalObjectType() && |
| VD->getModuleContext()->isStdlibModule()) { |
| // In optional context, ignore '.init(<some>)', 'init(nilLiteral:)', |
| if (isa<ConstructorDecl>(VD)) |
| return false; |
| } |
| return true; |
| }); |
| |
| auto baseType = MetatypeType::get(T); |
| llvm::SaveAndRestore<LookupKind> SaveLook(Kind, LookupKind::ValueExpr); |
| llvm::SaveAndRestore<Type> SaveType(ExprType, baseType); |
| llvm::SaveAndRestore<bool> SaveUnresolved(IsUnresolvedMember, true); |
| lookupVisibleMemberDecls(consumer, baseType, CurrDeclContext, |
| /*includeInstanceMembers=*/false, |
| /*includeDerivedRequirements*/false, |
| /*includeProtocolExtensionMembers*/true); |
| } |
| |
| void getUnresolvedMemberCompletions(ArrayRef<Type> Types) { |
| NeedLeadingDot = !HaveDot; |
| |
| SmallPtrSet<CanType, 4> seenTypes; |
| for (auto T : Types) { |
| if (!T || !seenTypes.insert(T->getCanonicalType()).second) |
| continue; |
| |
| if (auto objT = T->getOptionalObjectType()) { |
| // If this is optional type, perform completion for the object type. |
| // i.e. 'let _: Enum??? = .enumMember' is legal. |
| objT = objT->lookThroughAllOptionalTypes(); |
| if (seenTypes.insert(objT->getCanonicalType()).second) |
| getUnresolvedMemberCompletions(objT); |
| } |
| getUnresolvedMemberCompletions(T); |
| } |
| } |
| |
| void addCallArgumentCompletionResults( |
| ArrayRef<PossibleParamInfo> ParamInfos, |
| bool isLabeledTrailingClosure = false) { |
| Type ContextType; |
| if (auto typeContext = CurrDeclContext->getInnermostTypeContext()) |
| ContextType = typeContext->getDeclaredTypeInContext(); |
| |
| for (auto Info : ParamInfos) { |
| const auto *Arg = Info.Param; |
| if (!Arg) |
| continue; |
| CodeCompletionResultBuilder Builder( |
| Sink, CodeCompletionResult::ResultKind::Pattern, |
| SemanticContextKind::ExpressionSpecific, {}); |
| Builder.addCallParameter(Arg->getLabel(), Identifier(), |
| Arg->getPlainType(), ContextType, |
| Arg->isVariadic(), Arg->isInOut(), |
| /*isIUO=*/false, Arg->isAutoClosure(), |
| /*useUnderscoreLabel=*/true, |
| isLabeledTrailingClosure); |
| auto Ty = Arg->getPlainType(); |
| if (Arg->isInOut()) { |
| Ty = InOutType::get(Ty); |
| } else if (Arg->isAutoClosure()) { |
| // 'Ty' may be ErrorType. |
| if (auto funcTy = Ty->getAs<FunctionType>()) |
| Ty = funcTy->getResult(); |
| } |
| addTypeAnnotation(Builder, Ty); |
| Builder.setExpectedTypeRelation( |
| CodeCompletionResult::ExpectedTypeRelation::NotApplicable); |
| } |
| } |
| |
| void getTypeCompletions(Type BaseType) { |
| if (tryModuleCompletions(BaseType, /*OnlyTypes=*/true)) |
| return; |
| Kind = LookupKind::Type; |
| this->BaseType = BaseType; |
| NeedLeadingDot = !HaveDot; |
| lookupVisibleMemberDecls(*this, MetatypeType::get(BaseType), |
| CurrDeclContext, |
| IncludeInstanceMembers, |
| /*includeDerivedRequirements*/false, |
| /*includeProtocolExtensionMembers*/false); |
| if (BaseType->isAnyExistentialType()) { |
| addKeyword("Protocol", MetatypeType::get(BaseType)); |
| addKeyword("Type", ExistentialMetatypeType::get(BaseType)); |
| } else if (!BaseType->is<ModuleType>()) { |
| addKeyword("Type", MetatypeType::get(BaseType)); |
| } |
| } |
| |
| void getGenericRequirementCompletions(DeclContext *DC) { |
| auto genericSig = DC->getGenericSignatureOfContext(); |
| if (!genericSig) |
| return; |
| |
| for (auto GPT : genericSig->getGenericParams()) { |
| addGenericTypeParamRef(GPT->getDecl(), |
| DeclVisibilityKind::GenericParameter, {}); |
| } |
| |
| // For non-protocol nominal type decls, only suggest generic parameters. |
| if (auto D = DC->getAsDecl()) |
| if (isa<NominalTypeDecl>(D) && !isa<ProtocolDecl>(D)) |
| return; |
| |
| auto typeContext = DC->getInnermostTypeContext(); |
| if (!typeContext) |
| return; |
| |
| auto selfTy = typeContext->getSelfTypeInContext(); |
| Kind = LookupKind::GenericRequirement; |
| this->BaseType = selfTy; |
| NeedLeadingDot = false; |
| lookupVisibleMemberDecls(*this, MetatypeType::get(selfTy), |
| CurrDeclContext, IncludeInstanceMembers, |
| /*includeDerivedRequirements*/false, |
| /*includeProtocolExtensionMembers*/true); |
| } |
| |
| static bool canUseAttributeOnDecl(DeclAttrKind DAK, bool IsInSil, |
| bool IsConcurrencyEnabled, |
| Optional<DeclKind> DK) { |
| if (DeclAttribute::isUserInaccessible(DAK)) |
| return false; |
| if (DeclAttribute::isDeclModifier(DAK)) |
| return false; |
| if (DeclAttribute::shouldBeRejectedByParser(DAK)) |
| return false; |
| if (!IsInSil && DeclAttribute::isSilOnly(DAK)) |
| return false; |
| if (!IsConcurrencyEnabled && DeclAttribute::isConcurrencyOnly(DAK)) |
| return false; |
| if (!DK.hasValue()) |
| return true; |
| return DeclAttribute::canAttributeAppearOnDeclKind(DAK, DK.getValue()); |
| } |
| |
| void getAttributeDeclCompletions(bool IsInSil, Optional<DeclKind> DK) { |
| // FIXME: also include user-defined attribute keywords |
| StringRef TargetName = "Declaration"; |
| if (DK.hasValue()) { |
| switch (DK.getValue()) { |
| #define DECL(Id, ...) \ |
| case DeclKind::Id: \ |
| TargetName = #Id; \ |
| break; |
| #include "swift/AST/DeclNodes.def" |
| } |
| } |
| bool IsConcurrencyEnabled = Ctx.LangOpts.EnableExperimentalConcurrency; |
| std::string Description = TargetName.str() + " Attribute"; |
| #define DECL_ATTR(KEYWORD, NAME, ...) \ |
| if (canUseAttributeOnDecl(DAK_##NAME, IsInSil, IsConcurrencyEnabled, DK)) \ |
| addDeclAttrKeyword(#KEYWORD, Description); |
| #include "swift/AST/Attr.def" |
| } |
| |
| void getAttributeDeclParamCompletions(DeclAttrKind AttrKind, int ParamIndex) { |
| if (AttrKind == DAK_Available) { |
| if (ParamIndex == 0) { |
| addDeclAttrParamKeyword("*", "Platform", false); |
| |
| #define AVAILABILITY_PLATFORM(X, PrettyName) \ |
| addDeclAttrParamKeyword(swift::platformString(PlatformKind::X), \ |
| "Platform", false); |
| #include "swift/AST/PlatformKinds.def" |
| |
| } else { |
| addDeclAttrParamKeyword("unavailable", "", false); |
| addDeclAttrParamKeyword("message", "Specify message", true); |
| addDeclAttrParamKeyword("renamed", "Specify replacing name", true); |
| addDeclAttrParamKeyword("introduced", "Specify version number", true); |
| addDeclAttrParamKeyword("deprecated", "Specify version number", true); |
| } |
| } |
| } |
| |
| void collectPrecedenceGroups() { |
| assert(CurrDeclContext); |
| |
| if (CurrModule) { |
| for (auto FU: CurrModule->getFiles()) { |
| // We are looking through the current module, |
| // inspect only source files. |
| if (FU->getKind() != FileUnitKind::Source) |
| continue; |
| |
| llvm::SmallVector<PrecedenceGroupDecl*, 4> results; |
| cast<SourceFile>(FU)->getPrecedenceGroups(results); |
| |
| for (auto PG: results) |
| addPrecedenceGroupRef(PG); |
| } |
| } |
| for (auto Import : namelookup::getAllImports(CurrDeclContext)) { |
| auto Module = Import.importedModule; |
| if (Module == CurrModule) |
| continue; |
| |
| RequestedCachedResults.push_back(RequestedResultsTy::fromModule(Module) |
| .onlyPrecedenceGroups() |
| .withModuleQualifier(false)); |
| } |
| } |
| |
| void getPrecedenceGroupCompletions(SyntaxKind SK) { |
| switch (SK) { |
| case SyntaxKind::PrecedenceGroupAssociativity: |
| addKeyword(getAssociativitySpelling(Associativity::None)); |
| addKeyword(getAssociativitySpelling(Associativity::Left)); |
| addKeyword(getAssociativitySpelling(Associativity::Right)); |
| break; |
| case SyntaxKind::PrecedenceGroupAssignment: |
| addKeyword(getTokenText(tok::kw_false), Type(), SemanticContextKind::None, |
| CodeCompletionKeywordKind::kw_false); |
| addKeyword(getTokenText(tok::kw_true), Type(), SemanticContextKind::None, |
| CodeCompletionKeywordKind::kw_true); |
| break; |
| case SyntaxKind::PrecedenceGroupAttributeList: |
| addKeyword("associativity"); |
| addKeyword("higherThan"); |
| addKeyword("lowerThan"); |
| addKeyword("assignment"); |
| break; |
| case SyntaxKind::PrecedenceGroupRelation: |
| collectPrecedenceGroups(); |
| break; |
| default: |
| llvm_unreachable("not a precedencegroup SyntaxKind"); |
| } |
| } |
| |
| void getPoundAvailablePlatformCompletions() { |
| |
| // The platform names should be identical to those in @available. |
| getAttributeDeclParamCompletions(DAK_Available, 0); |
| } |
| |
| void getSelfTypeCompletionInDeclContext(SourceLoc Loc, bool isForDeclResult) { |
| const DeclContext *typeDC = CurrDeclContext->getInnermostTypeContext(); |
| if (!typeDC) |
| return; |
| |
| // For protocols, there's a 'Self' generic parameter. |
| if (typeDC->getSelfProtocolDecl()) |
| return; |
| |
| Type selfType = |
| CurrDeclContext->mapTypeIntoContext(typeDC->getSelfInterfaceType()); |
| |
| if (typeDC->getSelfClassDecl()) { |
| // In classes, 'Self' can be used in result type of func, subscript and |
| // computed property, or inside function bodies. |
| bool canUseDynamicSelf = false; |
| if (isForDeclResult) { |
| canUseDynamicSelf = true; |
| } else { |
| const auto *checkDC = CurrDeclContext; |
| if (isa<TypeAliasDecl>(checkDC)) |
| checkDC = checkDC->getParent(); |
| |
| if (const auto *AFD = checkDC->getInnermostMethodContext()) { |
| canUseDynamicSelf = Ctx.SourceMgr.rangeContainsTokenLoc( |
| AFD->getBodySourceRange(), Loc); |
| } |
| } |
| if (!canUseDynamicSelf) |
| return; |
| // 'Self' in class is a dynamic type. |
| selfType = DynamicSelfType::get(selfType, Ctx); |
| } else { |
| // In enums and structs, 'Self' is just an alias for the nominal type, |
| // and can be used anywhere. |
| } |
| |
| CodeCompletionResultBuilder Builder( |
| Sink, CodeCompletionResult::ResultKind::Keyword, |
| SemanticContextKind::CurrentNominal, expectedTypeContext); |
| Builder.addKeyword("Self"); |
| Builder.setKeywordKind(CodeCompletionKeywordKind::kw_Self); |
| addTypeAnnotation(Builder, selfType); |
| } |
| |
| void getTypeCompletionsInDeclContext(SourceLoc Loc, |
| bool ModuleQualifier = true) { |
| Kind = LookupKind::TypeInDeclContext; |
| |
| AccessFilteringDeclConsumer AccessFilteringConsumer( |
| CurrDeclContext, *this); |
| lookupVisibleDecls(AccessFilteringConsumer, CurrDeclContext, |
| /*IncludeTopLevel=*/false, Loc); |
| |
| RequestedCachedResults.push_back( |
| RequestedResultsTy::toplevelResults() |
| .onlyTypes() |
| .withModuleQualifier(ModuleQualifier)); |
| } |
| |
| void getToplevelCompletions(bool OnlyTypes) { |
| Kind = OnlyTypes ? LookupKind::TypeInDeclContext |
| : LookupKind::ValueInDeclContext; |
| NeedLeadingDot = false; |
| |
| UsableFilteringDeclConsumer UsableFilteringConsumer( |
| Ctx.SourceMgr, CurrDeclContext, Ctx.SourceMgr.getCodeCompletionLoc(), |
| *this); |
| AccessFilteringDeclConsumer AccessFilteringConsumer( |
| CurrDeclContext, UsableFilteringConsumer); |
| |
| CurrModule->lookupVisibleDecls({}, AccessFilteringConsumer, |
| NLKind::UnqualifiedLookup); |
| } |
| |
| void lookupExternalModuleDecls(const ModuleDecl *TheModule, |
| ArrayRef<std::string> AccessPath, |
| bool ResultsHaveLeadingDot) { |
| assert(CurrModule != TheModule && |
| "requested module should be external"); |
| |
| Kind = LookupKind::ImportFromModule; |
| NeedLeadingDot = ResultsHaveLeadingDot; |
| |
| ImportPath::Access::Builder builder; |
| for (auto Piece : AccessPath) { |
| builder.push_back(Ctx.getIdentifier(Piece)); |
| } |
| |
| AccessFilteringDeclConsumer FilteringConsumer(CurrDeclContext, *this); |
| TheModule->lookupVisibleDecls(builder.get(), FilteringConsumer, |
| NLKind::UnqualifiedLookup); |
| |
| llvm::SmallVector<PrecedenceGroupDecl*, 16> precedenceGroups; |
| TheModule->getPrecedenceGroups(precedenceGroups); |
| |
| for (auto PGD: precedenceGroups) |
| addPrecedenceGroupRef(PGD); |
| } |
| |
| void getStmtLabelCompletions(SourceLoc Loc, bool isContinue) { |
| class LabelFinder : public ASTWalker { |
| SourceManager &SM; |
| SourceLoc TargetLoc; |
| bool IsContinue; |
| |
| public: |
| SmallVector<Identifier, 2> Result; |
| |
| LabelFinder(SourceManager &SM, SourceLoc TargetLoc, bool IsContinue) |
| : SM(SM), TargetLoc(TargetLoc), IsContinue(IsContinue) {} |
| |
| std::pair<bool, Stmt *> walkToStmtPre(Stmt *S) override { |
| if (SM.isBeforeInBuffer(S->getEndLoc(), TargetLoc)) |
| return {false, S}; |
| |
| if (LabeledStmt *LS = dyn_cast<LabeledStmt>(S)) { |
| if (LS->getLabelInfo()) { |
| if (!IsContinue || LS->isPossibleContinueTarget()) { |
| auto label = LS->getLabelInfo().Name; |
| if (!llvm::is_contained(Result, label)) |
| Result.push_back(label); |
| } |
| } |
| } |
| |
| return {true, S}; |
| } |
| |
| Stmt *walkToStmtPost(Stmt *S) override { return nullptr; } |
| |
| std::pair<bool, Expr *> walkToExprPre(Expr *E) override { |
| if (SM.isBeforeInBuffer(E->getEndLoc(), TargetLoc)) |
| return {false, E}; |
| return {true, E}; |
| } |
| } Finder(CurrDeclContext->getASTContext().SourceMgr, Loc, isContinue); |
| const_cast<DeclContext *>(CurrDeclContext)->walkContext(Finder); |
| for (auto name : Finder.Result) { |
| CodeCompletionResultBuilder Builder( |
| Sink, CodeCompletionResult::ResultKind::Pattern, |
| SemanticContextKind::Local, {}); |
| Builder.addTextChunk(name.str()); |
| } |
| } |
| }; |
| |
| class CompletionOverrideLookup : public swift::VisibleDeclConsumer { |
| CodeCompletionResultSink &Sink; |
| ASTContext &Ctx; |
| const DeclContext *CurrDeclContext; |
| SmallVectorImpl<StringRef> &ParsedKeywords; |
| SourceLoc introducerLoc; |
| |
| bool hasFuncIntroducer = false; |
| bool hasVarIntroducer = false; |
| bool hasTypealiasIntroducer = false; |
| bool hasInitializerModifier = false; |
| bool hasAccessModifier = false; |
| bool hasOverride = false; |
| bool hasOverridabilityModifier = false; |
| bool hasStaticOrClass = false; |
| |
| public: |
| CompletionOverrideLookup(CodeCompletionResultSink &Sink, ASTContext &Ctx, |
| const DeclContext *CurrDeclContext, |
| SmallVectorImpl<StringRef> &ParsedKeywords, |
| SourceLoc introducerLoc) |
| : Sink(Sink), Ctx(Ctx), CurrDeclContext(CurrDeclContext), |
| ParsedKeywords(ParsedKeywords), introducerLoc(introducerLoc) { |
| hasFuncIntroducer = isKeywordSpecified("func"); |
| hasVarIntroducer = isKeywordSpecified("var") || |
| isKeywordSpecified("let"); |
| hasTypealiasIntroducer = isKeywordSpecified("typealias"); |
| hasInitializerModifier = isKeywordSpecified("required") || |
| isKeywordSpecified("convenience"); |
| hasAccessModifier = isKeywordSpecified("private") || |
| isKeywordSpecified("fileprivate") || |
| isKeywordSpecified("internal") || |
| isKeywordSpecified("public") || |
| isKeywordSpecified("open"); |
| hasOverride = isKeywordSpecified("override"); |
| hasOverridabilityModifier = isKeywordSpecified("final") || |
| isKeywordSpecified("open"); |
| hasStaticOrClass = isKeywordSpecified(getTokenText(tok::kw_class)) || |
| isKeywordSpecified(getTokenText(tok::kw_static)); |
| } |
| |
| bool isKeywordSpecified(StringRef Word) { |
| return std::find(ParsedKeywords.begin(), ParsedKeywords.end(), Word) |
| != ParsedKeywords.end(); |
| } |
| |
| bool missingOverride(DeclVisibilityKind Reason) { |
| return !hasOverride && Reason == DeclVisibilityKind::MemberOfSuper && |
| !CurrDeclContext->getSelfProtocolDecl(); |
| } |
| |
| void addAccessControl(const ValueDecl *VD, |
| CodeCompletionResultBuilder &Builder) { |
| auto CurrentNominal = CurrDeclContext->getSelfNominalTypeDecl(); |
| assert(CurrentNominal); |
| |
| auto AccessOfContext = CurrentNominal->getFormalAccess(); |
| if (AccessOfContext < AccessLevel::Public) |
| return; |
| |
| auto Access = VD->getFormalAccess(); |
| // Use the greater access between the protocol requirement and the witness. |
| // In case of: |
| // |
| // public protocol P { func foo() } |
| // public class B { func foo() {} } |
| // public class C: B, P { |
| // <complete> |
| // } |
| // |
| // 'VD' is 'B.foo()' which is implicitly 'internal'. But as the overriding |
| // declaration, the user needs to write both 'public' and 'override': |
| // |
| // public class C: B { |
| // public override func foo() {} |
| // } |
| if (Access < AccessLevel::Public && |
| !isa<ProtocolDecl>(VD->getDeclContext())) { |
| for (auto Conformance : CurrentNominal->getAllConformances()) { |
| Conformance->getRootConformance()->forEachValueWitness( |
| [&](ValueDecl *req, Witness witness) { |
| if (witness.getDecl() == VD) |
| Access = std::max( |
| Access, Conformance->getProtocol()->getFormalAccess()); |
| }); |
| } |
| } |
| |
| Access = std::min(Access, AccessOfContext); |
| // Only emit 'public', not needed otherwise. |
| if (Access >= AccessLevel::Public) |
| Builder.addAccessControlKeyword(Access); |
| } |
| |
| /// Return type if the result type if \p VD should be represented as opaque |
| /// result type. |
| Type getOpaqueResultType(const ValueDecl *VD, DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo) { |
| if (Reason != |
| DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal) |
| return nullptr; |
| |
| auto currTy = CurrDeclContext->getDeclaredTypeInContext(); |
| if (!currTy) |
| return nullptr; |
| |
| Type ResultT; |
| if (auto *FD = dyn_cast<FuncDecl>(VD)) { |
| if (FD->getGenericParams()) { |
| // Generic function cannot have opaque result type. |
| return nullptr; |
| } |
| ResultT = FD->getResultInterfaceType(); |
| } else if (auto *SD = dyn_cast<SubscriptDecl>(VD)) { |
| if (SD->getGenericParams()) { |
| // Generic subscript cannot have opaque result type. |
| return nullptr; |
| } |
| ResultT = SD->getElementInterfaceType(); |
| } else if (auto *VarD = dyn_cast<VarDecl>(VD)) { |
| ResultT = VarD->getInterfaceType(); |
| } else { |
| return nullptr; |
| } |
| |
| if (!ResultT->is<DependentMemberType>() || |
| !ResultT->castTo<DependentMemberType>()->getAssocType()) |
| // The result is not a valid associatedtype. |
| return nullptr; |
| |
| // Try substitution to see if the associated type is resolved to concrete |
| // type. |
| auto substMap = currTy->getMemberSubstitutionMap( |
| CurrDeclContext->getParentModule(), VD); |
| if (!ResultT.subst(substMap)->is<DependentMemberType>()) |
| // If resolved print it. |
| return nullptr; |
| |
| auto genericSig = VD->getDeclContext()->getGenericSignatureOfContext(); |
| |
| if (genericSig->isConcreteType(ResultT)) |
| // If it has same type requrement, we will emit the concrete type. |
| return nullptr; |
| |
| // Collect requirements on the associatedtype. |
| SmallVector<Type, 2> opaqueTypes; |
| bool hasExplicitAnyObject = false; |
| if (auto superTy = genericSig->getSuperclassBound(ResultT)) |
| opaqueTypes.push_back(superTy); |
| for (const auto proto : genericSig->getRequiredProtocols(ResultT)) |
| opaqueTypes.push_back(proto->getDeclaredInterfaceType()); |
| if (auto layout = genericSig->getLayoutConstraint(ResultT)) |
| hasExplicitAnyObject = layout->isClass(); |
| |
| if (!hasExplicitAnyObject) { |
| if (opaqueTypes.empty()) |
| return nullptr; |
| if (opaqueTypes.size() == 1) |
| return opaqueTypes.front(); |
| } |
| return ProtocolCompositionType::get( |
| VD->getASTContext(), opaqueTypes, hasExplicitAnyObject); |
| } |
| |
| void addValueOverride(const ValueDecl *VD, DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo, |
| CodeCompletionResultBuilder &Builder, |
| bool hasDeclIntroducer) { |
| class DeclPrinter : public StreamPrinter { |
| Type OpaqueBaseTy; |
| |
| public: |
| using StreamPrinter::StreamPrinter; |
| |
| Optional<unsigned> NameOffset; |
| |
| DeclPrinter(raw_ostream &OS, Type OpaqueBaseTy) |
| : StreamPrinter(OS), OpaqueBaseTy(OpaqueBaseTy) {} |
| |
| void printDeclLoc(const Decl *D) override { |
| if (!NameOffset.hasValue()) |
| NameOffset = OS.tell(); |
| } |
| |
| // As for FuncDecl, SubscriptDecl, and VarDecl, |
| void printDeclResultTypePre(ValueDecl *VD, TypeLoc &TL) override { |
| if (!OpaqueBaseTy.isNull()) { |
| OS << "some "; |
| TL = TypeLoc::withoutLoc(OpaqueBaseTy); |
| } |
| } |
| }; |
| |
| llvm::SmallString<256> DeclStr; |
| unsigned NameOffset = 0; |
| { |
| llvm::raw_svector_ostream OS(DeclStr); |
| DeclPrinter Printer( |
| OS, getOpaqueResultType(VD, Reason, dynamicLookupInfo)); |
| PrintOptions Options; |
| if (auto transformType = CurrDeclContext->getDeclaredTypeInContext()) |
| Options.setBaseType(transformType); |
| Options.SkipUnderscoredKeywords = true; |
| Options.PrintImplicitAttrs = false; |
| Options.ExclusiveAttrList.push_back(TAK_escaping); |
| Options.ExclusiveAttrList.push_back(TAK_autoclosure); |
| Options.PrintOverrideKeyword = false; |
| Options.PrintPropertyAccessors = false; |
| Options.PrintSubscriptAccessors = false; |
| Options.PrintStaticKeyword = !hasStaticOrClass; |
| VD->print(Printer, Options); |
| NameOffset = Printer.NameOffset.getValue(); |
| } |
| |
| if (!hasDeclIntroducer && !hasAccessModifier) |
| addAccessControl(VD, Builder); |
| |
| if (missingOverride(Reason)) { |
| if (!hasDeclIntroducer) |
| Builder.addOverrideKeyword(); |
| else { |
| auto dist = Ctx.SourceMgr.getByteDistance( |
| introducerLoc, Ctx.SourceMgr.getCodeCompletionLoc()); |
| if (dist <= CodeCompletionResult::MaxNumBytesToErase) { |
| Builder.setNumBytesToErase(dist); |
| Builder.addOverrideKeyword(); |
| Builder.addDeclIntroducer(DeclStr.str().substr(0, NameOffset)); |
| } |
| } |
| } |
| |
| if (!hasDeclIntroducer && NameOffset != 0) |
| Builder.addDeclIntroducer(DeclStr.str().substr(0, NameOffset)); |
| |
| Builder.addTextChunk(DeclStr.str().substr(NameOffset)); |
| } |
| |
| void addMethodOverride(const FuncDecl *FD, DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo) { |
| CodeCompletionResultBuilder Builder( |
| Sink, CodeCompletionResult::ResultKind::Declaration, |
| SemanticContextKind::Super, {}); |
| Builder.setExpectedTypeRelation( |
| CodeCompletionResult::ExpectedTypeRelation::NotApplicable); |
| Builder.setAssociatedDecl(FD); |
| addValueOverride(FD, Reason, dynamicLookupInfo, Builder, hasFuncIntroducer); |
| Builder.addBraceStmtWithCursor(); |
| } |
| |
| void addVarOverride(const VarDecl *VD, DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo) { |
| // Overrides cannot use 'let', but if the 'override' keyword is specified |
| // then the intention is clear, so provide the results anyway. The compiler |
| // can then provide an error telling you to use 'var' instead. |
| // If we don't need override then it's a protocol requirement, so show it. |
| if (missingOverride(Reason) && hasVarIntroducer && |
| isKeywordSpecified("let")) |
| return; |
| |
| CodeCompletionResultBuilder Builder( |
| Sink, CodeCompletionResult::ResultKind::Declaration, |
| SemanticContextKind::Super, {}); |
| Builder.setAssociatedDecl(VD); |
| addValueOverride(VD, Reason, dynamicLookupInfo, Builder, hasVarIntroducer); |
| } |
| |
| void addSubscriptOverride(const SubscriptDecl *SD, DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo) { |
| CodeCompletionResultBuilder Builder( |
| Sink, CodeCompletionResult::ResultKind::Declaration, |
| SemanticContextKind::Super, {}); |
| Builder.setExpectedTypeRelation( |
| CodeCompletionResult::ExpectedTypeRelation::NotApplicable); |
| Builder.setAssociatedDecl(SD); |
| addValueOverride(SD, Reason, dynamicLookupInfo, Builder, false); |
| Builder.addBraceStmtWithCursor(); |
| } |
| |
| void addTypeAlias(const AssociatedTypeDecl *ATD, DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo) { |
| CodeCompletionResultBuilder Builder(Sink, |
| CodeCompletionResult::ResultKind::Declaration, |
| SemanticContextKind::Super, {}); |
| Builder.setExpectedTypeRelation( |
| CodeCompletionResult::ExpectedTypeRelation::NotApplicable); |
| Builder.setAssociatedDecl(ATD); |
| if (!hasTypealiasIntroducer && !hasAccessModifier) |
| addAccessControl(ATD, Builder); |
| if (!hasTypealiasIntroducer) |
| Builder.addDeclIntroducer("typealias "); |
| Builder.addTextChunk(ATD->getName().str()); |
| Builder.addTextChunk(" = "); |
| Builder.addSimpleNamedParameter("Type"); |
| } |
| |
| void addConstructor(const ConstructorDecl *CD, DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo) { |
| CodeCompletionResultBuilder Builder( |
| Sink, |
| CodeCompletionResult::ResultKind::Declaration, |
| SemanticContextKind::Super, {}); |
| Builder.setExpectedTypeRelation( |
| CodeCompletionResult::ExpectedTypeRelation::NotApplicable); |
| Builder.setAssociatedDecl(CD); |
| |
| if (!hasAccessModifier) |
| addAccessControl(CD, Builder); |
| |
| if (missingOverride(Reason) && CD->isDesignatedInit() && !CD->isRequired()) |
| Builder.addOverrideKeyword(); |
| |
| // Emit 'required' if we're in class context, 'required' is not specified, |
| // and 1) this is a protocol conformance and the class is not final, or 2) |
| // this is subclass and the initializer is marked as required. |
| bool needRequired = false; |
| auto C = CurrDeclContext->getSelfClassDecl(); |
| if (C && !isKeywordSpecified("required")) { |
| switch (Reason) { |
| case DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal: |
| case DeclVisibilityKind::MemberOfProtocolDerivedByCurrentNominal: |
| if (!C->isFinal()) |
| needRequired = true; |
| break; |
| case DeclVisibilityKind::MemberOfSuper: |
| if (CD->isRequired()) |
| needRequired = true; |
| break; |
| default: break; |
| } |
| } |
| |
| llvm::SmallString<256> DeclStr; |
| if (needRequired) |
| DeclStr += "required "; |
| { |
| llvm::raw_svector_ostream OS(DeclStr); |
| PrintOptions Options; |
| if (auto transformType = CurrDeclContext->getDeclaredTypeInContext()) |
| Options.setBaseType(transformType); |
| Options.PrintImplicitAttrs = false; |
| Options.SkipAttributes = true; |
| CD->print(OS, Options); |
| } |
| Builder.addTextChunk(DeclStr); |
| Builder.addBraceStmtWithCursor(); |
| } |
| |
| // Implement swift::VisibleDeclConsumer. |
| void foundDecl(ValueDecl *D, DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo) override { |
| if (Reason == DeclVisibilityKind::MemberOfCurrentNominal) |
| return; |
| |
| if (D->shouldHideFromEditor()) |
| return; |
| |
| if (D->isFinal()) |
| return; |
| |
| bool hasIntroducer = hasFuncIntroducer || |
| hasVarIntroducer || |
| hasTypealiasIntroducer; |
| |
| if (hasStaticOrClass && !D->isStatic()) |
| return; |
| |
| // As per the current convention, only instance members are |
| // suggested if an introducer is not accompanied by a 'static' or |
| // 'class' modifier. |
| if (hasIntroducer && !hasStaticOrClass && D->isStatic()) |
| return; |
| |
| if (auto *FD = dyn_cast<FuncDecl>(D)) { |
| // We cannot override operators as members. |
| if (FD->isBinaryOperator() || FD->isUnaryOperator()) |
| return; |
| |
| // We cannot override individual accessors. |
| if (isa<AccessorDecl>(FD)) |
| return; |
| |
| if (hasFuncIntroducer || (!hasIntroducer && !hasInitializerModifier)) |
| addMethodOverride(FD, Reason, dynamicLookupInfo); |
| return; |
| } |
| |
| if (auto *VD = dyn_cast<VarDecl>(D)) { |
| if (hasVarIntroducer || (!hasIntroducer && !hasInitializerModifier)) |
| addVarOverride(VD, Reason, dynamicLookupInfo); |
| return; |
| } |
| |
| if (auto *SD = dyn_cast<SubscriptDecl>(D)) { |
| if (!hasIntroducer && !hasInitializerModifier) |
| addSubscriptOverride(SD, Reason, dynamicLookupInfo); |
| } |
| |
| if (auto *CD = dyn_cast<ConstructorDecl>(D)) { |
| if (!isa<ProtocolDecl>(CD->getDeclContext())) |
| return; |
| if (hasIntroducer || hasOverride || hasOverridabilityModifier || |
| hasStaticOrClass) |
| return; |
| if (CD->isRequired() || CD->isDesignatedInit()) |
| addConstructor(CD, Reason, dynamicLookupInfo); |
| return; |
| } |
| } |
| |
| void addDesignatedInitializers(NominalTypeDecl *NTD) { |
| if (hasFuncIntroducer || hasVarIntroducer || hasTypealiasIntroducer || |
| hasOverridabilityModifier || hasStaticOrClass) |
| return; |
| |
| const auto *CD = dyn_cast<ClassDecl>(NTD); |
| if (!CD) |
| return; |
| if (!CD->hasSuperclass()) |
| return; |
| CD = CD->getSuperclassDecl(); |
| for (const auto *Member : CD->getMembers()) { |
| const auto *Constructor = dyn_cast<ConstructorDecl>(Member); |
| if (!Constructor) |
| continue; |
| if (Constructor->hasStubImplementation()) |
| continue; |
| if (Constructor->isDesignatedInit()) |
| addConstructor(Constructor, DeclVisibilityKind::MemberOfSuper, {}); |
| } |
| } |
| |
| void addAssociatedTypes(NominalTypeDecl *NTD) { |
| if (!hasTypealiasIntroducer && |
| (hasFuncIntroducer || hasVarIntroducer || hasInitializerModifier || |
| hasOverride || hasOverridabilityModifier || hasStaticOrClass)) |
| return; |
| |
| for (auto Conformance : NTD->getAllConformances()) { |
| auto Proto = Conformance->getProtocol(); |
| if (!Proto->isAccessibleFrom(CurrDeclContext)) |
| continue; |
| for (auto *ATD : Proto->getAssociatedTypeMembers()) { |
| // FIXME: Also exclude the type alias that has already been specified. |
| if (!Conformance->hasTypeWitness(ATD) || |
| ATD->hasDefaultDefinitionType()) |
| continue; |
| addTypeAlias( |
| ATD, |
| DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal, |
| {}); |
| } |
| } |
| } |
| |
| static StringRef getResultBuilderDocComment( |
| ResultBuilderBuildFunction function) { |
| switch (function) { |
| case ResultBuilderBuildFunction::BuildArray: |
| return "Enables support for..in loops in a result builder by " |
| "combining the results of all iterations into a single result"; |
| |
| case ResultBuilderBuildFunction::BuildBlock: |
| return "Required by every result builder to build combined results " |
| "from statement blocks"; |
| |
| case ResultBuilderBuildFunction::BuildEitherFirst: |
| return "With buildEither(second:), enables support for 'if-else' and " |
| "'switch' statements by folding conditional results into a single " |
| "result"; |
| |
| case ResultBuilderBuildFunction::BuildEitherSecond: |
| return "With buildEither(first:), enables support for 'if-else' and " |
| "'switch' statements by folding conditional results into a single " |
| "result"; |
| |
| case ResultBuilderBuildFunction::BuildExpression: |
| return "If declared, provides contextual type information for statement " |
| "expressions to translate them into partial results"; |
| |
| case ResultBuilderBuildFunction::BuildFinalResult: |
| return "If declared, this will be called on the partial result from the " |
| "outermost block statement to produce the final returned result"; |
| |
| case ResultBuilderBuildFunction::BuildLimitedAvailability: |
| return "If declared, this will be called on the partial result of " |
| "an 'if #available' block to allow the result builder to erase " |
| "type information"; |
| |
| case ResultBuilderBuildFunction::BuildOptional: |
| return "Enables support for `if` statements that do not have an `else`"; |
| } |
| } |
| |
| void addResultBuilderBuildCompletion( |
| NominalTypeDecl *builder, Type componentType, |
| ResultBuilderBuildFunction function) { |
| CodeCompletionResultBuilder Builder( |
| Sink, |
| CodeCompletionResult::ResultKind::Pattern, |
| SemanticContextKind::CurrentNominal, {}); |
| Builder.setExpectedTypeRelation( |
| CodeCompletionResult::ExpectedTypeRelation::NotApplicable); |
| |
| if (!hasFuncIntroducer) { |
| if (!hasAccessModifier && |
| builder->getFormalAccess() >= AccessLevel::Public) |
| Builder.addAccessControlKeyword(AccessLevel::Public); |
| |
| if (!hasStaticOrClass) |
| Builder.addTextChunk("static "); |
| |
| Builder.addTextChunk("func "); |
| } |
| |
| std::string declStringWithoutFunc; |
| { |
| llvm::raw_string_ostream out(declStringWithoutFunc); |
| printResultBuilderBuildFunction( |
| builder, componentType, function, None, out); |
| } |
| Builder.addTextChunk(declStringWithoutFunc); |
| Builder.addBraceStmtWithCursor(); |
| Builder.setBriefDocComment(getResultBuilderDocComment(function)); |
| } |
| |
| /// Add completions for the various "build" functions in a result builder. |
| void addResultBuilderBuildCompletions(NominalTypeDecl *builder) { |
| Type componentType = inferResultBuilderComponentType(builder); |
| |
| addResultBuilderBuildCompletion( |
| builder, componentType, ResultBuilderBuildFunction::BuildBlock); |
| addResultBuilderBuildCompletion( |
| builder, componentType, ResultBuilderBuildFunction::BuildExpression); |
| addResultBuilderBuildCompletion( |
| builder, componentType, ResultBuilderBuildFunction::BuildOptional); |
| addResultBuilderBuildCompletion( |
| builder, componentType, ResultBuilderBuildFunction::BuildEitherFirst); |
| addResultBuilderBuildCompletion( |
| builder, componentType, ResultBuilderBuildFunction::BuildEitherSecond); |
| addResultBuilderBuildCompletion( |
| builder, componentType, ResultBuilderBuildFunction::BuildArray); |
| addResultBuilderBuildCompletion( |
| builder, componentType, |
| ResultBuilderBuildFunction::BuildLimitedAvailability); |
| addResultBuilderBuildCompletion( |
| builder, componentType, ResultBuilderBuildFunction::BuildFinalResult); |
| } |
| |
| void getOverrideCompletions(SourceLoc Loc) { |
| if (!CurrDeclContext->isTypeContext()) |
| return; |
| if (isa<ProtocolDecl>(CurrDeclContext)) |
| return; |
| |
| Type CurrTy = CurrDeclContext->getSelfTypeInContext(); |
| auto *NTD = CurrDeclContext->getSelfNominalTypeDecl(); |
| if (CurrTy && !CurrTy->is<ErrorType>()) { |
| // Look for overridable static members too. |
| Type Meta = MetatypeType::get(CurrTy); |
| lookupVisibleMemberDecls(*this, Meta, CurrDeclContext, |
| /*includeInstanceMembers=*/true, |
| /*includeDerivedRequirements*/true, |
| /*includeProtocolExtensionMembers*/false); |
| addDesignatedInitializers(NTD); |
| addAssociatedTypes(NTD); |
| } |
| |
| if (NTD && NTD->getAttrs().hasAttribute<ResultBuilderAttr>()) { |
| addResultBuilderBuildCompletions(NTD); |
| } |
| } |
| }; |
| } // end anonymous namespace |
| |
| static void addSelectorModifierKeywords(CodeCompletionResultSink &sink) { |
| auto addKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind) { |
| CodeCompletionResultBuilder Builder( |
| sink, |
| CodeCompletionResult::ResultKind::Keyword, |
| SemanticContextKind::None, {}); |
| Builder.setKeywordKind(Kind); |
| Builder.addTextChunk(Name); |
| Builder.addCallParameterColon(); |
| Builder.addSimpleTypedParameter("@objc property", /*IsVarArg=*/false); |
| }; |
| |
| addKeyword("getter", CodeCompletionKeywordKind::None); |
| addKeyword("setter", CodeCompletionKeywordKind::None); |
| } |
| |
| void CodeCompletionCallbacksImpl::completeDotExpr(CodeCompletionExpr *E, |
| SourceLoc DotLoc) { |
| assert(P.Tok.is(tok::code_complete)); |
| |
| // Don't produce any results in an enum element. |
| if (InEnumElementRawValue) |
| return; |
| |
| Kind = CompletionKind::DotExpr; |
| if (ParseExprSelectorContext != ObjCSelectorContext::None) { |
| PreferFunctionReferencesToCalls = true; |
| CompleteExprSelectorContext = ParseExprSelectorContext; |
| } |
| |
| ParsedExpr = E->getBase(); |
| this->DotLoc = DotLoc; |
| CurDeclContext = P.CurDeclContext; |
| CodeCompleteTokenExpr = E; |
| } |
| |
| void CodeCompletionCallbacksImpl::completeStmtOrExpr(CodeCompletionExpr *E) { |
| assert(P.Tok.is(tok::code_complete)); |
| Kind = CompletionKind::StmtOrExpr; |
| CurDeclContext = P.CurDeclContext; |
| CodeCompleteTokenExpr = E; |
| } |
| |
| void CodeCompletionCallbacksImpl::completePostfixExprBeginning(CodeCompletionExpr *E) { |
| assert(P.Tok.is(tok::code_complete)); |
| |
| // Don't produce any results in an enum element. |
| if (InEnumElementRawValue) |
| return; |
| |
| Kind = CompletionKind::PostfixExprBeginning; |
| if (ParseExprSelectorContext != ObjCSelectorContext::None) { |
| PreferFunctionReferencesToCalls = true; |
| CompleteExprSelectorContext = ParseExprSelectorContext; |
| if (CompleteExprSelectorContext == ObjCSelectorContext::MethodSelector) { |
| addSelectorModifierKeywords(CompletionContext.getResultSink()); |
| } |
| } |
| |
| |
| CurDeclContext = P.CurDeclContext; |
| CodeCompleteTokenExpr = E; |
| } |
| |
| void CodeCompletionCallbacksImpl::completeForEachSequenceBeginning( |
| CodeCompletionExpr *E) { |
| assert(P.Tok.is(tok::code_complete)); |
| Kind = CompletionKind::ForEachSequence; |
| CurDeclContext = P.CurDeclContext; |
| CodeCompleteTokenExpr = E; |
| } |
| |
| void CodeCompletionCallbacksImpl::completePostfixExpr(Expr *E, bool hasSpace) { |
| assert(P.Tok.is(tok::code_complete)); |
| |
| // Don't produce any results in an enum element. |
| if (InEnumElementRawValue) |
| return; |
| |
| HasSpace = hasSpace; |
| Kind = CompletionKind::PostfixExpr; |
| if (ParseExprSelectorContext != ObjCSelectorContext::None) { |
| PreferFunctionReferencesToCalls = true; |
| CompleteExprSelectorContext = ParseExprSelectorContext; |
| } |
| |
| ParsedExpr = E; |
| CurDeclContext = P.CurDeclContext; |
| } |
| |
| void CodeCompletionCallbacksImpl::completePostfixExprParen(Expr *E, |
| Expr *CodeCompletionE) { |
| assert(P.Tok.is(tok::code_complete)); |
| |
| // Don't produce any results in an enum element. |
| if (InEnumElementRawValue) |
| return; |
| |
| Kind = CompletionKind::PostfixExprParen; |
| ParsedExpr = E; |
| CurDeclContext = P.CurDeclContext; |
| CodeCompleteTokenExpr = static_cast<CodeCompletionExpr*>(CodeCompletionE); |
| |
| ShouldCompleteCallPatternAfterParen = true; |
| if (Context.LangOpts.CodeCompleteCallPatternHeuristics) { |
| // Lookahead one token to decide what kind of call completions to provide. |
| // When it appears that there is already code for the call present, just |
| // complete values and/or argument labels. Otherwise give the entire call |
| // pattern. |
| Token next = P.peekToken(); |
| if (!next.isAtStartOfLine() && !next.is(tok::eof) && !next.is(tok::r_paren)) { |
| ShouldCompleteCallPatternAfterParen = false; |
| } |
| } |
| } |
| |
| void CodeCompletionCallbacksImpl::completeExprKeyPath(KeyPathExpr *KPE, |
| SourceLoc DotLoc) { |
| Kind = (!KPE || KPE->isObjC()) ? CompletionKind::KeyPathExprObjC |
| : CompletionKind::KeyPathExprSwift; |
| ParsedExpr = KPE; |
| this->DotLoc = DotLoc; |
| CurDeclContext = P.CurDeclContext; |
| } |
| |
| void CodeCompletionCallbacksImpl::completePoundAvailablePlatform() { |
| Kind = CompletionKind::PoundAvailablePlatform; |
| CurDeclContext = P.CurDeclContext; |
| } |
| |
| void CodeCompletionCallbacksImpl::completeTypeDeclResultBeginning() { |
| Kind = CompletionKind::TypeDeclResultBeginning; |
| CurDeclContext = P.CurDeclContext; |
| } |
| |
| void CodeCompletionCallbacksImpl::completeTypeSimpleBeginning() { |
| Kind = CompletionKind::TypeSimpleBeginning; |
| CurDeclContext = P.CurDeclContext; |
| } |
| |
| void CodeCompletionCallbacksImpl::completeDeclAttrParam(DeclAttrKind DK, |
| int Index) { |
| Kind = CompletionKind::AttributeDeclParen; |
| AttrKind = DK; |
| AttrParamIndex = Index; |
| CurDeclContext = P.CurDeclContext; |
| } |
| |
| void CodeCompletionCallbacksImpl::completeEffectsSpecifier(bool hasAsync, |
| bool hasThrows) { |
| Kind = CompletionKind::EffectsSpecifier; |
| CurDeclContext = P.CurDeclContext; |
| ParsedKeywords.clear(); |
| if (hasAsync) |
| ParsedKeywords.emplace_back("async"); |
| if (hasThrows) |
| ParsedKeywords.emplace_back("throws"); |
| } |
| |
| void CodeCompletionCallbacksImpl::completeDeclAttrBeginning( |
| bool Sil, bool isIndependent) { |
| Kind = CompletionKind::AttributeBegin; |
| IsInSil = Sil; |
| CurDeclContext = P.CurDeclContext; |
| AttTargetIsIndependent = isIndependent; |
| } |
| |
| void CodeCompletionCallbacksImpl::completeInPrecedenceGroup(SyntaxKind SK) { |
| assert(P.Tok.is(tok::code_complete)); |
| |
| SyntxKind = SK; |
| Kind = CompletionKind::PrecedenceGroup; |
| CurDeclContext = P.CurDeclContext; |
| } |
| |
| void CodeCompletionCallbacksImpl::completeTypeIdentifierWithDot( |
| IdentTypeRepr *ITR) { |
| if (!ITR) { |
| completeTypeSimpleBeginning(); |
| return; |
| } |
| Kind = CompletionKind::TypeIdentifierWithDot; |
| ParsedTypeLoc = TypeLoc(ITR); |
| CurDeclContext = P.CurDeclContext; |
| } |
| |
| void CodeCompletionCallbacksImpl::completeTypeIdentifierWithoutDot( |
| IdentTypeRepr *ITR) { |
| assert(ITR); |
| Kind = CompletionKind::TypeIdentifierWithoutDot; |
| ParsedTypeLoc = TypeLoc(ITR); |
| CurDeclContext = P.CurDeclContext; |
| } |
| |
| void CodeCompletionCallbacksImpl::completeCaseStmtKeyword() { |
| Kind = CompletionKind::CaseStmtKeyword; |
| CurDeclContext = P.CurDeclContext; |
| } |
| |
| void CodeCompletionCallbacksImpl::completeCaseStmtBeginning(CodeCompletionExpr *E) { |
| assert(!InEnumElementRawValue); |
| |
| Kind = CompletionKind::CaseStmtBeginning; |
| CurDeclContext = P.CurDeclContext; |
| CodeCompleteTokenExpr = E; |
| } |
| |
| void CodeCompletionCallbacksImpl::completeImportDecl( |
| ImportPath::Builder &Path) { |
| Kind = CompletionKind::Import; |
| CurDeclContext = P.CurDeclContext; |
| DotLoc = Path.empty() ? SourceLoc() : Path.back().Loc; |
| if (DotLoc.isInvalid()) |
| return; |
| auto Importer = static_cast<ClangImporter *>(CurDeclContext->getASTContext(). |
| getClangModuleLoader()); |
| std::vector<std::string> SubNames; |
| Importer->collectSubModuleNames(Path.get().getModulePath(false), SubNames); |
| ASTContext &Ctx = CurDeclContext->getASTContext(); |
| Path.push_back(Identifier()); |
| for (StringRef Sub : SubNames) { |
| Path.back().Item = Ctx.getIdentifier(Sub); |
| SubModuleNameVisibilityPairs.push_back( |
| std::make_pair(Sub.str(), |
| Ctx.getLoadedModule(Path.get().getModulePath(false)))); |
| } |
| Path.pop_back(); |
| } |
| |
| void CodeCompletionCallbacksImpl::completeUnresolvedMember(CodeCompletionExpr *E, |
| SourceLoc DotLoc) { |
| Kind = CompletionKind::UnresolvedMember; |
| CurDeclContext = P.CurDeclContext; |
| CodeCompleteTokenExpr = E; |
| this->DotLoc = DotLoc; |
| } |
| |
| void CodeCompletionCallbacksImpl::completeCallArg(CodeCompletionExpr *E, |
| bool isFirst) { |
| CurDeclContext = P.CurDeclContext; |
| CodeCompleteTokenExpr = E; |
| Kind = CompletionKind::CallArg; |
| |
| ShouldCompleteCallPatternAfterParen = false; |
| if (isFirst) { |
| ShouldCompleteCallPatternAfterParen = true; |
| if (Context.LangOpts.CodeCompleteCallPatternHeuristics) { |
| // Lookahead one token to decide what kind of call completions to provide. |
| // When it appears that there is already code for the call present, just |
| // complete values and/or argument labels. Otherwise give the entire call |
| // pattern. |
| Token next = P.peekToken(); |
| if (!next.isAtStartOfLine() && !next.is(tok::eof) && !next.is(tok::r_paren)) { |
| ShouldCompleteCallPatternAfterParen = false; |
| } |
| } |
| } |
| } |
| |
| void CodeCompletionCallbacksImpl::completeLabeledTrailingClosure( |
| CodeCompletionExpr *E, bool isAtStartOfLine) { |
| CurDeclContext = P.CurDeclContext; |
| CodeCompleteTokenExpr = E; |
| Kind = CompletionKind::LabeledTrailingClosure; |
| IsAtStartOfLine = isAtStartOfLine; |
| } |
| |
| void CodeCompletionCallbacksImpl::completeReturnStmt(CodeCompletionExpr *E) { |
| CurDeclContext = P.CurDeclContext; |
| CodeCompleteTokenExpr = E; |
| Kind = CompletionKind::ReturnStmtExpr; |
| } |
| |
| void CodeCompletionCallbacksImpl::completeYieldStmt(CodeCompletionExpr *E, |
| Optional<unsigned> index) { |
| CurDeclContext = P.CurDeclContext; |
| CodeCompleteTokenExpr = E; |
| // TODO: use a different completion kind when completing without an index |
| // in a multiple-value context. |
| Kind = CompletionKind::YieldStmtExpr; |
| } |
| |
| void CodeCompletionCallbacksImpl::completeAfterPoundExpr( |
| CodeCompletionExpr *E, Optional<StmtKind> ParentKind) { |
| CurDeclContext = P.CurDeclContext; |
| CodeCompleteTokenExpr = E; |
| Kind = CompletionKind::AfterPoundExpr; |
| ParentStmtKind = ParentKind; |
| } |
| |
| void CodeCompletionCallbacksImpl::completeAfterPoundDirective() { |
| CurDeclContext = P.CurDeclContext; |
| Kind = CompletionKind::AfterPoundDirective; |
| } |
| |
| void CodeCompletionCallbacksImpl::completePlatformCondition() { |
| CurDeclContext = P.CurDeclContext; |
| Kind = CompletionKind::PlatformConditon; |
| } |
| |
| void CodeCompletionCallbacksImpl::completeAfterIfStmt(bool hasElse) { |
| CurDeclContext = P.CurDeclContext; |
| if (hasElse) { |
| Kind = CompletionKind::AfterIfStmtElse; |
| } else { |
| Kind = CompletionKind::StmtOrExpr; |
| } |
| } |
| |
| void CodeCompletionCallbacksImpl::completeGenericRequirement() { |
| CurDeclContext = P.CurDeclContext; |
| Kind = CompletionKind::GenericRequirement; |
| } |
| |
| void CodeCompletionCallbacksImpl::completeNominalMemberBeginning( |
| SmallVectorImpl<StringRef> &Keywords, SourceLoc introducerLoc) { |
| assert(!InEnumElementRawValue); |
| this->introducerLoc = introducerLoc; |
| ParsedKeywords.clear(); |
| ParsedKeywords.append(Keywords.begin(), Keywords.end()); |
| Kind = CompletionKind::NominalMemberBeginning; |
| CurDeclContext = P.CurDeclContext; |
| } |
| |
| void CodeCompletionCallbacksImpl::completeAccessorBeginning( |
| CodeCompletionExpr *E) { |
| Kind = CompletionKind::AccessorBeginning; |
| CurDeclContext = P.CurDeclContext; |
| CodeCompleteTokenExpr = E; |
| } |
| |
| void CodeCompletionCallbacksImpl::completeStmtLabel(StmtKind ParentKind) { |
| CurDeclContext = P.CurDeclContext; |
| Kind = CompletionKind::StmtLabel; |
| ParentStmtKind = ParentKind; |
| } |
| |
| static bool isDynamicLookup(Type T) { |
| return T->getRValueType()->isAnyObject(); |
| } |
| |
| static bool isClangSubModule(ModuleDecl *TheModule) { |
| if (auto ClangMod = TheModule->findUnderlyingClangModule()) |
| return ClangMod->isSubModule(); |
| return false; |
| } |
| |
| static void |
| addKeyword(CodeCompletionResultSink &Sink, StringRef Name, |
| CodeCompletionKeywordKind Kind, StringRef TypeAnnotation = "", |
| CodeCompletionResult::ExpectedTypeRelation TypeRelation = |
| CodeCompletionResult::ExpectedTypeRelation::NotApplicable) { |
| CodeCompletionResultBuilder Builder(Sink, |
| CodeCompletionResult::ResultKind::Keyword, |
| SemanticContextKind::None, {}); |
| Builder.setKeywordKind(Kind); |
| Builder.addKeyword(Name); |
| if (!TypeAnnotation.empty()) |
| Builder.addTypeAnnotation(TypeAnnotation); |
| Builder.setExpectedTypeRelation(TypeRelation); |
| } |
| |
| static void addDeclKeywords(CodeCompletionResultSink &Sink, |
| bool IsConcurrencyEnabled) { |
| auto AddDeclKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind, |
| Optional<DeclAttrKind> DAK) { |
| if (Name == "let" || Name == "var") { |
| // Treat keywords that could be the start of a pattern specially. |
| return; |
| } |
| |
| // FIXME: This should use canUseAttributeOnDecl. |
| |
| // Remove user inaccessible keywords. |
| if (DAK.hasValue() && DeclAttribute::isUserInaccessible(*DAK)) return; |
| |
| // Remove keywords only available when concurrency is enabled. |
| if (DAK.hasValue() && !IsConcurrencyEnabled && |
| DeclAttribute::isConcurrencyOnly(*DAK)) |
| return; |
| |
| addKeyword(Sink, Name, Kind); |
| }; |
| |
| #define DECL_KEYWORD(kw) AddDeclKeyword(#kw, CodeCompletionKeywordKind::kw_##kw, None); |
| #include "swift/Syntax/TokenKinds.def" |
| |
| // Context-sensitive keywords. |
| auto AddCSKeyword = [&](StringRef Name, DeclAttrKind Kind) { |
| AddDeclKeyword(Name, CodeCompletionKeywordKind::None, Kind); |
| }; |
| |
| #define CONTEXTUAL_CASE(KW, CLASS) AddCSKeyword(#KW, DAK_##CLASS); |
| #define CONTEXTUAL_DECL_ATTR(KW, CLASS, ...) CONTEXTUAL_CASE(KW, CLASS) |
| #define CONTEXTUAL_DECL_ATTR_ALIAS(KW, CLASS) CONTEXTUAL_CASE(KW, CLASS) |
| #define CONTEXTUAL_SIMPLE_DECL_ATTR(KW, CLASS, ...) CONTEXTUAL_CASE(KW, CLASS) |
| #include <swift/AST/Attr.def> |
| #undef CONTEXTUAL_CASE |
| |
| } |
| |
| static void addStmtKeywords(CodeCompletionResultSink &Sink, bool MaybeFuncBody) { |
| auto AddStmtKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind) { |
| if (!MaybeFuncBody && Kind == CodeCompletionKeywordKind::kw_return) |
| return; |
| addKeyword(Sink, Name, Kind); |
| }; |
| #define STMT_KEYWORD(kw) AddStmtKeyword(#kw, CodeCompletionKeywordKind::kw_##kw); |
| #include "swift/Syntax/TokenKinds.def" |
| } |
| |
| static void addCaseStmtKeywords(CodeCompletionResultSink &Sink) { |
| addKeyword(Sink, "case", CodeCompletionKeywordKind::kw_case); |
| addKeyword(Sink, "default", CodeCompletionKeywordKind::kw_default); |
| } |
| |
| static void addLetVarKeywords(CodeCompletionResultSink &Sink) { |
| addKeyword(Sink, "let", CodeCompletionKeywordKind::kw_let); |
| addKeyword(Sink, "var", CodeCompletionKeywordKind::kw_var); |
| } |
| |
| static void addAccessorKeywords(CodeCompletionResultSink &Sink) { |
| addKeyword(Sink, "get", CodeCompletionKeywordKind::None); |
| addKeyword(Sink, "set", CodeCompletionKeywordKind::None); |
| } |
| |
| static void addObserverKeywords(CodeCompletionResultSink &Sink) { |
| addKeyword(Sink, "willSet", CodeCompletionKeywordKind::None); |
| addKeyword(Sink, "didSet", CodeCompletionKeywordKind::None); |
| } |
| |
| static void addExprKeywords(CodeCompletionResultSink &Sink, |
| bool IsConcurrencyEnabled) { |
| // Expr keywords. |
| addKeyword(Sink, "try", CodeCompletionKeywordKind::kw_try); |
| addKeyword(Sink, "try!", CodeCompletionKeywordKind::kw_try); |
| addKeyword(Sink, "try?", CodeCompletionKeywordKind::kw_try); |
| if (IsConcurrencyEnabled) { |
| addKeyword(Sink, "await", CodeCompletionKeywordKind::None); |
| } |
| } |
| |
| static void addOpaqueTypeKeyword(CodeCompletionResultSink &Sink) { |
| addKeyword(Sink, "some", CodeCompletionKeywordKind::None, "some"); |
| } |
| |
| static void addAnyTypeKeyword(CodeCompletionResultSink &Sink, Type T) { |
| CodeCompletionResultBuilder Builder(Sink, |
| CodeCompletionResult::ResultKind::Keyword, |
| SemanticContextKind::None, {}); |
| Builder.setKeywordKind(CodeCompletionKeywordKind::None); |
| Builder.addKeyword("Any"); |
| Builder.addTypeAnnotation(T, PrintOptions()); |
| } |
| |
| void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink, |
| bool MaybeFuncBody) { |
| switch (Kind) { |
| case CompletionKind::None: |
| case CompletionKind::DotExpr: |
| case CompletionKind::AttributeDeclParen: |
| case CompletionKind::AttributeBegin: |
| case CompletionKind::PoundAvailablePlatform: |
| case CompletionKind::Import: |
| case CompletionKind::UnresolvedMember: |
| case CompletionKind::CallArg: |
| case CompletionKind::LabeledTrailingClosure: |
| case CompletionKind::AfterPoundExpr: |
| case CompletionKind::AfterPoundDirective: |
| case CompletionKind::PlatformConditon: |
| case CompletionKind::GenericRequirement: |
| case CompletionKind::KeyPathExprObjC: |
| case CompletionKind::KeyPathExprSwift: |
| case CompletionKind::PrecedenceGroup: |
| case CompletionKind::StmtLabel: |
| break; |
| |
| case CompletionKind::EffectsSpecifier: { |
| if (!llvm::is_contained(ParsedKeywords, "async") && |
| Context.LangOpts.EnableExperimentalConcurrency) |
| addKeyword(Sink, "async", CodeCompletionKeywordKind::None); |
| if (!llvm::is_contained(ParsedKeywords, "throws")) |
| addKeyword(Sink, "throws", CodeCompletionKeywordKind::kw_throws); |
| break; |
| } |
| |
| case CompletionKind::AccessorBeginning: { |
| // TODO: Omit already declared or mutally exclusive accessors. |
| // E.g. If 'get' is already declared, emit 'set' only. |
| addAccessorKeywords(Sink); |
| |
| // Only 'var' for non-protocol context can have 'willSet' and 'didSet'. |
| assert(ParsedDecl); |
| VarDecl *var = dyn_cast<VarDecl>(ParsedDecl); |
| if (auto accessor = dyn_cast<AccessorDecl>(ParsedDecl)) |
| var = dyn_cast<VarDecl>(accessor->getStorage()); |
| if (var && !var->getDeclContext()->getSelfProtocolDecl()) |
| addObserverKeywords(Sink); |
| |
| if (!isa<AccessorDecl>(ParsedDecl)) |
| break; |
| |
| MaybeFuncBody = true; |
| LLVM_FALLTHROUGH; |
| } |
| case CompletionKind::StmtOrExpr: |
| addDeclKeywords(Sink, Context.LangOpts.EnableExperimentalConcurrency); |
| addStmtKeywords(Sink, MaybeFuncBody); |
| LLVM_FALLTHROUGH; |
| case CompletionKind::ReturnStmtExpr: |
| case CompletionKind::YieldStmtExpr: |
| case CompletionKind::PostfixExprBeginning: |
| case CompletionKind::ForEachSequence: |
| addSuperKeyword(Sink); |
| addLetVarKeywords(Sink); |
| addExprKeywords(Sink, Context.LangOpts.EnableExperimentalConcurrency); |
| addAnyTypeKeyword(Sink, CurDeclContext->getASTContext().TheAnyType); |
| break; |
| |
| case CompletionKind::CaseStmtKeyword: |
| addCaseStmtKeywords(Sink); |
| break; |
| |
| case CompletionKind::PostfixExpr: |
| case CompletionKind::PostfixExprParen: |
| case CompletionKind::CaseStmtBeginning: |
| case CompletionKind::TypeIdentifierWithDot: |
| case CompletionKind::TypeIdentifierWithoutDot: |
| break; |
| |
| case CompletionKind::TypeDeclResultBeginning: { |
| auto DC = CurDeclContext; |
| if (ParsedDecl && ParsedDecl == CurDeclContext->getAsDecl()) |
| DC = ParsedDecl->getDeclContext(); |
| if (!isa<ProtocolDecl>(DC)) |
| if (DC->isTypeContext() || (ParsedDecl && isa<FuncDecl>(ParsedDecl))) |
| addOpaqueTypeKeyword(Sink); |
| |
| LLVM_FALLTHROUGH; |
| } |
| case CompletionKind::TypeSimpleBeginning: |
| addAnyTypeKeyword(Sink, CurDeclContext->getASTContext().TheAnyType); |
| break; |
| |
| case CompletionKind::NominalMemberBeginning: { |
| bool HasDeclIntroducer = llvm::find_if(ParsedKeywords, |
| [this](const StringRef kw) { |
| return llvm::StringSwitch<bool>(kw) |
| .Case("associatedtype", true) |
| .Case("class", !CurDeclContext || !isa<ClassDecl>(CurDeclContext)) |
| .Case("deinit", true) |
| .Case("enum", true) |
| .Case("extension", true) |
| .Case("func", true) |
| .Case("import", true) |
| .Case("init", true) |
| .Case("let", true) |
| .Case("operator", true) |
| .Case("precedencegroup", true) |
| .Case("protocol", true) |
| .Case("struct", true) |
| .Case("subscript", true) |
| .Case("typealias", true) |
| .Case("var", true) |
| .Default(false); |
| }) != ParsedKeywords.end(); |
| if (!HasDeclIntroducer) { |
| addDeclKeywords(Sink, Context.LangOpts.EnableExperimentalConcurrency); |
| addLetVarKeywords(Sink); |
| } |
| break; |
| } |
| |
| case CompletionKind::AfterIfStmtElse: |
| addKeyword(Sink, "if", CodeCompletionKeywordKind::kw_if); |
| break; |
| } |
| } |
| |
| static void addPoundDirectives(CodeCompletionResultSink &Sink) { |
| auto addWithName = |
| [&](StringRef name, CodeCompletionKeywordKind K, |
| llvm::function_ref<void(CodeCompletionResultBuilder &)> consumer = |
| nullptr) { |
| CodeCompletionResultBuilder Builder(Sink, CodeCompletionResult::Keyword, |
| SemanticContextKind::None, {}); |
| Builder.addBaseName(name); |
| Builder.setKeywordKind(K); |
| if (consumer) |
| consumer(Builder); |
| }; |
| |
| addWithName("sourceLocation", CodeCompletionKeywordKind::pound_sourceLocation, |
| [&] (CodeCompletionResultBuilder &Builder) { |
| Builder.addLeftParen(); |
| Builder.addTextChunk("file"); |
| Builder.addCallParameterColon(); |
| Builder.addSimpleTypedParameter("String"); |
| Builder.addComma(); |
| Builder.addTextChunk("line"); |
| Builder.addCallParameterColon(); |
| Builder.addSimpleTypedParameter("Int"); |
| Builder.addRightParen(); |
| }); |
| addWithName("warning", CodeCompletionKeywordKind::pound_warning, |
| [&] (CodeCompletionResultBuilder &Builder) { |
| Builder.addLeftParen(); |
| Builder.addTextChunk("\""); |
| Builder.addSimpleNamedParameter("message"); |
| Builder.addTextChunk("\""); |
| Builder.addRightParen(); |
| }); |
| addWithName("error", CodeCompletionKeywordKind::pound_error, |
| [&] (CodeCompletionResultBuilder &Builder) { |
| Builder.addLeftParen(); |
| Builder.addTextChunk("\""); |
| Builder.addSimpleNamedParameter("message"); |
| Builder.addTextChunk("\""); |
| Builder.addRightParen(); |
| }); |
| |
| addWithName("if ", CodeCompletionKeywordKind::pound_if, |
| [&] (CodeCompletionResultBuilder &Builder) { |
| Builder.addSimpleNamedParameter("condition"); |
| }); |
| |
| // FIXME: These directives are only valid in conditional completion block. |
| addWithName("elseif ", CodeCompletionKeywordKind::pound_elseif, |
| [&] (CodeCompletionResultBuilder &Builder) { |
| Builder.addSimpleNamedParameter("condition"); |
| }); |
| addWithName("else", CodeCompletionKeywordKind::pound_else); |
| addWithName("endif", CodeCompletionKeywordKind::pound_endif); |
| } |
| |
| /// Add platform conditions used in '#if' and '#elseif' directives. |
| static void addPlatformConditions(CodeCompletionResultSink &Sink) { |
| auto addWithName = |
| [&](StringRef Name, |
| llvm::function_ref<void(CodeCompletionResultBuilder & Builder)> |
| consumer) { |
| CodeCompletionResultBuilder Builder( |
| Sink, CodeCompletionResult::ResultKind::Pattern, |
| SemanticContextKind::ExpressionSpecific, {}); |
| Builder.addBaseName(Name); |
| Builder.addLeftParen(); |
| consumer(Builder); |
| Builder.addRightParen(); |
| }; |
| addWithName("os", [](CodeCompletionResultBuilder &Builder) { |
| Builder.addSimpleNamedParameter("name"); |
| }); |
| addWithName("arch", [](CodeCompletionResultBuilder &Builder) { |
| Builder.addSimpleNamedParameter("name"); |
| }); |
| addWithName("canImport", [](CodeCompletionResultBuilder &Builder) { |
| Builder.addSimpleNamedParameter("module"); |
| }); |
| addWithName("targetEnvironment", [](CodeCompletionResultBuilder &Builder) { |
| Builder.addTextChunk("simulator"); |
| }); |
| addWithName("swift", [](CodeCompletionResultBuilder &Builder) { |
| Builder.addTextChunk(">="); |
| Builder.addSimpleNamedParameter("version"); |
| }); |
| addWithName("swift", [](CodeCompletionResultBuilder &Builder) { |
| Builder.addTextChunk("<"); |
| Builder.addSimpleNamedParameter("version"); |
| }); |
| addWithName("compiler", [](CodeCompletionResultBuilder &Builder) { |
| Builder.addTextChunk(">="); |
| Builder.addSimpleNamedParameter("version"); |
| }); |
| addWithName("compiler", [](CodeCompletionResultBuilder &Builder) { |
| Builder.addTextChunk("<"); |
| Builder.addSimpleNamedParameter("version"); |
| }); |
| |
| addKeyword(Sink, "true", CodeCompletionKeywordKind::kw_true, "Bool"); |
| addKeyword(Sink, "false", CodeCompletionKeywordKind::kw_false, "Bool"); |
| } |
| |
| /// Add flags specified by '-D' to completion results. |
| static void addConditionalCompilationFlags(ASTContext &Ctx, |
| CodeCompletionResultSink &Sink) { |
| for (auto Flag : Ctx.LangOpts.getCustomConditionalCompilationFlags()) { |
| // TODO: Should we filter out some flags? |
| CodeCompletionResultBuilder Builder( |
| Sink, CodeCompletionResult::ResultKind::Keyword, |
| SemanticContextKind::ExpressionSpecific, {}); |
| Builder.addTextChunk(Flag); |
| } |
| } |
| |
| static void deliverCompletionResults(CodeCompletionContext &CompletionContext, |
| CompletionLookup &Lookup, |
| SourceFile &SF, |
| CodeCompletionConsumer &Consumer) { |
| llvm::SmallPtrSet<Identifier, 8> seenModuleNames; |
| std::vector<RequestedCachedModule> RequestedModules; |
| |
| for (auto &Request: Lookup.RequestedCachedResults) { |
| llvm::DenseSet<CodeCompletionCache::Key> ImportsSeen; |
| auto handleImport = [&](ImportedModule Import) { |
| ModuleDecl *TheModule = Import.importedModule; |
| ImportPath::Access Path = Import.accessPath; |
| if (TheModule->getFiles().empty()) |
| return; |
| |
| // Clang submodules are ignored and there's no lookup cost involved, |
| // so just ignore them and don't put the empty results in the cache |
| // because putting a lot of objects in the cache will push out |
| // other lookups. |
| if (isClangSubModule(TheModule)) |
| return; |
| |
| std::vector<std::string> AccessPath; |
| for (auto Piece : Path) { |
| AccessPath.push_back(std::string(Piece.Item)); |
| } |
| |
| StringRef ModuleFilename = TheModule->getModuleFilename(); |
| // ModuleFilename can be empty if something strange happened during |
| // module loading, for example, the module file is corrupted. |
| if (!ModuleFilename.empty()) { |
| auto &Ctx = TheModule->getASTContext(); |
| CodeCompletionCache::Key K{ |
| ModuleFilename.str(), |
| std::string(TheModule->getName()), |
| AccessPath, |
| Request.NeedLeadingDot, |
| SF.hasTestableOrPrivateImport( |
| AccessLevel::Internal, TheModule, |
| SourceFile::ImportQueryKind::TestableOnly), |
| SF.hasTestableOrPrivateImport( |
| AccessLevel::Internal, TheModule, |
| SourceFile::ImportQueryKind::PrivateOnly), |
| Ctx.LangOpts.CodeCompleteInitsInPostfixExpr, |
| CompletionContext.getAnnotateResult(), |
| }; |
| |
| using PairType = llvm::DenseSet<swift::ide::CodeCompletionCache::Key, |
| llvm::DenseMapInfo<CodeCompletionCache::Key>>::iterator; |
| std::pair<PairType, bool> Result = ImportsSeen.insert(K); |
| if (!Result.second) |
| return; // already handled. |
| RequestedModules.push_back({std::move(K), TheModule, |
| Request.OnlyTypes, Request.OnlyPrecedenceGroups}); |
| |
| if (Request.IncludeModuleQualifier && |
| seenModuleNames.insert(TheModule->getName()).second) |
| Lookup.addModuleName(TheModule); |
| } |
| }; |
| |
| if (Request.TheModule) { |
| // FIXME: actually check imports. |
| for (auto Import : namelookup::getAllImports(Request.TheModule)) { |
| handleImport(Import); |
| } |
| } else { |
| // Add results from current module. |
| Lookup.getToplevelCompletions(Request.OnlyTypes); |
| |
| // Add the qualifying module name |
| auto curModule = SF.getParentModule(); |
| if (Request.IncludeModuleQualifier && |
| seenModuleNames.insert(curModule->getName()).second) |
| Lookup.addModuleName(curModule); |
| |
| // Add results for all imported modules. |
| SmallVector<ImportedModule, 4> Imports; |
| SF.getImportedModules( |
| Imports, {ModuleDecl::ImportFilterKind::Exported, |
| ModuleDecl::ImportFilterKind::Default, |
| ModuleDecl::ImportFilterKind::ImplementationOnly}); |
| |
| for (auto Imported : Imports) { |
| for (auto Import : namelookup::getAllImports(Imported.importedModule)) |
| handleImport(Import); |
| } |
| } |
| } |
| Lookup.RequestedCachedResults.clear(); |
| CompletionContext.typeContextKind = Lookup.typeContextKind(); |
| |
| // Use the current SourceFile as the DeclContext so that we can use it to |
| // perform qualified lookup, and to get the correct visibility for |
| // @testable imports. |
| DeclContext *DCForModules = &SF; |
| Consumer.handleResultsAndModules(CompletionContext, RequestedModules, |
| DCForModules); |
| } |
| |
| void deliverUnresolvedMemberResults( |
| ArrayRef<UnresolvedMemberTypeCheckCompletionCallback::Result> Results, |
| DeclContext *DC, SourceLoc DotLoc, |
| ide::CodeCompletionContext &CompletionCtx, |
| CodeCompletionConsumer &Consumer) { |
| ASTContext &Ctx = DC->getASTContext(); |
| CompletionLookup Lookup(CompletionCtx.getResultSink(), Ctx, DC, |
| &CompletionCtx); |
| |
| assert(DotLoc.isValid()); |
| Lookup.setHaveDot(DotLoc); |
| Lookup.shouldCheckForDuplicates(Results.size() > 1); |
| |
| // Get the canonical versions of the top-level types |
| SmallPtrSet<CanType, 4> originalTypes; |
| for (auto &Result: Results) |
| originalTypes.insert(Result.ExpectedTy->getCanonicalType()); |
| |
| for (auto &Result: Results) { |
| Lookup.setExpectedTypes({Result.ExpectedTy}, |
| Result.IsImplicitSingleExpressionReturn, |
| /*expectsNonVoid*/true); |
| Lookup.setIdealExpectedType(Result.ExpectedTy); |
| |
| // For optional types, also get members of the unwrapped type if it's not |
| // already equivalent to one of the top-level types. Handling it via the top |
| // level type and not here ensures we give the correct type relation |
| // (identical, rather than convertible). |
| if (Result.ExpectedTy->getOptionalObjectType()) { |
| Type Unwrapped = Result.ExpectedTy->lookThroughAllOptionalTypes(); |
| if (originalTypes.insert(Unwrapped->getCanonicalType()).second) |
| Lookup.getUnresolvedMemberCompletions(Unwrapped); |
| } |
| Lookup.getUnresolvedMemberCompletions(Result.ExpectedTy); |
| } |
| SourceFile *SF = DC->getParentSourceFile(); |
| deliverCompletionResults(CompletionCtx, Lookup, *SF, Consumer); |
| } |
| |
| void deliverDotExprResults( |
| ArrayRef<DotExprTypeCheckCompletionCallback::Result> Results, |
| Expr *BaseExpr, DeclContext *DC, SourceLoc DotLoc, bool IsInSelector, |
| ide::CodeCompletionContext &CompletionCtx, |
| CodeCompletionConsumer &Consumer) { |
| ASTContext &Ctx = DC->getASTContext(); |
| CompletionLookup Lookup(CompletionCtx.getResultSink(), Ctx, DC, |
| &CompletionCtx); |
| |
| if (DotLoc.isValid()) |
| Lookup.setHaveDot(DotLoc); |
| |
| Lookup.setIsSuperRefExpr(isa<SuperRefExpr>(BaseExpr)); |
| |
| if (auto *DRE = dyn_cast<DeclRefExpr>(BaseExpr)) |
| Lookup.setIsSelfRefExpr(DRE->getDecl()->getName() == Ctx.Id_self); |
| |
| if (isa<BindOptionalExpr>(BaseExpr) || isa<ForceValueExpr>(BaseExpr)) |
| Lookup.setIsUnwrappedOptional(true); |
| |
| if (IsInSelector) { |
| Lookup.includeInstanceMembers(); |
| Lookup.setPreferFunctionReferencesToCalls(); |
| } |
| |
| Lookup.shouldCheckForDuplicates(Results.size() > 1); |
| for (auto &Result: Results) { |
| Lookup.setIsStaticMetatype(Result.BaseIsStaticMetaType); |
| Lookup.getPostfixKeywordCompletions(Result.BaseTy, BaseExpr); |
| Lookup.setExpectedTypes(Result.ExpectedTypes, |
| Result.IsImplicitSingleExpressionReturn, |
| Result.ExpectsNonVoid); |
| if (isDynamicLookup(Result.BaseTy)) |
| Lookup.setIsDynamicLookup(); |
| Lookup.getValueExprCompletions(Result.BaseTy, Result.BaseDecl); |
| } |
| |
| SourceFile *SF = DC->getParentSourceFile(); |
| deliverCompletionResults(CompletionCtx, Lookup, *SF, Consumer); |
| } |
| |
| bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) { |
| assert(ParsedExpr || CurDeclContext); |
| |
| SourceLoc CompletionLoc = ParsedExpr |
| ? ParsedExpr->getLoc() |
| : CurDeclContext->getASTContext().SourceMgr.getCodeCompletionLoc(); |
| |
| switch (Kind) { |
| case CompletionKind::DotExpr: { |
| assert(CodeCompleteTokenExpr); |
| assert(CurDeclContext); |
| |
| DotExprTypeCheckCompletionCallback Lookup(CurDeclContext, |
| CodeCompleteTokenExpr); |
| llvm::SaveAndRestore<TypeCheckCompletionCallback*> |
| CompletionCollector(Context.CompletionCallback, &Lookup); |
| typeCheckContextAt(CurDeclContext, CompletionLoc); |
| |
| // This (hopefully) only happens in cases where the expression isn't |
| // typechecked during normal compilation either (e.g. member completion in a |
| // switch case where there control expression is invalid). Having normal |
| // typechecking still resolve even these cases would be beneficial for |
| // tooling in general though. |
| if (!Lookup.gotCallback()) |
| Lookup.fallbackTypeCheck(); |
| |
| addKeywords(CompletionContext.getResultSink(), MaybeFuncBody); |
| |
| Expr *CheckedBase = CodeCompleteTokenExpr->getBase(); |
| deliverDotExprResults(Lookup.getResults(), CheckedBase, CurDeclContext, |
| DotLoc, isInsideObjCSelector(), CompletionContext, |
| Consumer); |
| return true; |
| } |
| case CompletionKind::UnresolvedMember: { |
| assert(CodeCompleteTokenExpr); |
| assert(CurDeclContext); |
| |
| UnresolvedMemberTypeCheckCompletionCallback Lookup(CodeCompleteTokenExpr); |
| llvm::SaveAndRestore<TypeCheckCompletionCallback*> |
| CompletionCollector(Context.CompletionCallback, &Lookup); |
| typeCheckContextAt(CurDeclContext, CompletionLoc); |
| |
| if (!Lookup.gotCallback()) |
| Lookup.fallbackTypeCheck(CurDeclContext); |
| |
| addKeywords(CompletionContext.getResultSink(), MaybeFuncBody); |
| deliverUnresolvedMemberResults(Lookup.getResults(), CurDeclContext, DotLoc, |
| CompletionContext, Consumer); |
| return true; |
| } |
| default: |
| return false; |
| } |
| } |
| |
| // Undoes the single-expression closure/function body transformation on the |
| // given DeclContext and its parent contexts if they have a single expression |
| // body that contains the code completion location. |
| // |
| // FIXME: Remove this once all expression position completions are migrated |
| // to work via TypeCheckCompletionCallback. |
| static void undoSingleExpressionReturn(DeclContext *DC) { |
| auto updateBody = [](BraceStmt *BS, ASTContext &Ctx) -> bool { |
| ASTNode LastElem = BS->getLastElement(); |
| auto *RS = dyn_cast_or_null<ReturnStmt>(LastElem.dyn_cast<Stmt*>()); |
| |
| if (!RS || !RS->isImplicit()) |
| return false; |
| |
| BS->setLastElement(RS->getResult()); |
| return true; |
| }; |
| |
| while (ClosureExpr *CE = dyn_cast_or_null<ClosureExpr>(DC)) { |
| if (CE->hasSingleExpressionBody()) { |
| if (updateBody(CE->getBody(), CE->getASTContext())) |
| CE->setBody(CE->getBody(), false); |
| } |
| DC = DC->getParent(); |
| } |
| if (FuncDecl *FD = dyn_cast_or_null<FuncDecl>(DC)) { |
| if (FD->hasSingleExpressionBody()) { |
| if (updateBody(FD->getBody(), FD->getASTContext())) |
| FD->setHasSingleExpressionBody(false); |
| } |
| } |
| } |
| |
| void CodeCompletionCallbacksImpl::doneParsing() { |
| CompletionContext.CodeCompletionKind = Kind; |
| |
| if (Kind == CompletionKind::None) { |
| return; |
| } |
| |
| bool MaybeFuncBody = true; |
| if (CurDeclContext) { |
| auto *CD = CurDeclContext->getLocalContext(); |
| if (!CD || CD->getContextKind() == DeclContextKind::Initializer || |
| CD->getContextKind() == DeclContextKind::TopLevelCodeDecl) |
| MaybeFuncBody = false; |
| } |
| |
| if (auto *DC = dyn_cast_or_null<DeclContext>(ParsedDecl)) { |
| if (DC->isChildContextOf(CurDeclContext)) |
| CurDeclContext = DC; |
| } |
| |
| if (trySolverCompletion(MaybeFuncBody)) |
| return; |
| |
| undoSingleExpressionReturn(CurDeclContext); |
| typeCheckContextAt( |
| CurDeclContext, |
| ParsedExpr |
| ? ParsedExpr->getLoc() |
| : CurDeclContext->getASTContext().SourceMgr.getCodeCompletionLoc()); |
| |
| // Add keywords even if type checking fails completely. |
| addKeywords(CompletionContext.getResultSink(), MaybeFuncBody); |
| |
| Optional<Type> ExprType; |
| ConcreteDeclRef ReferencedDecl = nullptr; |
| if (ParsedExpr) { |
| if (auto *checkedExpr = findParsedExpr(CurDeclContext, |
| ParsedExpr->getSourceRange())) { |
| ParsedExpr = checkedExpr; |
| } |
| |
| if (auto typechecked = typeCheckParsedExpr()) { |
| ExprType = typechecked->first; |
| ReferencedDecl = typechecked->second; |
| ParsedExpr->setType(*ExprType); |
| } |
| |
| if (!ExprType && Kind != CompletionKind::PostfixExprParen && |
| Kind != CompletionKind::CallArg && |
| Kind != CompletionKind::KeyPathExprObjC) |
| return; |
| } |
| |
| if (!ParsedTypeLoc.isNull() && !typecheckParsedType()) |
| return; |
| |
| CompletionLookup Lookup(CompletionContext.getResultSink(), P.Context, |
| CurDeclContext, &CompletionContext); |
| if (ExprType) { |
| Lookup.setIsStaticMetatype(ParsedExpr->isStaticallyDerivedMetatype()); |
| } |
| if (auto *DRE = dyn_cast_or_null<DeclRefExpr>(ParsedExpr)) { |
| Lookup.setIsSelfRefExpr(DRE->getDecl()->getName() == Context.Id_self); |
| } else if (ParsedExpr && isa<SuperRefExpr>(ParsedExpr)) { |
| Lookup.setIsSuperRefExpr(); |
| } |
| |
| if (isInsideObjCSelector()) |
| Lookup.includeInstanceMembers(); |
| if (PreferFunctionReferencesToCalls) |
| Lookup.setPreferFunctionReferencesToCalls(); |
| |
| auto DoPostfixExprBeginning = [&] (){ |
| SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc(); |
| Lookup.getValueCompletionsInDeclContext(Loc); |
| Lookup.getSelfTypeCompletionInDeclContext(Loc, /*isForDeclResult=*/false); |
| }; |
| |
| switch (Kind) { |
| case CompletionKind::None: |
| case CompletionKind::DotExpr: |
| case CompletionKind::UnresolvedMember: |
| llvm_unreachable("should be already handled"); |
| return; |
| |
| case CompletionKind::KeyPathExprSwift: { |
| auto KPE = dyn_cast<KeyPathExpr>(ParsedExpr); |
| auto BGT = (*ExprType)->getAs<BoundGenericType>(); |
| if (!KPE || !BGT || BGT->getGenericArgs().size() != 2) |
| break; |
| assert(!KPE->isObjC()); |
| |
| if (DotLoc.isValid()) |
| Lookup.setHaveDot(DotLoc); |
| |
| bool OnRoot = !KPE->getComponents().front().isValid(); |
| Lookup.setIsSwiftKeyPathExpr(OnRoot); |
| |
| Type baseType = BGT->getGenericArgs()[OnRoot ? 0 : 1]; |
| if (OnRoot && baseType->is<UnresolvedType>()) { |
| // Infer the root type of the keypath from the context type. |
| ExprContextInfo ContextInfo(CurDeclContext, ParsedExpr); |
| for (auto T : ContextInfo.getPossibleTypes()) { |
| if (auto unwrapped = T->getOptionalObjectType()) |
| T = unwrapped; |
| |
| // If the context type is any of the KeyPath types, use it. |
| if (T->getAnyNominal() && T->getAnyNominal()->getKeyPathTypeKind() && |
| !T->hasUnresolvedType() && T->is<BoundGenericType>()) { |
| baseType = T->castTo<BoundGenericType>()->getGenericArgs()[0]; |
| break; |
| } |
| |
| // KeyPath can be used as a function that receives its root type. |
| if (T->is<AnyFunctionType>()) { |
| auto *fnType = T->castTo<AnyFunctionType>(); |
| if (fnType->getNumParams() == 1) { |
| const AnyFunctionType::Param ¶m = fnType->getParams()[0]; |
| baseType = param.getParameterType(); |
| break; |
| } |
| } |
| } |
| } |
| if (!OnRoot && KPE->getComponents().back().getKind() == |
| KeyPathExpr::Component::Kind::OptionalWrap) { |
| // KeyPath expr with '?' (e.g. '\Ty.[0].prop?.another'). |
| // Although expected type is optional, we should unwrap it because it's |
| // unwrapped. |
| baseType = baseType->getOptionalObjectType(); |
| } |
| |
| Lookup.getValueExprCompletions(baseType); |
| break; |
| } |
| |
| case CompletionKind::StmtOrExpr: |
| case CompletionKind::ForEachSequence: |
| case CompletionKind::PostfixExprBeginning: { |
| ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr); |
| Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(), |
| ContextInfo.isImplicitSingleExpressionReturn()); |
| DoPostfixExprBeginning(); |
| break; |
| } |
| |
| case CompletionKind::PostfixExpr: { |
| Lookup.setHaveLeadingSpace(HasSpace); |
| if (isDynamicLookup(*ExprType)) |
| Lookup.setIsDynamicLookup(); |
| Lookup.getValueExprCompletions(*ExprType, ReferencedDecl.getDecl()); |
| Lookup.getOperatorCompletions(ParsedExpr, leadingSequenceExprs); |
| Lookup.getPostfixKeywordCompletions(*ExprType, ParsedExpr); |
| break; |
| } |
| |
| case CompletionKind::PostfixExprParen: { |
| Lookup.setHaveLParen(true); |
| |
| ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr); |
| |
| if (ShouldCompleteCallPatternAfterParen) { |
| ExprContextInfo ParentContextInfo(CurDeclContext, ParsedExpr); |
| Lookup.setExpectedTypes( |
| ParentContextInfo.getPossibleTypes(), |
| ParentContextInfo.isImplicitSingleExpressionReturn()); |
| if (!ContextInfo.getPossibleCallees().empty()) { |
| for (auto &typeAndDecl : ContextInfo.getPossibleCallees()) |
| Lookup.tryFunctionCallCompletions(typeAndDecl.Type, typeAndDecl.Decl, |
| typeAndDecl.SemanticContext); |
| } else if (ExprType && ((*ExprType)->is<AnyFunctionType>() || |
| (*ExprType)->is<AnyMetatypeType>())) { |
| Lookup.getValueExprCompletions(*ExprType, ReferencedDecl.getDecl()); |
| } |
| } else { |
| // Add argument labels, then fallthrough to get values. |
| Lookup.addCallArgumentCompletionResults(ContextInfo.getPossibleParams()); |
| } |
| |
| if (!Lookup.FoundFunctionCalls || |
| (Lookup.FoundFunctionCalls && |
| Lookup.FoundFunctionsWithoutFirstKeyword)) { |
| Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(), |
| ContextInfo.isImplicitSingleExpressionReturn()); |
| Lookup.setHaveLParen(false); |
| DoPostfixExprBeginning(); |
| } |
| break; |
| } |
| |
| case CompletionKind::KeyPathExprObjC: { |
| if (DotLoc.isValid()) |
| Lookup.setHaveDot(DotLoc); |
| Lookup.setIsKeyPathExpr(); |
| Lookup.includeInstanceMembers(); |
| |
| if (ExprType) { |
| if (isDynamicLookup(*ExprType)) |
| Lookup.setIsDynamicLookup(); |
| |
| Lookup.getValueExprCompletions(*ExprType, ReferencedDecl.getDecl()); |
| } else { |
| SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc(); |
| Lookup.getValueCompletionsInDeclContext(Loc, KeyPathFilter, |
| /*LiteralCompletions=*/false); |
| } |
| break; |
| } |
| |
| case CompletionKind::TypeDeclResultBeginning: |
| case CompletionKind::TypeSimpleBeginning: { |
| auto Loc = Context.SourceMgr.getCodeCompletionLoc(); |
| Lookup.getTypeCompletionsInDeclContext(Loc); |
| Lookup.getSelfTypeCompletionInDeclContext( |
| Loc, Kind == CompletionKind::TypeDeclResultBeginning); |
| break; |
| } |
| |
| case CompletionKind::TypeIdentifierWithDot: { |
| Lookup.setHaveDot(SourceLoc()); |
| Lookup.getTypeCompletions(ParsedTypeLoc.getType()); |
| break; |
| } |
| |
| case CompletionKind::TypeIdentifierWithoutDot: { |
| Lookup.getTypeCompletions(ParsedTypeLoc.getType()); |
| break; |
| } |
| |
| case CompletionKind::CaseStmtBeginning: { |
| ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr); |
| Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(), |
| ContextInfo.isImplicitSingleExpressionReturn()); |
| Lookup.setIdealExpectedType(CodeCompleteTokenExpr->getType()); |
| Lookup.getUnresolvedMemberCompletions(ContextInfo.getPossibleTypes()); |
| DoPostfixExprBeginning(); |
| break; |
| } |
| |
| case CompletionKind::NominalMemberBeginning: { |
| CompletionOverrideLookup OverrideLookup(CompletionContext.getResultSink(), |
| P.Context, CurDeclContext, |
| ParsedKeywords, introducerLoc); |
| OverrideLookup.getOverrideCompletions(SourceLoc()); |
| break; |
| } |
| |
| case CompletionKind::AccessorBeginning: { |
| if (isa<AccessorDecl>(ParsedDecl)) { |
| ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr); |
| Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(), |
| ContextInfo.isImplicitSingleExpressionReturn()); |
| DoPostfixExprBeginning(); |
| } |
| break; |
| } |
| |
| case CompletionKind::AttributeBegin: { |
| Lookup.getAttributeDeclCompletions(IsInSil, AttTargetDK); |
| |
| // TypeName at attribute position after '@'. |
| // - VarDecl: Property Wrappers. |
| // - ParamDecl/VarDecl/FuncDecl: Function Builders. |
| if (!AttTargetDK || *AttTargetDK == DeclKind::Var || |
| *AttTargetDK == DeclKind::Param || *AttTargetDK == DeclKind::Func) |
| Lookup.getTypeCompletionsInDeclContext( |
| P.Context.SourceMgr.getCodeCompletionLoc()); |
| break; |
| } |
| case CompletionKind::AttributeDeclParen: { |
| Lookup.getAttributeDeclParamCompletions(AttrKind, AttrParamIndex); |
| break; |
| } |
| case CompletionKind::PoundAvailablePlatform: { |
| Lookup.getPoundAvailablePlatformCompletions(); |
| break; |
| } |
| case CompletionKind::Import: { |
| if (DotLoc.isValid()) |
| Lookup.addSubModuleNames(SubModuleNameVisibilityPairs); |
| else |
| Lookup.addImportModuleNames(); |
| break; |
| } |
| case CompletionKind::CallArg: { |
| ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr); |
| |
| bool shouldPerformGlobalCompletion = true; |
| |
| if (ShouldCompleteCallPatternAfterParen && |
| !ContextInfo.getPossibleCallees().empty()) { |
| Lookup.setHaveLParen(true); |
| for (auto &typeAndDecl : ContextInfo.getPossibleCallees()) { |
| auto apply = ContextInfo.getAnalyzedExpr(); |
| if (apply && isa<SubscriptExpr>(apply)) { |
| Lookup.addSubscriptCallPattern( |
| typeAndDecl.Type, |
| dyn_cast_or_null<SubscriptDecl>(typeAndDecl.Decl), |
| typeAndDecl.SemanticContext); |
| } else { |
| Lookup.addFunctionCallPattern( |
| typeAndDecl.Type, |
| dyn_cast_or_null<AbstractFunctionDecl>(typeAndDecl.Decl), |
| typeAndDecl.SemanticContext); |
| } |
| } |
| Lookup.setHaveLParen(false); |
| |
| shouldPerformGlobalCompletion = |
| !Lookup.FoundFunctionCalls || |
| (Lookup.FoundFunctionCalls && |
| Lookup.FoundFunctionsWithoutFirstKeyword); |
| } else if (!ContextInfo.getPossibleParams().empty()) { |
| auto params = ContextInfo.getPossibleParams(); |
| Lookup.addCallArgumentCompletionResults(params); |
| |
| shouldPerformGlobalCompletion = !ContextInfo.getPossibleTypes().empty(); |
| // Fallback to global completion if the position is out of number. It's |
| // better than suggest nothing. |
| shouldPerformGlobalCompletion |= llvm::all_of( |
| params, [](const PossibleParamInfo &P) { return !P.Param; }); |
| } |
| |
| if (shouldPerformGlobalCompletion) { |
| Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(), |
| ContextInfo.isImplicitSingleExpressionReturn()); |
| DoPostfixExprBeginning(); |
| } |
| break; |
| } |
| |
| case CompletionKind::LabeledTrailingClosure: { |
| ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr); |
| |
| SmallVector<PossibleParamInfo, 2> params; |
| // Only complete function type parameters |
| llvm::copy_if(ContextInfo.getPossibleParams(), std::back_inserter(params), |
| [](const PossibleParamInfo &P) { |
| // nullptr indicates out of bounds. |
| if (!P.Param) |
| return true; |
| return P.Param->getPlainType() |
| ->lookThroughAllOptionalTypes() |
| ->is<AnyFunctionType>(); |
| }); |
| |
| bool allRequired = false; |
| if (!params.empty()) { |
| Lookup.addCallArgumentCompletionResults( |
| params, /*isLabeledTrailingClosure=*/true); |
| allRequired = llvm::all_of( |
| params, [](const PossibleParamInfo &P) { return P.IsRequired; }); |
| } |
| |
| // If there're optional parameters, do global completion or member |
| // completion depending on the completion is happening at the start of line. |
| if (!allRequired) { |
| if (IsAtStartOfLine) { |
| // foo() {} |
| // <HERE> |
| |
| auto &Sink = CompletionContext.getResultSink(); |
| if (isa<Initializer>(CurDeclContext)) |
| CurDeclContext = CurDeclContext->getParent(); |
| |
| if (CurDeclContext->isTypeContext()) { |
| // Override completion (CompletionKind::NominalMemberBeginning). |
| addDeclKeywords(Sink, Context.LangOpts.EnableExperimentalConcurrency); |
| addLetVarKeywords(Sink); |
| SmallVector<StringRef, 0> ParsedKeywords; |
| CompletionOverrideLookup OverrideLookup(Sink, Context, CurDeclContext, |
| ParsedKeywords, SourceLoc()); |
| OverrideLookup.getOverrideCompletions(SourceLoc()); |
| } else { |
| // Global completion (CompletionKind::PostfixExprBeginning). |
| addDeclKeywords(Sink, Context.LangOpts.EnableExperimentalConcurrency); |
| addStmtKeywords(Sink, MaybeFuncBody); |
| addSuperKeyword(Sink); |
| addLetVarKeywords(Sink); |
| addExprKeywords(Sink, Context.LangOpts.EnableExperimentalConcurrency); |
| addAnyTypeKeyword(Sink, Context.TheAnyType); |
| DoPostfixExprBeginning(); |
| } |
| } else { |
| // foo() {} <HERE> |
| // Member completion. |
| Expr *analyzedExpr = ContextInfo.getAnalyzedExpr(); |
| if (!analyzedExpr) |
| break; |
| |
| // Only if the completion token is the last token in the call. |
| if (analyzedExpr->getEndLoc() != CodeCompleteTokenExpr->getLoc()) |
| break; |
| |
| Type resultTy = analyzedExpr->getType(); |
| // If the call expression doesn't have a type, fallback to: |
| if (!resultTy || resultTy->is<ErrorType>()) { |
| // 1) Try to type check removing CodeCompletionExpr from the call. |
| Expr *removedExpr = analyzedExpr; |
| removeCodeCompletionExpr(CurDeclContext->getASTContext(), |
| removedExpr); |
| ConcreteDeclRef referencedDecl; |
| auto optT = getTypeOfCompletionContextExpr( |
| CurDeclContext->getASTContext(), CurDeclContext, |
| CompletionTypeCheckKind::Normal, removedExpr, referencedDecl); |
| if (optT) { |
| resultTy = *optT; |
| analyzedExpr->setType(resultTy); |
| } |
| } |
| if (!resultTy || resultTy->is<ErrorType>()) { |
| // 2) Infer it from the possible callee info. |
| if (!ContextInfo.getPossibleCallees().empty()) { |
| auto calleeInfo = ContextInfo.getPossibleCallees()[0]; |
| resultTy = calleeInfo.Type->getResult(); |
| analyzedExpr->setType(resultTy); |
| } |
| } |
| if (!resultTy || resultTy->is<ErrorType>()) { |
| // 3) Give up providing postfix completions. |
| break; |
| } |
| |
| auto &SM = CurDeclContext->getASTContext().SourceMgr; |
| auto leadingChar = |
| SM.extractText({SM.getCodeCompletionLoc().getAdvancedLoc(-1), 1}); |
| Lookup.setHaveLeadingSpace(leadingChar.find_first_of(" \t\f\v") != |
| StringRef::npos); |
| |
| if (isDynamicLookup(resultTy)) |
| Lookup.setIsDynamicLookup(); |
| Lookup.getValueExprCompletions(resultTy, /*VD=*/nullptr); |
| Lookup.getOperatorCompletions(analyzedExpr, leadingSequenceExprs); |
| Lookup.getPostfixKeywordCompletions(resultTy, analyzedExpr); |
| } |
| } |
| break; |
| } |
| |
| case CompletionKind::ReturnStmtExpr : { |
| SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc(); |
| SmallVector<Type, 2> possibleReturnTypes; |
| collectPossibleReturnTypesFromContext(CurDeclContext, possibleReturnTypes); |
| Lookup.setExpectedTypes(possibleReturnTypes, |
| /*isImplicitSingleExpressionReturn*/ false); |
| Lookup.getValueCompletionsInDeclContext(Loc); |
| break; |
| } |
| |
| case CompletionKind::YieldStmtExpr: { |
| SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc(); |
| if (auto FD = dyn_cast<AccessorDecl>(CurDeclContext)) { |
| if (FD->isCoroutine()) { |
| // TODO: handle multi-value yields. |
| Lookup.setExpectedTypes(FD->getStorage()->getValueInterfaceType(), |
| /*isImplicitSingleExpressionReturn*/ false); |
| } |
| } |
| Lookup.getValueCompletionsInDeclContext(Loc); |
| break; |
| } |
| |
| case CompletionKind::AfterPoundExpr: { |
| ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr); |
| Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(), |
| ContextInfo.isImplicitSingleExpressionReturn()); |
| |
| Lookup.addPoundAvailable(ParentStmtKind); |
| Lookup.addPoundLiteralCompletions(/*needPound=*/false); |
| Lookup.addObjCPoundKeywordCompletions(/*needPound=*/false); |
| break; |
| } |
| |
| case CompletionKind::AfterPoundDirective: { |
| addPoundDirectives(CompletionContext.getResultSink()); |
| // FIXME: Add pound expressions (e.g. '#selector()') if it's at statements |
| // position. |
| break; |
| } |
| |
| case CompletionKind::PlatformConditon: { |
| addPlatformConditions(CompletionContext.getResultSink()); |
| addConditionalCompilationFlags(CurDeclContext->getASTContext(), |
| CompletionContext.getResultSink()); |
| break; |
| } |
| |
| case CompletionKind::GenericRequirement: |
| Lookup.getGenericRequirementCompletions(CurDeclContext); |
| break; |
| case CompletionKind::PrecedenceGroup: |
| Lookup.getPrecedenceGroupCompletions(SyntxKind); |
| break; |
| case CompletionKind::StmtLabel: { |
| SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc(); |
| Lookup.getStmtLabelCompletions(Loc, ParentStmtKind == StmtKind::Continue); |
| break; |
| } |
| case CompletionKind::AfterIfStmtElse: |
| case CompletionKind::CaseStmtKeyword: |
| case CompletionKind::EffectsSpecifier: |
| // Handled earlier by keyword completions. |
| break; |
| } |
| |
| deliverCompletionResults(CompletionContext, Lookup, P.SF, Consumer); |
| } |
| |
| void PrintingCodeCompletionConsumer::handleResults( |
| MutableArrayRef<CodeCompletionResult *> Results) { |
| unsigned NumResults = 0; |
| for (auto Result : Results) { |
| if (!IncludeKeywords && Result->getKind() == CodeCompletionResult::Keyword) |
| continue; |
| ++NumResults; |
| } |
| if (NumResults == 0) |
| return; |
| |
| OS << "Begin completions, " << NumResults << " items\n"; |
| for (auto Result : Results) { |
| if (!IncludeKeywords && Result->getKind() == CodeCompletionResult::Keyword) |
| continue; |
| Result->printPrefix(OS); |
| if (PrintAnnotatedDescription) { |
| printCodeCompletionResultDescriptionAnnotated(*Result, OS, /*leadingPunctuation=*/false); |
| OS << "; typename="; |
| printCodeCompletionResultTypeNameAnnotated(*Result, OS); |
| } else { |
| Result->getCompletionString()->print(OS); |
| } |
| |
| llvm::SmallString<64> Name; |
| llvm::raw_svector_ostream NameOs(Name); |
| Result->getCompletionString()->getName(NameOs); |
| OS << "; name=" << Name; |
| |
| StringRef comment = Result->getBriefDocComment(); |
| if (IncludeComments && !comment.empty()) { |
| OS << "; comment=" << comment; |
| } |
| |
| OS << "\n"; |
| } |
| OS << "End completions\n"; |
| } |
| |
| namespace { |
| class CodeCompletionCallbacksFactoryImpl |
| : public CodeCompletionCallbacksFactory { |
| CodeCompletionContext &CompletionContext; |
| CodeCompletionConsumer &Consumer; |
| |
| public: |
| CodeCompletionCallbacksFactoryImpl(CodeCompletionContext &CompletionContext, |
| CodeCompletionConsumer &Consumer) |
| : CompletionContext(CompletionContext), Consumer(Consumer) {} |
| |
| CodeCompletionCallbacks *createCodeCompletionCallbacks(Parser &P) override { |
| return new CodeCompletionCallbacksImpl(P, CompletionContext, Consumer); |
| } |
| }; |
| } // end anonymous namespace |
| |
| CodeCompletionCallbacksFactory * |
| swift::ide::makeCodeCompletionCallbacksFactory( |
| CodeCompletionContext &CompletionContext, |
| CodeCompletionConsumer &Consumer) { |
| return new CodeCompletionCallbacksFactoryImpl(CompletionContext, Consumer); |
| } |
| |
| void swift::ide::lookupCodeCompletionResultsFromModule( |
| CodeCompletionResultSink &targetSink, const ModuleDecl *module, |
| ArrayRef<std::string> accessPath, bool needLeadingDot, |
| const DeclContext *currDeclContext) { |
| CompletionLookup Lookup(targetSink, module->getASTContext(), currDeclContext); |
| Lookup.lookupExternalModuleDecls(module, accessPath, needLeadingDot); |
| } |
| |
| void swift::ide::copyCodeCompletionResults(CodeCompletionResultSink &targetSink, |
| CodeCompletionResultSink &sourceSink, |
| bool onlyTypes, |
| bool onlyPrecedenceGroups) { |
| |
| // We will be adding foreign results (from another sink) into TargetSink. |
| // TargetSink should have an owning pointer to the allocator that keeps the |
| // results alive. |
| targetSink.ForeignAllocators.push_back(sourceSink.Allocator); |
| |
| if (onlyTypes) { |
| std::copy_if(sourceSink.Results.begin(), sourceSink.Results.end(), |
| std::back_inserter(targetSink.Results), |
| [](CodeCompletionResult *R) -> bool { |
| if (R->getKind() != CodeCompletionResult::Declaration) |
| return false; |
| switch(R->getAssociatedDeclKind()) { |
| case CodeCompletionDeclKind::Module: |
| case CodeCompletionDeclKind::Class: |
| case CodeCompletionDeclKind::Struct: |
| case CodeCompletionDeclKind::Enum: |
| case CodeCompletionDeclKind::Protocol: |
| case CodeCompletionDeclKind::TypeAlias: |
| case CodeCompletionDeclKind::AssociatedType: |
| case CodeCompletionDeclKind::GenericTypeParam: |
| return true; |
| case CodeCompletionDeclKind::PrecedenceGroup: |
| case CodeCompletionDeclKind::EnumElement: |
| case CodeCompletionDeclKind::Constructor: |
| case CodeCompletionDeclKind::Destructor: |
| case CodeCompletionDeclKind::Subscript: |
| case CodeCompletionDeclKind::StaticMethod: |
| case CodeCompletionDeclKind::InstanceMethod: |
| case CodeCompletionDeclKind::PrefixOperatorFunction: |
| case CodeCompletionDeclKind::PostfixOperatorFunction: |
| case CodeCompletionDeclKind::InfixOperatorFunction: |
| case CodeCompletionDeclKind::FreeFunction: |
| case CodeCompletionDeclKind::StaticVar: |
| case CodeCompletionDeclKind::InstanceVar: |
| case CodeCompletionDeclKind::LocalVar: |
| case CodeCompletionDeclKind::GlobalVar: |
| return false; |
| } |
| |
| llvm_unreachable("Unhandled CodeCompletionDeclKind in switch."); |
| }); |
| } else if (onlyPrecedenceGroups) { |
| std::copy_if(sourceSink.Results.begin(), sourceSink.Results.end(), |
| std::back_inserter(targetSink.Results), |
| [](CodeCompletionResult *R) -> bool { |
| return R->getAssociatedDeclKind() == |
| CodeCompletionDeclKind::PrecedenceGroup; |
| }); |
| } else { |
| targetSink.Results.insert(targetSink.Results.end(), |
| sourceSink.Results.begin(), |
| sourceSink.Results.end()); |
| } |
| } |
| |
| void SimpleCachingCodeCompletionConsumer::handleResultsAndModules( |
| CodeCompletionContext &context, |
| ArrayRef<RequestedCachedModule> requestedModules, |
| DeclContext *DCForModules) { |
| for (auto &R : requestedModules) { |
| // FIXME(thread-safety): lock the whole AST context. We might load a |
| // module. |
| llvm::Optional<CodeCompletionCache::ValueRefCntPtr> V = |
| context.Cache.get(R.Key); |
| if (!V.hasValue()) { |
| // No cached results found. Fill the cache. |
| V = context.Cache.createValue(); |
| (*V)->Sink.annotateResult = context.getAnnotateResult(); |
| lookupCodeCompletionResultsFromModule( |
| (*V)->Sink, R.TheModule, R.Key.AccessPath, |
| R.Key.ResultsHaveLeadingDot, DCForModules); |
| context.Cache.set(R.Key, *V); |
| } |
| assert(V.hasValue()); |
| copyCodeCompletionResults(context.getResultSink(), (*V)->Sink, |
| R.OnlyTypes, R.OnlyPrecedenceGroups); |
| } |
| |
| handleResults(context.takeResults()); |
| } |