| //===--- 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/Utils.h" |
| #include "swift/Parse/CodeCompletionCallbacks.h" |
| #include "swift/Sema/IDETypeChecking.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; |
| } |
| |
| 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; |
| for (auto C : getChunks()) { |
| bool AnnotatedTextChunk = false; |
| if (C.getNestingLevel() < PrevNestingLevel) { |
| OS << "#}"; |
| } |
| switch (C.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: |
| AnnotatedTextChunk = C.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 (C.getKind() == ChunkKind::CallParameterInternalName) |
| OS << "("; |
| else if (C.getKind() == ChunkKind::CallParameterClosureType) |
| OS << "##"; |
| for (char Ch : C.getText()) { |
| if (Ch == '\n') |
| OS << "\\n"; |
| else |
| OS << Ch; |
| } |
| if (AnnotatedTextChunk) |
| OS << "']"; |
| else if (C.getKind() == ChunkKind::CallParameterInternalName) |
| OS << ")"; |
| break; |
| case ChunkKind::OptionalBegin: |
| case ChunkKind::CallParameterBegin: |
| case ChunkKind::GenericParameterBegin: |
| OS << "{#"; |
| break; |
| case ChunkKind::DynamicLookupMethodCallTail: |
| case ChunkKind::OptionalMethodCallTail: |
| OS << C.getText(); |
| break; |
| case ChunkKind::TypeAnnotation: |
| OS << "[#"; |
| OS << C.getText(); |
| OS << "#]"; |
| break; |
| case ChunkKind::BraceStmtWithCursor: |
| OS << " {|}"; |
| break; |
| } |
| PrevNestingLevel = C.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"); |
| } |
| |
| void CodeCompletionResult::print(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 (NumBytesToErase != 0) { |
| Prefix.append("/Erase["); |
| Prefix.append(Twine(NumBytesToErase).str()); |
| Prefix.append("]"); |
| } |
| switch (TypeDistance) { |
| 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::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; |
| CompletionString->print(OS); |
| } |
| |
| void CodeCompletionResult::dump() const { |
| print(llvm::errs()); |
| } |
| |
| static StringRef copyString(llvm::BumpPtrAllocator &Allocator, |
| StringRef Str) { |
| char *Mem = Allocator.Allocate<char>(Str.size()); |
| std::copy(Str.begin(), Str.end(), Mem); |
| return StringRef(Mem, Str.size()); |
| } |
| |
| static ArrayRef<StringRef> copyStringArray(llvm::BumpPtrAllocator &Allocator, |
| ArrayRef<StringRef> Arr) { |
| StringRef *Buff = Allocator.Allocate<StringRef>(Arr.size()); |
| std::copy(Arr.begin(), Arr.end(), Buff); |
| return llvm::makeArrayRef(Buff, Arr.size()); |
| } |
| |
| static ArrayRef<std::pair<StringRef, StringRef>> copyStringPairArray( |
| llvm::BumpPtrAllocator &Allocator, |
| ArrayRef<std::pair<StringRef, StringRef>> Arr) { |
| std::pair<StringRef, StringRef> *Buff = Allocator.Allocate<std::pair<StringRef, |
| StringRef>>(Arr.size()); |
| std::copy(Arr.begin(), Arr.end(), Buff); |
| return llvm::makeArrayRef(Buff, Arr.size()); |
| } |
| |
| 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) |
| CurrentModule = D->getModuleContext(); |
| |
| if (D->getAttrs().getDeprecated(D->getASTContext())) |
| setNotRecommended(CodeCompletionResult::Deprecated); |
| } |
| |
| 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 copyStringArray(Allocator, USRs); |
| |
| return ArrayRef<StringRef>(); |
| } |
| |
| static CodeCompletionResult::ExpectedTypeRelation calculateTypeRelation( |
| Type Ty, |
| Type ExpectedTy, |
| 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, *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 |
| calculateTypeRelationForDecl(const Decl *D, Type ExpectedType, |
| bool IsImplicitlyCurriedInstanceMethod, |
| bool UseFuncResultType = true) { |
| auto VD = dyn_cast<ValueDecl>(D); |
| auto DC = D->getDeclContext(); |
| if (!VD) |
| return CodeCompletionResult::ExpectedTypeRelation::Unrelated; |
| |
| if (auto FD = dyn_cast<AbstractFunctionDecl>(VD)) { |
| auto funcType = FD->getInterfaceType()->getAs<AnyFunctionType>(); |
| if (DC->isTypeContext() && funcType && funcType->is<AnyFunctionType>() && |
| !IsImplicitlyCurriedInstanceMethod) |
| funcType = funcType->getResult()->getAs<AnyFunctionType>(); |
| if (funcType) { |
| funcType = funcType->removeArgumentLabels(1)->castTo<AnyFunctionType>(); |
| auto relation = calculateTypeRelation(funcType, ExpectedType, DC); |
| if (UseFuncResultType) |
| relation = |
| std::max(relation, calculateTypeRelation(funcType->getResult(), |
| ExpectedType, DC)); |
| return relation; |
| } |
| } |
| if (auto NTD = dyn_cast<NominalTypeDecl>(VD)) { |
| return std::max( |
| calculateTypeRelation(NTD->getInterfaceType(), ExpectedType, DC), |
| calculateTypeRelation(NTD->getDeclaredInterfaceType(), ExpectedType, DC)); |
| } |
| return calculateTypeRelation(VD->getInterfaceType(), ExpectedType, DC); |
| } |
| |
| static CodeCompletionResult::ExpectedTypeRelation |
| calculateMaxTypeRelationForDecl( |
| const Decl *D, const ExpectedTypeContext &typeContext, |
| bool IsImplicitlyCurriedInstanceMethod = false) { |
| auto Result = CodeCompletionResult::ExpectedTypeRelation::Unrelated; |
| for (auto Type : 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.isSingleExpressionBody && Type->isVoid()) |
| continue; |
| |
| Result = std::max(Result, calculateTypeRelationForDecl( |
| D, Type, IsImplicitlyCurriedInstanceMethod)); |
| |
| // Map invalid -> unrelated when in a single-expression body, since the |
| // input may be incomplete. |
| if (typeContext.isSingleExpressionBody && |
| 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; |
| } |
| } |
| |
| auto typeRelation = ExpectedTypeRelation; |
| if (typeRelation == CodeCompletionResult::Unrelated) |
| typeRelation = |
| calculateMaxTypeRelationForDecl(AssociatedDecl, declTypeContext); |
| |
| if (typeRelation == CodeCompletionResult::Invalid) { |
| IsNotRecommended = true; |
| NotRecReason = CodeCompletionResult::NotRecommendedReason::TypeMismatch; |
| } |
| |
| return new (*Sink.Allocator) CodeCompletionResult( |
| SemanticContext, NumBytesToErase, CCS, AssociatedDecl, ModuleName, |
| /*NotRecommended=*/IsNotRecommended, NotRecReason, |
| copyString(*Sink.Allocator, BriefComment), |
| copyAssociatedUSRs(*Sink.Allocator, AssociatedDecl), |
| copyStringPairArray(*Sink.Allocator, CommentWords), typeRelation); |
| } |
| |
| case CodeCompletionResult::ResultKind::Keyword: |
| return new (*Sink.Allocator) |
| CodeCompletionResult(KeywordKind, SemanticContext, NumBytesToErase, |
| CCS, ExpectedTypeRelation); |
| |
| case CodeCompletionResult::ResultKind::BuiltinOperator: |
| case CodeCompletionResult::ResultKind::Pattern: |
| return new (*Sink.Allocator) CodeCompletionResult( |
| Kind, SemanticContext, NumBytesToErase, CCS, ExpectedTypeRelation); |
| |
| 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: |
| 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::DeclAttrParamColon: |
| case ChunkKind::CallParameterType: |
| case ChunkKind::CallParameterClosureType: |
| case ChunkKind::OptionalBegin: |
| case ChunkKind::CallParameterBegin: |
| case ChunkKind::GenericParameterBegin: |
| case ChunkKind::DynamicLookupMethodCallTail: |
| case ChunkKind::OptionalMethodCallTail: |
| case ChunkKind::TypeAnnotation: |
| 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()) { |
| for (auto C : getChunks().slice(*FirstTextChunk)) { |
| using ChunkKind = Chunk::ChunkKind; |
| |
| bool shouldPrint = !C.isAnnotation(); |
| switch (C.getKind()) { |
| case ChunkKind::TypeAnnotation: |
| case ChunkKind::CallParameterClosureType: |
| case ChunkKind::DeclAttrParamColon: |
| case ChunkKind::OptionalMethodCallTail: |
| continue; |
| case ChunkKind::ThrowsKeyword: |
| case ChunkKind::RethrowsKeyword: |
| shouldPrint = true; // Even when they're annotations. |
| break; |
| default: |
| break; |
| } |
| |
| if (C.hasText() && shouldPrint) { |
| TextSize += C.getText().size(); |
| OS << C.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; |
| std::vector<RequestedCachedModule> RequestedModules; |
| CodeCompletionConsumer &Consumer; |
| CodeCompletionExpr *CodeCompleteTokenExpr = nullptr; |
| CompletionKind Kind = CompletionKind::None; |
| Expr *ParsedExpr = nullptr; |
| SourceLoc DotLoc; |
| 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; |
| 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.addTextChunk("super"); |
| Builder.addTypeAnnotation(ST.getString()); |
| } |
| |
| /// Set to true when we have delivered code completion results |
| /// to the \c Consumer. |
| bool DeliveredResults = false; |
| |
| |
| 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>()) { |
| auto refDecl = ParsedExpr->getReferencedDecl(); |
| auto exprTy = ParsedExpr->getType(); |
| if (!refDecl) { |
| // FIXME: do this in the not-already-type-checked branch too? |
| if (auto *apply = dyn_cast<SelfApplyExpr>(ParsedExpr)) { |
| refDecl = apply->getFn()->getReferencedDecl(); |
| } else if (auto *apply = dyn_cast<ApplyExpr>(ParsedExpr)) { |
| // If this is an IUO, use the underlying non-optional type instead |
| auto fnDecl = apply->getFn()->getReferencedDecl(); |
| if (auto FD = fnDecl.getDecl()) { |
| if (FD->isImplicitlyUnwrappedOptional()) { |
| if (auto OT = exprTy->getOptionalObjectType()) |
| exprTy = OT; |
| } |
| } |
| } |
| } |
| return std::make_pair(exprTy, refDecl); |
| } |
| |
| 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"); |
| return !performTypeLocChecking(P.Context, ParsedTypeLoc, |
| CurDeclContext, 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 completeExpr() override; |
| void completeDotExpr(Expr *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() override; |
| void completeTypeIdentifierWithoutDot() override; |
| |
| void completeCaseStmtKeyword() override; |
| void completeCaseStmtBeginning(CodeCompletionExpr *E) override; |
| void completeDeclAttrBeginning(bool Sil, bool isIndependent) override; |
| void completeDeclAttrParam(DeclAttrKind DK, int Index) override; |
| void completeInPrecedenceGroup(SyntaxKind SK) override; |
| void completeNominalMemberBeginning( |
| SmallVectorImpl<StringRef> &Keywords, SourceLoc introducerLoc) override; |
| void completeAccessorBeginning(CodeCompletionExpr *E) override; |
| |
| void completePoundAvailablePlatform() override; |
| void completeImportDecl(std::vector<std::pair<Identifier, SourceLoc>> &Path) override; |
| void completeUnresolvedMember(CodeCompletionExpr *E, |
| SourceLoc DotLoc) override; |
| void completeCallArg(CodeCompletionExpr *E, bool isFirst) override; |
| 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 completeGenericParams(TypeLoc TL) override; |
| void completeAfterIfStmt(bool hasElse) override; |
| |
| void doneParsing() override; |
| |
| private: |
| void addKeywords(CodeCompletionResultSink &Sink, bool MaybeFuncBody); |
| void deliverCompletionResults(); |
| }; |
| } // end anonymous namespace |
| |
| void CodeCompletionCallbacksImpl::completeExpr() { |
| if (DeliveredResults) |
| return; |
| |
| Parser::ParserPositionRAII RestorePosition(P); |
| P.restoreParserPosition(ExprBeginPosition); |
| |
| // FIXME: implement fallback code completion. |
| |
| deliverCompletionResults(); |
| } |
| |
| namespace { |
| static bool isTopLevelContext(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."); |
| } |
| |
| /// 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 |
| }; |
| |
| 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 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; |
| DeclName Name = AFD->getFullName(); |
| 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)); |
| } |
| |
| bool shouldUseFunctionReference(AbstractFunctionDecl *D) { |
| if (PreferFunctionReferencesToCalls) |
| return true; |
| bool isImplicitlyCurriedIM = isImplicitlyCurriedInstanceMethod(D); |
| for (auto expectedType : expectedTypeContext.possibleTypes) { |
| if (expectedType && |
| expectedType->lookThroughAllOptionalTypes() |
| ->is<AnyFunctionType>() && |
| calculateTypeRelationForDecl(D, expectedType, isImplicitlyCurriedIM, |
| /*UseFuncResultType=*/false) >= |
| CodeCompletionResult::ExpectedTypeRelation::Convertible) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| public: |
| struct RequestedResultsTy { |
| const ModuleDecl *TheModule; |
| bool OnlyTypes; |
| bool OnlyPrecedenceGroups; |
| bool NeedLeadingDot; |
| |
| static RequestedResultsTy fromModule(const ModuleDecl *TheModule) { |
| return { TheModule, false, false, false }; |
| } |
| |
| RequestedResultsTy onlyTypes() const { |
| return { TheModule, true, false, NeedLeadingDot }; |
| } |
| |
| RequestedResultsTy onlyPrecedenceGroups() const { |
| assert(!OnlyTypes && "onlyTypes() already includes precedence groups"); |
| return { TheModule, false, true, false }; |
| } |
| |
| RequestedResultsTy needLeadingDot(bool NeedDot) const { |
| return { TheModule, OnlyTypes, OnlyPrecedenceGroups, NeedDot }; |
| } |
| |
| static RequestedResultsTy toplevelResults() { |
| return { nullptr, false, false, false }; |
| } |
| }; |
| |
| 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) { |
| (void)createTypeChecker(Ctx); |
| |
| // 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 isSingleExpressionBody) { |
| expectedTypeContext.isSingleExpressionBody = isSingleExpressionBody; |
| expectedTypeContext.possibleTypes.clear(); |
| expectedTypeContext.possibleTypes.reserve(Types.size()); |
| for (auto T : Types) |
| if (T) |
| expectedTypeContext.possibleTypes.push_back(T); |
| } |
| |
| CodeCompletionContext::TypeContextKind typeContextKind() const { |
| if (expectedTypeContext.empty()) { |
| return CodeCompletionContext::TypeContextKind::None; |
| } else if (expectedTypeContext.isSingleExpressionBody) { |
| return CodeCompletionContext::TypeContextKind::SingleExpressionBody; |
| } else { |
| return CodeCompletionContext::TypeContextKind::Required; |
| } |
| } |
| |
| bool needDot() const { |
| return NeedLeadingDot; |
| } |
| |
| void setHaveLParen(bool Value) { |
| HaveLParen = Value; |
| } |
| |
| void setIsSuperRefExpr() { |
| IsSuperRefExpr = true; |
| } |
| |
| void setIsSelfRefExpr(bool value) { IsSelfRefExpr = value; } |
| |
| void setIsKeyPathExpr() { |
| IsKeyPathExpr = true; |
| } |
| |
| 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::OtherModule, |
| expectedTypeContext); |
| auto MD = ModuleDecl::create(Ctx.getIdentifier(Pair.first), Ctx); |
| Builder.setAssociatedDecl(MD); |
| Builder.addTextChunk(MD->getNameStr()); |
| Builder.addTypeAnnotation("Module"); |
| if (Pair.second) |
| Builder.setNotRecommended(CodeCompletionResult::NotRecommendedReason:: |
| Redundant); |
| } |
| } |
| |
| void collectImportedModules(llvm::StringSet<> &ImportedModules) { |
| ModuleDecl::ImportFilter ImportFilter; |
| ImportFilter |= ModuleDecl::ImportFilterKind::Public; |
| ImportFilter |= ModuleDecl::ImportFilterKind::Private; |
| ImportFilter |= ModuleDecl::ImportFilterKind::ImplementationOnly; |
| |
| SmallVector<ModuleDecl::ImportedModule, 16> Imported; |
| SmallVector<ModuleDecl::ImportedModule, 16> FurtherImported; |
| CurrDeclContext->getParentSourceFile()->getImportedModules(Imported, |
| ImportFilter); |
| while (!Imported.empty()) { |
| ModuleDecl *MD = Imported.back().second; |
| Imported.pop_back(); |
| if (!ImportedModules.insert(MD->getNameStr()).second) |
| continue; |
| FurtherImported.clear(); |
| MD->getImportedModules(FurtherImported, |
| ModuleDecl::ImportFilterKind::Public); |
| Imported.append(FurtherImported.begin(), FurtherImported.end()); |
| for (auto SubMod : FurtherImported) { |
| Imported.push_back(SubMod); |
| } |
| } |
| } |
| |
| 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); |
| CodeCompletionResultBuilder Builder( |
| Sink, |
| CodeCompletionResult::ResultKind::Declaration, |
| SemanticContextKind::OtherModule, |
| expectedTypeContext); |
| Builder.setAssociatedDecl(MD); |
| Builder.addTextChunk(MD->getNameStr()); |
| Builder.addTypeAnnotation("Module"); |
| |
| // Imported modules are not recommended. |
| if (ImportedModules.count(MD->getNameStr()) != 0) |
| Builder.setNotRecommended( |
| CodeCompletionResult::NotRecommendedReason::Redundant); |
| } |
| } |
| |
| SemanticContextKind getSemanticContext(const Decl *D, |
| DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo) { |
| if (ForcedSemanticContext) |
| return *ForcedSemanticContext; |
| |
| if (IsUnresolvedMember) { |
| if (isa<EnumElementDecl>(D)) { |
| return SemanticContextKind::ExpressionSpecific; |
| } |
| } |
| |
| 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::MemberOfProtocolImplementedByCurrentNominal: |
| 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) && isTopLevelContext(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, |
| {}); |
| } |
| } |
| llvm_unreachable("unhandled kind"); |
| } |
| |
| 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.addTextChunk(NameStr); |
| } else { |
| SmallString<16> buffer; |
| Builder.addTextChunk(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) { |
| T = T->getReferenceStorageReferent(); |
| if (T->isVoid()) { |
| Builder.addTypeAnnotation("Void"); |
| } else { |
| PrintOptions PO; |
| PO.OpaqueReturnTypePrinting = |
| PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; |
| Builder.addTypeAnnotation(T.getString(PO)); |
| } |
| } |
| |
| void addTypeAnnotationForImplicitlyUnwrappedOptional( |
| CodeCompletionResultBuilder &Builder, Type T, |
| 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 = "?"; |
| } |
| |
| T = T->getReferenceStorageReferent(); |
| PrintOptions PO; |
| PO.PrintOptionalAsImplicitlyUnwrapped = true; |
| PO.OpaqueReturnTypePrinting = |
| PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; |
| Builder.addTypeAnnotation(T.getString(PO) + suffix); |
| } |
| |
| /// 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()) { |
| auto protos = genericSig->getConformsTo(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; |
| auto elementTy = SD->getElementTypeLoc().getType(); |
| 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) { |
| auto GenericSig = VD->getInnermostDeclContext() |
| ->getGenericSignatureOfContext(); |
| |
| 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 eraseArchetypes(T, GenericSig); |
| } |
| |
| 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 && Conformance->isConcrete()) { |
| return Conformance->getConcrete() |
| ->getTypeWitness(const_cast<AssociatedTypeDecl *>(ATD)); |
| } |
| } |
| } |
| return Type(); |
| } |
| |
| void addVarDeclRef(const VarDecl *VD, DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo) { |
| if (!VD->hasName() || |
| !VD->isAccessibleFrom(CurrDeclContext) || |
| VD->shouldHideFromEditor()) |
| return; |
| |
| 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); |
| |
| 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); |
| } |
| if (VD->isImplicitlyUnwrappedOptional()) |
| addTypeAnnotationForImplicitlyUnwrappedOptional(Builder, VarType, |
| DynamicOrOptional); |
| else |
| addTypeAnnotation(Builder, VarType); |
| } |
| |
| 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, |
| 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; |
| |
| case DefaultArgumentKind::File: |
| case DefaultArgumentKind::Line: |
| case DefaultArgumentKind::Column: |
| case DefaultArgumentKind::Function: |
| case DefaultArgumentKind::DSOHandle: |
| // 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(); |
| |
| Builder.addCallParameter(argName, bodyName, paramTy, isVariadic, isInOut, |
| isIUO, isAutoclosure); |
| |
| 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, |
| bool includeDefaultArgs = true) { |
| ArrayRef<const ParamDecl *> declParams; |
| if (Params) |
| declParams = Params->getArray(); |
| return addCallArgumentPatterns(Builder, AFT->getParams(), declParams, |
| includeDefaultArgs); |
| } |
| |
| static void addThrows(CodeCompletionResultBuilder &Builder, |
| const AnyFunctionType *AFT, |
| const AbstractFunctionDecl *AFD) { |
| if (AFD && AFD->getAttrs().hasAttribute<RethrowsAttr>()) |
| Builder.addAnnotatedRethrows(); |
| else if (AFT->throws()) |
| 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.addTextChunk("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) { |
| foundFunction(AFT); |
| auto genericSig = |
| SD->getInnermostDeclContext()->getGenericSignatureOfContext(); |
| AFT = eraseArchetypes(const_cast<AnyFunctionType *>(AFT), genericSig) |
| ->castTo<AnyFunctionType>(); |
| |
| CommandWordsPairs Pairs; |
| CodeCompletionResultBuilder Builder( |
| Sink, CodeCompletionResult::ResultKind::Declaration, |
| getSemanticContextKind(SD), expectedTypeContext); |
| Builder.setAssociatedDecl(SD); |
| setClangDeclKeywords(SD, Pairs, Builder); |
| if (!HaveLParen) |
| Builder.addLeftBracket(); |
| else |
| Builder.addAnnotatedLeftBracket(); |
| addCallArgumentPatterns(Builder, AFT, SD->getIndices()); |
| if (!HaveLParen) |
| Builder.addRightBracket(); |
| else |
| Builder.addAnnotatedRightBracket(); |
| if (SD && SD->isImplicitlyUnwrappedOptional()) |
| addTypeAnnotationForImplicitlyUnwrappedOptional(Builder, |
| AFT->getResult()); |
| else |
| addTypeAnnotation(Builder, AFT->getResult()); |
| } |
| |
| void addFunctionCallPattern(const AnyFunctionType *AFT, |
| const AbstractFunctionDecl *AFD = nullptr) { |
| if (AFD) { |
| auto genericSig = |
| AFD->getInnermostDeclContext()->getGenericSignatureOfContext(); |
| AFT = eraseArchetypes(const_cast<AnyFunctionType *>(AFT), genericSig) |
| ->castTo<AnyFunctionType>(); |
| } |
| |
| // 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, |
| getSemanticContextKind(AFD), expectedTypeContext); |
| if (AFD) { |
| Builder.setAssociatedDecl(AFD); |
| setClangDeclKeywords(AFD, Pairs, Builder); |
| } |
| |
| if (!HaveLParen) |
| Builder.addLeftParen(); |
| else |
| Builder.addAnnotatedLeftParen(); |
| |
| addCallArgumentPatterns(Builder, AFT->getParams(), declParams, |
| 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()); |
| else |
| addTypeAnnotation(Builder, AFT->getResult()); |
| }; |
| |
| if (!AFD || !AFD->getInterfaceType() || |
| !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) { |
| switch (Kind) { |
| case LookupKind::ValueExpr: |
| return ExprType->is<AnyMetatypeType>() && !FD->isStatic(); |
| case LookupKind::ValueInDeclContext: |
| if (InsideStaticMethod && |
| FD->getDeclContext() == CurrentMethod->getDeclContext() && |
| !FD->isStatic()) |
| return true; |
| if (auto Init = dyn_cast<Initializer>(CurrDeclContext)) |
| return FD->getDeclContext() == Init->getParent() && !FD->isStatic(); |
| return false; |
| case LookupKind::EnumElement: |
| case LookupKind::Type: |
| case LookupKind::TypeInDeclContext: |
| 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->getName().empty()) |
| return; |
| |
| // Suppress "sequenced" as a result, because it crashes completions. |
| // TODO(TF-315): Fix properly and then remove this. |
| if (FD->getName().str() == "sequenced") |
| return; |
| |
| foundFunction(FD); |
| |
| Identifier Name = FD->getName(); |
| 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>(); |
| } |
| |
| 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(); |
| |
| llvm::SmallString<32> TypeStr; |
| |
| if (!AFT) { |
| llvm::raw_svector_ostream OS(TypeStr); |
| FunctionType.print(OS); |
| Builder.addTypeAnnotation(OS.str()); |
| return; |
| } |
| |
| if (IsImplicitlyCurriedInstanceMethod) { |
| Builder.addLeftParen(); |
| addCallArgumentPatterns(Builder, AFT->getParams(), |
| {FD->getImplicitSelfDecl()}, |
| includeDefaultArgs); |
| Builder.addRightParen(); |
| } else if (trivialTrailingClosure) { |
| Builder.addBraceStmtWithCursor(" { code }"); |
| addThrows(Builder, AFT, FD); |
| } else { |
| Builder.addLeftParen(); |
| addCallArgumentPatterns(Builder, AFT, FD->getParameters(), |
| includeDefaultArgs); |
| Builder.addRightParen(); |
| addThrows(Builder, AFT, FD); |
| } |
| |
| Type ResultType = AFT->getResult(); |
| |
| // Build type annotation. |
| { |
| llvm::raw_svector_ostream OS(TypeStr); |
| if (IsImplicitlyCurriedInstanceMethod) { |
| auto *FnType = ResultType->castTo<AnyFunctionType>(); |
| AnyFunctionType::printParams(FnType->getParams(), OS); |
| ResultType = FnType->getResult(); |
| OS << " -> "; |
| } |
| |
| // What's left is the result type. |
| if (ResultType->isVoid()) { |
| OS << "Void"; |
| } else { |
| // 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; |
| ResultType.print(OS, PO); |
| } |
| } |
| Builder.addTypeAnnotation(TypeStr); |
| }; |
| |
| 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.addTextChunk("init"); |
| } else if (!addName.empty()) { |
| Builder.addTextChunk(addName.str()); |
| } |
| |
| if (!ConstructorType) { |
| addTypeAnnotation(Builder, MemberType); |
| return; |
| } |
| |
| if (!HaveLParen) |
| Builder.addLeftParen(); |
| else |
| Builder.addAnnotatedLeftParen(); |
| |
| addCallArgumentPatterns(Builder, ConstructorType, CD->getParameters(), |
| 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 (CD->isImplicitlyUnwrappedOptional()) { |
| addTypeAnnotationForImplicitlyUnwrappedOptional( |
| Builder, Result.hasValue() ? Result.getValue() |
| : ConstructorType->getResult()); |
| } else { |
| addTypeAnnotation(Builder, Result.hasValue() |
| ? Result.getValue() |
| : ConstructorType->getResult()); |
| } |
| }; |
| |
| 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); |
| SmallVector<ValueDecl *, 16> initializers; |
| if (CurrDeclContext->lookupQualified(type, DeclBaseName::createConstructor(), |
| NL_QualifiedDefault, |
| initializers)) { |
| for (auto *init : initializers) { |
| if (init->shouldHideFromEditor()) |
| continue; |
| if (IsUnresolvedMember && |
| cast<ConstructorDecl>(init)->isFailable() && |
| !cast<ConstructorDecl>(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(), 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); |
| } |
| |
| 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.addTextChunk(NTD->getName().str()); |
| addTypeAnnotation(Builder, NTD->getDeclaredType()); |
| } |
| |
| 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.addTextChunk(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.addTextChunk(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.addTextChunk(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.addTextChunk(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->getName()); |
| |
| // 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()); |
| Builder.addRightParen(); |
| |
| // Extract result as the enum type. |
| EnumType = EnumType->castTo<FunctionType>()->getResult(); |
| } |
| |
| addTypeAnnotation(Builder, EnumType); |
| } |
| |
| 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.addTextChunk(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.addTextChunk(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. |
| void addCompoundFunctionName(AbstractFunctionDecl *AFD, |
| DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo) { |
| 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. |
| auto ArgLabels = AFD->getFullName().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(); |
| } |
| } |
| |
| // Implement swift::VisibleDeclConsumer. |
| void foundDecl(ValueDecl *D, DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo) override { |
| if (D->shouldHideFromEditor()) |
| return; |
| |
| if (IsKeyPathExpr && !KeyPathFilter(D, Reason)) |
| return; |
| |
| if (IsSwiftKeyPathExpr && !SwiftKeyPathFilter(D, Reason)) |
| 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 (shouldUseFunctionReference(CD)) { |
| addCompoundFunctionName(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; |
| // We do not want 'init' completions for 'self' in non-convenience |
| // initializers and for 'super' in convenience initializers. |
| if ((IsSelfRefExpr && CDC->isConvenienceInit()) || |
| ((IsSuperRefExpr && !CDC->isConvenienceInit()))) |
| 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 (shouldUseFunctionReference(FD)) { |
| addCompoundFunctionName(FD, Reason, dynamicLookupInfo); |
| return; |
| } |
| |
| addMethodCall(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 *TAD = dyn_cast<TypeAliasDecl>(D)) { |
| addTypeAliasRef(TAD, Reason, dynamicLookupInfo); |
| return; |
| } |
| |
| if (auto *GP = dyn_cast<GenericTypeParamDecl>(D)) { |
| addGenericTypeParamRef(GP, 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.addTextChunk(TupleElt.getName().str()); |
| } else { |
| llvm::SmallString<4> IndexStr; |
| { |
| llvm::raw_svector_ostream OS(IndexStr); |
| OS << Index; |
| } |
| Builder.addTextChunk(IndexStr.str()); |
| } |
| addTypeAnnotation(Builder, TupleElt.getType()); |
| Index++; |
| } |
| return true; |
| } |
| |
| bool tryFunctionCallCompletions(Type ExprType, const ValueDecl *VD) { |
| ExprType = ExprType->getRValueType(); |
| if (auto AFT = ExprType->getAs<AnyFunctionType>()) { |
| if (auto *AFD = dyn_cast_or_null<AbstractFunctionDecl>(VD)) { |
| addFunctionCallPattern(AFT, AFD); |
| } else { |
| addFunctionCallPattern(AFT); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| bool tryModuleCompletions(Type ExprType) { |
| if (auto MT = ExprType->getAs<ModuleType>()) { |
| ModuleDecl *M = MT->getModule(); |
| if (CurrModule != M) { |
| // Only use the cache if it is not the current module. |
| RequestedCachedResults.push_back( |
| RequestedResultsTy::fromModule(M).needLeadingDot(needDot())); |
| 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); |
| 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()) { |
| NumBytesToEraseForOptionalUnwrap = Ctx.SourceMgr.getByteDistance( |
| DotLoc, Ctx.SourceMgr.getCodeCompletionLoc()); |
| } else { |
| NumBytesToEraseForOptionalUnwrap = 0; |
| } |
| if (NumBytesToEraseForOptionalUnwrap <= |
| CodeCompletionResult::MaxNumBytesToErase) { |
| if (!tryTupleExprCompletions(Unwrapped)) { |
| lookupVisibleMemberDecls(*this, Unwrapped, CurrDeclContext, |
| IncludeInstanceMembers); |
| } |
| } |
| 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); |
| } |
| |
| template <typename T> |
| void collectOperatorsFromMap(SourceFile::OperatorMap<T> &map, |
| bool includePrivate, |
| std::vector<OperatorDecl *> &results) { |
| for (auto &pair : map) { |
| if (pair.second.getPointer() && |
| (pair.second.getInt() || includePrivate)) { |
| results.push_back(pair.second.getPointer()); |
| } |
| } |
| } |
| |
| void collectOperatorsFrom(SourceFile *SF, |
| std::vector<OperatorDecl *> &results) { |
| bool includePrivate = CurrDeclContext->getParentSourceFile() == SF; |
| collectOperatorsFromMap(SF->PrefixOperators, includePrivate, results); |
| collectOperatorsFromMap(SF->PostfixOperators, includePrivate, results); |
| collectOperatorsFromMap(SF->InfixOperators, includePrivate, results); |
| } |
| |
| void collectOperatorsFrom(LoadedFile *F, |
| std::vector<OperatorDecl *> &results) { |
| SmallVector<Decl *, 64> topLevelDecls; |
| F->getTopLevelDecls(topLevelDecls); |
| for (auto D : topLevelDecls) { |
| if (auto op = dyn_cast<OperatorDecl>(D)) |
| results.push_back(op); |
| } |
| } |
| |
| std::vector<OperatorDecl *> collectOperators() { |
| std::vector<OperatorDecl *> results; |
| assert(CurrDeclContext); |
| for (auto import : namelookup::getAllImports(CurrDeclContext)) { |
| for (auto fileUnit : import.second->getFiles()) { |
| switch (fileUnit->getKind()) { |
| case FileUnitKind::Builtin: |
| case FileUnitKind::ClangModule: |
| case FileUnitKind::DWARFModule: |
| continue; |
| case FileUnitKind::Source: |
| collectOperatorsFrom(cast<SourceFile>(fileUnit), results); |
| break; |
| case FileUnitKind::SerializedAST: |
| collectOperatorsFrom(cast<LoadedFile>(fileUnit), results); |
| break; |
| } |
| } |
| } |
| return 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.addTextChunk(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); |
| builder.addCallParameter(Identifier(), Identifier(), RHSType, |
| /*IsVarArg*/ false, /*IsInOut*/ false, |
| /*isIUO*/ false, /*isAutoClosure*/ false); |
| 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.addTextChunk(op->getName().str()); |
| builder.addWhitespace(" "); |
| if (RHSType) |
| builder.addCallParameter(Identifier(), Identifier(), RHSType, |
| /*IsVarArg*/ false, /*IsInOut*/ false, |
| /*isIUO*/ false, /*isAutoClosure*/ false); |
| 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); |
| |
| std::vector<OperatorDecl *> operators = collectOperators(); |
| // 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, |
| StringRef defaultTypeName) { |
| // 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 (!defaultTypeName.empty()) |
| builder.addTypeAnnotation(defaultTypeName); |
| } |
| |
| /// Add '#file', '#line', et at. |
| void addPoundLiteralCompletions(bool needPound) { |
| auto addFromProto = [&](StringRef name, CodeCompletionKeywordKind kwKind, |
| CodeCompletionLiteralKind literalKind, |
| StringRef defaultTypeName) { |
| if (!needPound) |
| name = name.substr(1); |
| |
| CodeCompletionResultBuilder builder( |
| Sink, CodeCompletionResult::ResultKind::Keyword, |
| SemanticContextKind::None, {}); |
| builder.setLiteralKind(literalKind); |
| builder.setKeywordKind(kwKind); |
| builder.addTextChunk(name); |
| addTypeRelationFromProtocol(builder, literalKind, defaultTypeName); |
| }; |
| |
| addFromProto("#function", CodeCompletionKeywordKind::pound_function, |
| CodeCompletionLiteralKind::StringLiteral, "String"); |
| addFromProto("#file", CodeCompletionKeywordKind::pound_file, |
| CodeCompletionLiteralKind::StringLiteral, "String"); |
| addFromProto("#line", CodeCompletionKeywordKind::pound_line, |
| CodeCompletionLiteralKind::IntegerLiteral, "Int"); |
| addFromProto("#column", CodeCompletionKeywordKind::pound_column, |
| CodeCompletionLiteralKind::IntegerLiteral, "Int"); |
| |
| addKeyword(needPound ? "#dsohandle" : "dsohandle", "UnsafeRawPointer", |
| CodeCompletionKeywordKind::pound_dsohandle); |
| } |
| |
| void addValueLiteralCompletions() { |
| auto &context = CurrDeclContext->getASTContext(); |
| |
| auto addFromProto = [&]( |
| CodeCompletionLiteralKind kind, StringRef defaultTypeName, |
| llvm::function_ref<void(CodeCompletionResultBuilder &)> consumer, |
| bool isKeyword = false) { |
| |
| CodeCompletionResultBuilder builder(Sink, CodeCompletionResult::Literal, |
| SemanticContextKind::None, {}); |
| builder.setLiteralKind(kind); |
| |
| consumer(builder); |
| addTypeRelationFromProtocol(builder, kind, defaultTypeName); |
| }; |
| |
| // 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, "Int", [](Builder &builder) { |
| builder.addTextChunk("0"); |
| }); |
| addFromProto(LK::BooleanLiteral, "Bool", [](Builder &builder) { |
| builder.addTextChunk("true"); |
| }, /*isKeyword=*/true); |
| addFromProto(LK::BooleanLiteral, "Bool", [](Builder &builder) { |
| builder.addTextChunk("false"); |
| }, /*isKeyword=*/true); |
| addFromProto(LK::NilLiteral, "", [](Builder &builder) { |
| builder.addTextChunk("nil"); |
| }, /*isKeyword=*/true); |
| addFromProto(LK::StringLiteral, "String", [&](Builder &builder) { |
| builder.addTextChunk("\""); |
| builder.addSimpleNamedParameter("abc"); |
| builder.addTextChunk("\""); |
| }); |
| addFromProto(LK::ArrayLiteral, "Array", [&](Builder &builder) { |
| builder.addLeftBracket(); |
| builder.addSimpleNamedParameter("values"); |
| builder.addRightBracket(); |
| }); |
| addFromProto(LK::DictionaryLiteral, "Dictionary", [&](Builder &builder) { |
| builder.addLeftBracket(); |
| builder.addSimpleNamedParameter("key"); |
| builder.addTextChunk(": "); |
| builder.addSimpleNamedParameter("value"); |
| builder.addRightBracket(); |
| }); |
| |
| auto floatType = context.getFloatDecl()->getDeclaredType(); |
| addFromProto(LK::ColorLiteral, "", [&](Builder &builder) { |
| builder.addTextChunk("#colorLiteral"); |
| builder.addLeftParen(); |
| builder.addCallParameter(context.getIdentifier("red"), floatType, |
| /*IsVarArg*/ false, /*IsInOut*/ false, |
| /*isIUO*/ false, /*isAutoClosure*/ false); |
| builder.addComma(); |
| builder.addCallParameter(context.getIdentifier("green"), floatType, |
| /*IsVarArg*/ false, /*IsInOut*/ false, |
| /*isIUO*/ false, /*isAutoClosure*/ false); |
| builder.addComma(); |
| builder.addCallParameter(context.getIdentifier("blue"), floatType, |
| /*IsVarArg*/ false, /*IsInOut*/ false, |
| /*isIUO*/ false, /*isAutoClosure*/ false); |
| builder.addComma(); |
| builder.addCallParameter(context.getIdentifier("alpha"), floatType, |
| /*IsVarArg*/ false, /*IsInOut*/ false, |
| /*isIUO*/ false, /*isAutoClosure*/ false); |
| builder.addRightParen(); |
| }); |
| |
| auto stringType = context.getStringDecl()->getDeclaredType(); |
| addFromProto(LK::ImageLiteral, "", [&](Builder &builder) { |
| builder.addTextChunk("#imageLiteral"); |
| builder.addLeftParen(); |
| builder.addCallParameter(context.getIdentifier("resourceName"), |
| stringType, /*IsVarArg*/ false, |
| /*IsInOut*/ false, /*isIUO*/ false, |
| /*isAutoClosure*/ false); |
| 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 IncludeTopLevel = false, |
| bool RequestCache = true, |
| bool LiteralCompletions = true) { |
| ExprType = Type(); |
| Kind = LookupKind::ValueInDeclContext; |
| NeedLeadingDot = false; |
| FilteredDeclConsumer Consumer(*this, Filter); |
| lookupVisibleDecls(Consumer, CurrDeclContext, |
| /*IncludeTopLevel=*/IncludeTopLevel, Loc); |
| if (RequestCache) |
| RequestedCachedResults.push_back(RequestedResultsTy::toplevelResults()); |
| |
| // 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; |
| |
| DeclContext *DC = const_cast<DeclContext *>(CurrDeclContext); |
| |
| // 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) { |
| return isReferenceableByImplicitMemberExpr(CurrModule, DC, T, VD); |
| }); |
| |
| 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); |
| } |
| |
| void getUnresolvedMemberCompletions(ArrayRef<Type> Types) { |
| NeedLeadingDot = !HaveDot; |
| for (auto T : Types) { |
| if (!T) |
| continue; |
| if (auto objT = T->getOptionalObjectType()) { |
| // If this is optional type, perform completion for the object type. |
| // i.e. 'let _: Enum??? = .enumMember' is legal. |
| getUnresolvedMemberCompletions(objT->lookThroughAllOptionalTypes()); |
| |
| // 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::ExpressionSpecific, |
| CodeCompletionKeywordKind::kw_nil, bytesToErase); |
| } |
| getUnresolvedMemberCompletions(T); |
| } |
| } |
| |
| void addArgNameCompletionResults(ArrayRef<StringRef> Names) { |
| for (auto Name : Names) { |
| CodeCompletionResultBuilder Builder(Sink, |
| CodeCompletionResult::ResultKind::Keyword, |
| SemanticContextKind::ExpressionSpecific, {}); |
| Builder.addTextChunk(Name); |
| Builder.addCallParameterColon(); |
| Builder.addTypeAnnotation("Argument name"); |
| } |
| } |
| |
| void getTypeCompletions(Type BaseType) { |
| Kind = LookupKind::Type; |
| this->BaseType = BaseType; |
| NeedLeadingDot = !HaveDot; |
| lookupVisibleMemberDecls(*this, MetatypeType::get(BaseType), |
| CurrDeclContext, |
| IncludeInstanceMembers); |
| if (BaseType->isAnyExistentialType()) { |
| addKeyword("Protocol", MetatypeType::get(BaseType)); |
| addKeyword("Type", ExistentialMetatypeType::get(BaseType)); |
| } else { |
| addKeyword("Type", MetatypeType::get(BaseType)); |
| } |
| } |
| |
| static bool canUseAttributeOnDecl(DeclAttrKind DAK, bool IsInSil, |
| 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 (!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" |
| } |
| } |
| std::string Description = TargetName.str() + " Attribute"; |
| #define DECL_ATTR(KEYWORD, NAME, ...) \ |
| if (canUseAttributeOnDecl(DAK_##NAME, IsInSil, 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(#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.second; |
| if (Module == CurrModule) |
| continue; |
| |
| RequestedCachedResults.push_back( |
| RequestedResultsTy::fromModule(Module).onlyPrecedenceGroups()); |
| } |
| } |
| |
| 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 getTypeCompletionsInDeclContext(SourceLoc Loc) { |
| Kind = LookupKind::TypeInDeclContext; |
| lookupVisibleDecls(*this, CurrDeclContext, |
| /*IncludeTopLevel=*/false, Loc); |
| |
| RequestedCachedResults.push_back( |
| RequestedResultsTy::toplevelResults().onlyTypes()); |
| } |
| |
| void getToplevelCompletions(bool OnlyTypes) { |
| Kind = OnlyTypes ? LookupKind::TypeInDeclContext |
| : LookupKind::ValueInDeclContext; |
| NeedLeadingDot = false; |
| AccessFilteringDeclConsumer FilteringConsumer(CurrDeclContext, *this); |
| CurrModule->lookupVisibleDecls({}, FilteringConsumer, |
| NLKind::UnqualifiedLookup); |
| } |
| |
| void getVisibleDeclsOfModule(const ModuleDecl *TheModule, |
| ArrayRef<std::string> AccessPath, |
| bool ResultsHaveLeadingDot) { |
| Kind = LookupKind::ImportFromModule; |
| NeedLeadingDot = ResultsHaveLeadingDot; |
| |
| llvm::SmallVector<std::pair<Identifier, SourceLoc>, 1> LookupAccessPath; |
| for (auto Piece : AccessPath) { |
| LookupAccessPath.push_back( |
| std::make_pair(Ctx.getIdentifier(Piece), SourceLoc())); |
| } |
| AccessFilteringDeclConsumer FilteringConsumer(CurrDeclContext, *this); |
| TheModule->lookupVisibleDecls(LookupAccessPath, FilteringConsumer, |
| NLKind::UnqualifiedLookup); |
| |
| llvm::SmallVector<PrecedenceGroupDecl*, 16> precedenceGroups; |
| TheModule->getPrecedenceGroups(precedenceGroups); |
| |
| for (auto PGD: precedenceGroups) |
| addPrecedenceGroupRef(PGD); |
| } |
| }; |
| |
| 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) { |
| (void)createTypeChecker(Ctx); |
| |
| 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) { |
| assert(CurrDeclContext->getSelfNominalTypeDecl()); |
| auto AccessOfContext = |
| CurrDeclContext->getSelfNominalTypeDecl()->getFormalAccess(); |
| auto Access = std::min(VD->getFormalAccess(), 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. |
| TypeLoc getOpaqueResultTypeLoc(const ValueDecl *VD, DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo) { |
| if (Reason != |
| DeclVisibilityKind::MemberOfProtocolImplementedByCurrentNominal) |
| return nullptr; |
| |
| auto currTy = CurrDeclContext->getDeclaredTypeInContext(); |
| if (!currTy) |
| return nullptr; |
| |
| Type ResultT; |
| if (auto *FD = dyn_cast<FuncDecl>(VD)) |
| ResultT = FD->getResultInterfaceType(); |
| else if (auto *SD = dyn_cast<SubscriptDecl>(VD)) |
| ResultT = SD->getElementInterfaceType(); |
| else if (auto *VarD = dyn_cast<VarDecl>(VD)) |
| ResultT = VarD->getInterfaceType(); |
| else |
| return nullptr; |
| |
| if (!ResultT->is<DependentMemberType>()) |
| // The result is not associatedtype. |
| return nullptr; |
| |
| // If associatedtype doesn't have conformance/superclass constraint, we |
| // can't use opaque type. |
| auto assocTyD = ResultT->castTo<DependentMemberType>()->getAssocType(); |
| if (!assocTyD->getInherited().size()) |
| return nullptr; |
| |
| // Try substitution to see if the associated type is resolved to concrete |
| // type. |
| auto substMap = currTy->getMemberSubstitutionMap( |
| CurrDeclContext->getParentModule(), VD); |
| ResultT = ResultT.subst(substMap); |
| if (!ResultT || !ResultT->is<DependentMemberType>()) |
| // If resolved print it. |
| return nullptr; |
| |
| return assocTyD->getInherited()[0]; |
| } |
| |
| void addValueOverride(const ValueDecl *VD, DeclVisibilityKind Reason, |
| DynamicLookupInfo dynamicLookupInfo, |
| CodeCompletionResultBuilder &Builder, |
| bool hasDeclIntroducer) { |
| class DeclPrinter : public StreamPrinter { |
| TypeLoc OpaqueBaseTy; |
| |
| public: |
| using StreamPrinter::StreamPrinter; |
| |
| Optional<unsigned> NameOffset; |
| |
| DeclPrinter(raw_ostream &OS, TypeLoc 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 = OpaqueBaseTy; |
| } |
| } |
| }; |
| |
| llvm::SmallString<256> DeclStr; |
| unsigned NameOffset = 0; |
| { |
| llvm::raw_svector_ostream OS(DeclStr); |
| DeclPrinter Printer( |
| OS, getOpaqueResultTypeLoc(VD, Reason, dynamicLookupInfo)); |
| PrintOptions Options; |
| if (auto transformType = CurrDeclContext->getDeclaredTypeInContext()) |
| Options.setBaseType(transformType); |
| 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()); |
| Builder.setNumBytesToErase(dist); |
| Builder.addOverrideKeyword(); |
| Builder.addDeclIntroducer(DeclStr.str().substr(0, NameOffset)); |
| } |
| } |
| |
| if (!hasDeclIntroducer) |
| 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.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.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.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.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")) { |
| if (Reason == |
| DeclVisibilityKind::MemberOfProtocolImplementedByCurrentNominal && |
| !C->isFinal()) |
| needRequired = true; |
| else if (Reason == DeclVisibilityKind::MemberOfSuper && CD->isRequired()) |
| needRequired = true; |
| } |
| |
| llvm::SmallString<256> DeclStr; |
| if (needRequired) |
| DeclStr += "required "; |
| { |
| llvm::raw_svector_ostream OS(DeclStr); |
| PrintOptions Options; |
| 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() || |
| // A 'class' member with an initial value cannot be overriden either. |
| (D->isStatic() && D->getAttrs().hasAttribute<HasInitialValueAttr>())) |
| 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::MemberOfProtocolImplementedByCurrentNominal, |
| {}); |
| } |
| } |
| } |
| |
| 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); |
| addDesignatedInitializers(NTD); |
| addAssociatedTypes(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(Expr *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; |
| this->DotLoc = DotLoc; |
| CurDeclContext = P.CurDeclContext; |
| } |
| |
| 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::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() { |
| Kind = CompletionKind::TypeIdentifierWithDot; |
| CurDeclContext = P.CurDeclContext; |
| } |
| |
| void CodeCompletionCallbacksImpl::completeTypeIdentifierWithoutDot() { |
| Kind = CompletionKind::TypeIdentifierWithoutDot; |
| 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( |
| std::vector<std::pair<Identifier, SourceLoc>> &Path) { |
| Kind = CompletionKind::Import; |
| CurDeclContext = P.CurDeclContext; |
| DotLoc = Path.empty() ? SourceLoc() : Path.back().second; |
| if (DotLoc.isInvalid()) |
| return; |
| auto Importer = static_cast<ClangImporter *>(CurDeclContext->getASTContext(). |
| getClangModuleLoader()); |
| std::vector<std::string> SubNames; |
| Importer->collectSubModuleNames(Path, SubNames); |
| ASTContext &Ctx = CurDeclContext->getASTContext(); |
| for (StringRef Sub : SubNames) { |
| Path.push_back(std::make_pair(Ctx.getIdentifier(Sub), SourceLoc())); |
| SubModuleNameVisibilityPairs.push_back( |
| std::make_pair(Sub.str(), Ctx.getLoadedModule(Path))); |
| 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::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::completeGenericParams(TypeLoc TL) { |
| CurDeclContext = P.CurDeclContext; |
| Kind = CompletionKind::GenericParams; |
| ParsedTypeLoc = TL; |
| } |
| |
| 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; |
| } |
| |
| 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 = "") { |
| CodeCompletionResultBuilder Builder( |
| Sink, CodeCompletionResult::ResultKind::Keyword, |
| SemanticContextKind::None, {}); |
| Builder.setKeywordKind(Kind); |
| Builder.addTextChunk(Name); |
| if (!TypeAnnotation.empty()) |
| Builder.addTypeAnnotation(TypeAnnotation); |
| } |
| |
| static void addDeclKeywords(CodeCompletionResultSink &Sink) { |
| 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; |
| } |
| |
| // Remove user inaccessible keywords. |
| if (DAK.hasValue() && DeclAttribute::isUserInaccessible(*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) { |
| // Expr keywords. |
| addKeyword(Sink, "try", CodeCompletionKeywordKind::kw_try); |
| addKeyword(Sink, "try!", CodeCompletionKeywordKind::kw_try); |
| addKeyword(Sink, "try?", CodeCompletionKeywordKind::kw_try); |
| } |
| |
| static void addOpaqueTypeKeyword(CodeCompletionResultSink &Sink) { |
| addKeyword(Sink, "some", CodeCompletionKeywordKind::None, "some"); |
| } |
| |
| static void addAnyTypeKeyword(CodeCompletionResultSink &Sink) { |
| addKeyword(Sink, "Any", CodeCompletionKeywordKind::None, "Any"); |
| } |
| |
| 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::AfterPoundExpr: |
| case CompletionKind::AfterPoundDirective: |
| case CompletionKind::PlatformConditon: |
| case CompletionKind::GenericParams: |
| case CompletionKind::KeyPathExprObjC: |
| case CompletionKind::KeyPathExprSwift: |
| case CompletionKind::PrecedenceGroup: |
| 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); |
| addStmtKeywords(Sink, MaybeFuncBody); |
| LLVM_FALLTHROUGH; |
| case CompletionKind::ReturnStmtExpr: |
| case CompletionKind::YieldStmtExpr: |
| case CompletionKind::PostfixExprBeginning: |
| case CompletionKind::ForEachSequence: |
| addSuperKeyword(Sink); |
| addLetVarKeywords(Sink); |
| addExprKeywords(Sink); |
| addAnyTypeKeyword(Sink); |
| 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: |
| if (!isa<ProtocolDecl>(CurDeclContext)) |
| if (CurDeclContext->isTypeContext() || |
| (ParsedDecl && isa<FuncDecl>(ParsedDecl))) |
| addOpaqueTypeKeyword(Sink); |
| |
| LLVM_FALLTHROUGH; |
| case CompletionKind::TypeSimpleBeginning: |
| addAnyTypeKeyword(Sink); |
| 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); |
| 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.addTextChunk(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.addTextChunk(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); |
| } |
| } |
| |
| 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; |
| } |
| // Add keywords even if type checking fails completely. |
| addKeywords(CompletionContext.getResultSink(), MaybeFuncBody); |
| |
| if (auto *DC = dyn_cast_or_null<DeclContext>(ParsedDecl)) { |
| if (DC->isChildContextOf(CurDeclContext)) |
| CurDeclContext = DC; |
| } |
| |
| typeCheckContextUntil( |
| CurDeclContext, |
| CurDeclContext->getASTContext().SourceMgr.getCodeCompletionLoc()); |
| |
| 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()->getFullName() == Context.Id_self); |
| } else if (auto *SRE = dyn_cast_or_null<SuperRefExpr>(ParsedExpr)) { |
| Lookup.setIsSuperRefExpr(); |
| } |
| |
| if (isInsideObjCSelector()) |
| Lookup.includeInstanceMembers(); |
| if (PreferFunctionReferencesToCalls) |
| Lookup.setPreferFunctionReferencesToCalls(); |
| |
| auto DoPostfixExprBeginning = [&] (){ |
| SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc(); |
| Lookup.getValueCompletionsInDeclContext(Loc); |
| }; |
| |
| switch (Kind) { |
| case CompletionKind::None: |
| llvm_unreachable("should be already handled"); |
| return; |
| |
| case CompletionKind::DotExpr: { |
| Lookup.setHaveDot(DotLoc); |
| |
| if (isDynamicLookup(*ExprType)) |
| Lookup.setIsDynamicLookup(); |
| |
| Lookup.getPostfixKeywordCompletions(*ExprType, ParsedExpr); |
| |
| if (isa<BindOptionalExpr>(ParsedExpr) || isa<ForceValueExpr>(ParsedExpr)) |
| Lookup.setIsUnwrappedOptional(true); |
| |
| ExprContextInfo ContextInfo(CurDeclContext, ParsedExpr); |
| Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(), |
| ContextInfo.isSingleExpressionBody()); |
| Lookup.getValueExprCompletions(*ExprType, ReferencedDecl.getDecl()); |
| break; |
| } |
| |
| 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 (!T->getAnyNominal() || !T->getAnyNominal()->getKeyPathTypeKind() || |
| T->hasUnresolvedType() || !T->is<BoundGenericType>()) |
| continue; |
| // Use the first KeyPath context type found. |
| baseType = T->castTo<BoundGenericType>()->getGenericArgs()[0]; |
| break; |
| } |
| } |
| if (!OnRoot && KPE->getComponents().back().getKind() == |
| KeyPathExpr::Component::Kind::OptionalWrap) { |
| // KeyPath expr with '?' (e.g. '\Ty.[0].prop?.another'). |
| // Althogh 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.isSingleExpressionBody()); |
| 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.isSingleExpressionBody()); |
| if (ExprType && ((*ExprType)->is<AnyFunctionType>() || |
| (*ExprType)->is<AnyMetatypeType>())) { |
| Lookup.getValueExprCompletions(*ExprType, ReferencedDecl.getDecl()); |
| } else { |
| for (auto &typeAndDecl : ContextInfo.getPossibleCallees()) |
| Lookup.tryFunctionCallCompletions(typeAndDecl.first, typeAndDecl.second); |
| } |
| } else { |
| // Add argument labels, then fallthrough to get values. |
| Lookup.addArgNameCompletionResults(ContextInfo.getPossibleNames()); |
| } |
| |
| if (!Lookup.FoundFunctionCalls || |
| (Lookup.FoundFunctionCalls && |
| Lookup.FoundFunctionsWithoutFirstKeyword)) { |
| Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(), |
| ContextInfo.isSingleExpressionBody()); |
| 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, |
| false, true, false); |
| } |
| break; |
| } |
| |
| case CompletionKind::TypeDeclResultBeginning: |
| case CompletionKind::TypeSimpleBeginning: { |
| Lookup.getTypeCompletionsInDeclContext( |
| P.Context.SourceMgr.getCodeCompletionLoc()); |
| 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.isSingleExpressionBody()); |
| 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.isSingleExpressionBody()); |
| DoPostfixExprBeginning(); |
| } |
| break; |
| } |
| |
| case CompletionKind::AttributeBegin: { |
| Lookup.getAttributeDeclCompletions(IsInSil, AttTargetDK); |
| |
| // TypeName at attribute position after '@'. |
| // - VarDecl: Property Wrappers. |
| // - ParamDecl/VarDecl/FuncDecl: Function Buildres. |
| 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::UnresolvedMember: { |
| Lookup.setHaveDot(DotLoc); |
| ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr); |
| Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(), |
| ContextInfo.isSingleExpressionBody()); |
| Lookup.getUnresolvedMemberCompletions(ContextInfo.getPossibleTypes()); |
| break; |
| } |
| case CompletionKind::CallArg : { |
| ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr); |
| |
| bool shouldPerformGlobalCompletion = true; |
| |
| if (ShouldCompleteCallPatternAfterParen && |
| !ContextInfo.getPossibleCallees().empty()) { |
| Lookup.setHaveLParen(true); |
| for (auto &typeAndDecl : ContextInfo.getPossibleCallees()) { |
| if (auto SD = dyn_cast_or_null<SubscriptDecl>(typeAndDecl.second)) { |
| Lookup.addSubscriptCallPattern(typeAndDecl.first, SD); |
| } else { |
| Lookup.addFunctionCallPattern( |
| typeAndDecl.first, |
| dyn_cast_or_null<AbstractFunctionDecl>(typeAndDecl.second)); |
| } |
| } |
| Lookup.setHaveLParen(false); |
| |
| shouldPerformGlobalCompletion = |
| !Lookup.FoundFunctionCalls || |
| (Lookup.FoundFunctionCalls && |
| Lookup.FoundFunctionsWithoutFirstKeyword); |
| } else if (!ContextInfo.getPossibleNames().empty()) { |
| Lookup.addArgNameCompletionResults(ContextInfo.getPossibleNames()); |
| |
| shouldPerformGlobalCompletion = !ContextInfo.getPossibleTypes().empty(); |
| } |
| |
| if (shouldPerformGlobalCompletion) { |
| Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(), |
| ContextInfo.isSingleExpressionBody()); |
| DoPostfixExprBeginning(); |
| } |
| break; |
| } |
| |
| case CompletionKind::ReturnStmtExpr : { |
| SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc(); |
| Lookup.setExpectedTypes(getReturnTypeFromContext(CurDeclContext), |
| /*isSingleExpressionBody*/ 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(), |
| /*isSingleExpressionBody*/ false); |
| } |
| } |
| Lookup.getValueCompletionsInDeclContext(Loc); |
| break; |
| } |
| |
| case CompletionKind::AfterPoundExpr: { |
| ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr); |
| Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(), |
| ContextInfo.isSingleExpressionBody()); |
| |
| 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::GenericParams: |
| if (auto GT = ParsedTypeLoc.getType()->getAnyGeneric()) { |
| if (auto Params = GT->getGenericParams()) { |
| for (auto GP : Params->getParams()) { |
| Lookup.addGenericTypeParamRef( |
| GP, DeclVisibilityKind::GenericParameter, {}); |
| } |
| } |
| } |
| break; |
| case CompletionKind::PrecedenceGroup: |
| Lookup.getPrecedenceGroupCompletions(SyntxKind); |
| break; |
| case CompletionKind::AfterIfStmtElse: |
| case CompletionKind::CaseStmtKeyword: |
| // Handled earlier by keyword completions. |
| break; |
| } |
| |
| for (auto &Request: Lookup.RequestedCachedResults) { |
| // 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. |
| const SourceFile &SF = P.SF; |
| |
| llvm::DenseSet<CodeCompletionCache::Key> ImportsSeen; |
| auto handleImport = [&](ModuleDecl::ImportedModule Import) { |
| ModuleDecl *TheModule = Import.second; |
| ModuleDecl::AccessPathTy Path = Import.first; |
| 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(Piece.first.str()); |
| } |
| |
| 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, TheModule->getName().str(), AccessPath, |
| Request.NeedLeadingDot, |
| SF.hasTestableOrPrivateImport( |
| AccessLevel::Internal, TheModule, |
| SourceFile::ImportQueryKind::TestableOnly), |
| SF.hasTestableOrPrivateImport( |
| AccessLevel::Internal, TheModule, |
| SourceFile::ImportQueryKind::PrivateOnly), |
| Ctx.LangOpts.CodeCompleteInitsInPostfixExpr}; |
| |
| 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.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 results for all imported modules. |
| ModuleDecl::ImportFilter ImportFilter; |
| ImportFilter |= ModuleDecl::ImportFilterKind::Public; |
| ImportFilter |= ModuleDecl::ImportFilterKind::Private; |
| ImportFilter |= ModuleDecl::ImportFilterKind::ImplementationOnly; |
| SmallVector<ModuleDecl::ImportedModule, 4> Imports; |
| auto *SF = CurDeclContext->getParentSourceFile(); |
| SF->getImportedModules(Imports, ImportFilter); |
| |
| for (auto Imported : Imports) { |
| for (auto Import : namelookup::getAllImports(Imported.second)) |
| handleImport(Import); |
| } |
| } |
| } |
| Lookup.RequestedCachedResults.clear(); |
| |
| CompletionContext.typeContextKind = Lookup.typeContextKind(); |
| |
| deliverCompletionResults(); |
| } |
| |
| void CodeCompletionCallbacksImpl::deliverCompletionResults() { |
| // 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 = &P.SF; |
| |
| Consumer.handleResultsAndModules(CompletionContext, RequestedModules, |
| DCForModules); |
| RequestedModules.clear(); |
| DeliveredResults = true; |
| } |
| |
| 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->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.getVisibleDeclsOfModule(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(); |
| 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()); |
| } |