//===--- 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 "CodeCompletionDiagnostics.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/GenericSignature.h" #include "swift/AST/ImportCache.h" #include "swift/AST/Initializer.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/Frontend/FrontendOptions.h" #include "swift/IDE/CodeCompletionStringPrinter.h" #include "swift/IDE/CompletionLookup.h" #include "swift/IDE/CodeCompletionCache.h" #include "swift/IDE/CodeCompletionResultPrinter.h" #include "swift/IDE/Utils.h" #include "swift/Parse/CodeCompletionCallbacks.h" #include "swift/Sema/CodeCompletionTypeChecking.h" #include "swift/Sema/IDETypeChecking.h" #include "swift/Strings.h" #include "swift/Subsystems.h" #include "swift/Syntax/SyntaxKind.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/SaveAndRestore.h" #include "llvm/Support/raw_ostream.h" #include #include using namespace swift; using namespace ide; 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; } ContextFreeCodeCompletionResult * ContextFreeCodeCompletionResult::createPatternOrBuiltInOperatorResult( llvm::BumpPtrAllocator &Allocator, CodeCompletionResultKind Kind, CodeCompletionString *CompletionString, CodeCompletionOperatorKind KnownOperatorKind, NullTerminatedStringRef BriefDocComment, CodeCompletionResultType ResultType, ContextFreeNotRecommendedReason NotRecommended, CodeCompletionDiagnosticSeverity DiagnosticSeverity, NullTerminatedStringRef DiagnosticMessage) { return new (Allocator) ContextFreeCodeCompletionResult( Kind, /*AssociatedKind=*/0, KnownOperatorKind, /*IsSystem=*/false, CompletionString, /*ModuleName=*/"", BriefDocComment, /*AssociatedUSRs=*/{}, ResultType, NotRecommended, DiagnosticSeverity, DiagnosticMessage, getCodeCompletionResultFilterName(CompletionString, Allocator)); } ContextFreeCodeCompletionResult * ContextFreeCodeCompletionResult::createKeywordResult( llvm::BumpPtrAllocator &Allocator, CodeCompletionKeywordKind Kind, CodeCompletionString *CompletionString, NullTerminatedStringRef BriefDocComment, CodeCompletionResultType ResultType) { return new (Allocator) ContextFreeCodeCompletionResult( CodeCompletionResultKind::Keyword, static_cast(Kind), CodeCompletionOperatorKind::None, /*IsSystem=*/false, CompletionString, /*ModuleName=*/"", BriefDocComment, /*AssociatedUSRs=*/{}, ResultType, ContextFreeNotRecommendedReason::None, CodeCompletionDiagnosticSeverity::None, /*DiagnosticMessage=*/"", getCodeCompletionResultFilterName(CompletionString, Allocator)); } ContextFreeCodeCompletionResult * ContextFreeCodeCompletionResult::createLiteralResult( llvm::BumpPtrAllocator &Allocator, CodeCompletionLiteralKind LiteralKind, CodeCompletionString *CompletionString, CodeCompletionResultType ResultType) { return new (Allocator) ContextFreeCodeCompletionResult( CodeCompletionResultKind::Literal, static_cast(LiteralKind), CodeCompletionOperatorKind::None, /*IsSystem=*/false, CompletionString, /*ModuleName=*/"", /*BriefDocComment=*/"", /*AssociatedUSRs=*/{}, ResultType, ContextFreeNotRecommendedReason::None, CodeCompletionDiagnosticSeverity::None, /*DiagnosticMessage=*/"", getCodeCompletionResultFilterName(CompletionString, Allocator)); } ContextFreeCodeCompletionResult * ContextFreeCodeCompletionResult::createDeclResult( llvm::BumpPtrAllocator &Allocator, CodeCompletionString *CompletionString, const Decl *AssociatedDecl, NullTerminatedStringRef ModuleName, NullTerminatedStringRef BriefDocComment, ArrayRef AssociatedUSRs, CodeCompletionResultType ResultType, ContextFreeNotRecommendedReason NotRecommended, CodeCompletionDiagnosticSeverity DiagnosticSeverity, NullTerminatedStringRef DiagnosticMessage) { assert(AssociatedDecl && "should have a decl"); return new (Allocator) ContextFreeCodeCompletionResult( CodeCompletionResultKind::Declaration, static_cast(getCodeCompletionDeclKind(AssociatedDecl)), CodeCompletionOperatorKind::None, getDeclIsSystem(AssociatedDecl), CompletionString, ModuleName, BriefDocComment, AssociatedUSRs, ResultType, NotRecommended, DiagnosticSeverity, DiagnosticMessage, getCodeCompletionResultFilterName(CompletionString, Allocator)); } CodeCompletionDeclKind ContextFreeCodeCompletionResult::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: if (cast(D)->isActor()) { return CodeCompletionDeclKind::Actor; } else { return CodeCompletionDeclKind::Class; } case DeclKind::Protocol: return CodeCompletionDeclKind::Protocol; case DeclKind::Var: case DeclKind::Param: { auto DC = D->getDeclContext(); if (DC->isTypeContext()) { if (cast(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(D); if (DC->isTypeContext()) { if (FD->isStatic()) return CodeCompletionDeclKind::StaticMethod; return CodeCompletionDeclKind::InstanceMethod; } if (FD->isOperator()) { if (auto op = FD->getOperatorDecl()) { switch (op->getKind()) { case DeclKind::PrefixOperator: return CodeCompletionDeclKind::PrefixOperatorFunction; case DeclKind::PostfixOperator: return CodeCompletionDeclKind::PostfixOperatorFunction; case DeclKind::InfixOperator: return CodeCompletionDeclKind::InfixOperatorFunction; default: llvm_unreachable("unexpected operator kind"); } } else { return CodeCompletionDeclKind::InfixOperatorFunction; } } return CodeCompletionDeclKind::FreeFunction; } case DeclKind::InfixOperator: return CodeCompletionDeclKind::InfixOperatorFunction; case DeclKind::PrefixOperator: return CodeCompletionDeclKind::PrefixOperatorFunction; case DeclKind::PostfixOperator: return CodeCompletionDeclKind::PostfixOperatorFunction; case DeclKind::PrecedenceGroup: return CodeCompletionDeclKind::PrecedenceGroup; case DeclKind::EnumElement: return CodeCompletionDeclKind::EnumElement; case DeclKind::Subscript: return CodeCompletionDeclKind::Subscript; } llvm_unreachable("invalid DeclKind"); } bool ContextFreeCodeCompletionResult::getDeclIsSystem(const Decl *D) { return D->getModuleContext()->isSystemModule(); } void CodeCompletionResult::printPrefix(raw_ostream &OS) const { llvm::SmallString<64> Prefix; switch (getKind()) { case CodeCompletionResultKind::Declaration: Prefix.append("Decl"); switch (getAssociatedDeclKind()) { case CodeCompletionDeclKind::Class: Prefix.append("[Class]"); break; case CodeCompletionDeclKind::Actor: Prefix.append("[Actor]"); 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 CodeCompletionResultKind::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 CodeCompletionResultKind::Pattern: Prefix.append("Pattern"); break; case CodeCompletionResultKind::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 CodeCompletionResultKind::BuiltinOperator: Prefix.append("BuiltinOperator"); break; } Prefix.append("/"); switch (getSemanticContext()) { case SemanticContextKind::None: Prefix.append("None"); 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 (!getModuleName().empty()) Prefix.append((Twine("[") + StringRef(getModuleName()) + "]").str()); break; } if (getFlair().toRaw()) { Prefix.append("/Flair["); bool isFirstFlair = true; #define PRINT_FLAIR(KIND, NAME) \ if (getFlair().contains(CodeCompletionFlairBit::KIND)) { \ if (isFirstFlair) { isFirstFlair = false; } \ else { Prefix.append(","); } \ Prefix.append(NAME); \ } PRINT_FLAIR(ExpressionSpecific, "ExprSpecific"); PRINT_FLAIR(SuperChain, "SuperChain"); PRINT_FLAIR(ArgumentLabels, "ArgLabels"); PRINT_FLAIR(CommonKeywordAtCurrentPosition, "CommonKeyword") PRINT_FLAIR(RareKeywordAtCurrentPosition, "RareKeyword") PRINT_FLAIR(RareTypeAtCurrentPosition, "RareType") PRINT_FLAIR(ExpressionAtNonScriptOrMainFileScope, "ExprAtFileScope") Prefix.append("]"); } if (isNotRecommended()) Prefix.append("/NotRecommended"); if (isSystem()) Prefix.append("/IsSystem"); if (NumBytesToErase != 0) { Prefix.append("/Erase["); Prefix.append(Twine(NumBytesToErase).str()); Prefix.append("]"); } switch (getExpectedTypeRelation()) { case CodeCompletionResultTypeRelation::Invalid: Prefix.append("/TypeRelation[Invalid]"); break; case CodeCompletionResultTypeRelation::Identical: Prefix.append("/TypeRelation[Identical]"); break; case CodeCompletionResultTypeRelation::Convertible: Prefix.append("/TypeRelation[Convertible]"); break; case CodeCompletionResultTypeRelation::NotApplicable: case CodeCompletionResultTypeRelation::Unknown: case CodeCompletionResultTypeRelation::Unrelated: break; } Prefix.append(": "); while (Prefix.size() < 36) { Prefix.append(" "); } OS << Prefix; } void CodeCompletionResult::dump() const { printPrefix(llvm::errs()); getCompletionString()->print(llvm::errs()); llvm::errs() << "\n"; } CodeCompletionResult * CodeCompletionResult::withFlair(CodeCompletionFlair NewFlair, CodeCompletionResultSink &Sink) const { return new (*Sink.Allocator) CodeCompletionResult( ContextFree, SemanticContext, NewFlair, NumBytesToErase, TypeDistance, NotRecommended, DiagnosticSeverity, DiagnosticMessage); } CodeCompletionResult * CodeCompletionResult::withContextFreeResultSemanticContextAndFlair( const ContextFreeCodeCompletionResult &NewContextFree, SemanticContextKind NewSemanticContext, CodeCompletionFlair NewFlair, CodeCompletionResultSink &Sink) const { return new (*Sink.Allocator) CodeCompletionResult( NewContextFree, NewSemanticContext, NewFlair, NumBytesToErase, TypeDistance, NotRecommended, DiagnosticSeverity, DiagnosticMessage); } void CodeCompletionResultBuilder::withNestedGroup( CodeCompletionString::Chunk::ChunkKind Kind, llvm::function_ref body) { ++CurrentNestingLevel; addSimpleChunk(Kind); body(); --CurrentNestingLevel; } void CodeCompletionResultBuilder::addChunkWithText( CodeCompletionString::Chunk::ChunkKind Kind, StringRef Text) { addChunkWithTextNoCopy(Kind, Text.copy(*Sink.Allocator)); } void CodeCompletionResultBuilder::setAssociatedDecl(const Decl *D) { assert(Kind == CodeCompletionResultKind::Declaration); AssociatedDecl = D; if (auto *ClangD = D->getClangDecl()) CurrentModule = ClangD->getImportedOwningModule(); // FIXME: macros // FIXME: imported header module if (!CurrentModule) { ModuleDecl *MD = D->getModuleContext(); // If this is an underscored cross-import overlay, map it to the underlying // module that declares it instead. if (ModuleDecl *Declaring = MD->getDeclaringModuleIfCrossImportOverlay()) MD = Declaring; CurrentModule = MD; } if (D->getAttrs().getDeprecated(D->getASTContext())) setContextFreeNotRecommended(ContextFreeNotRecommendedReason::Deprecated); else if (D->getAttrs().getSoftDeprecated(D->getASTContext())) setContextFreeNotRecommended( ContextFreeNotRecommendedReason::SoftDeprecated); if (D->getClangNode()) { if (auto *ClangD = D->getClangDecl()) { const auto &ClangContext = ClangD->getASTContext(); if (const clang::RawComment *RC = ClangContext.getRawCommentForAnyRedecl(ClangD)) { setBriefDocComment(RC->getBriefText(ClangContext)); } } } else { setBriefDocComment(AssociatedDecl->getBriefComment()); } } void CodeCompletionResultBuilder::addCallArgument( Identifier Name, Identifier LocalName, Type Ty, Type ContextTy, bool IsVarArg, bool IsInOut, bool IsIUO, bool IsAutoClosure, bool UseUnderscoreLabel, bool IsLabeledTrailingClosure, bool HasDefault) { ++CurrentNestingLevel; using ChunkKind = CodeCompletionString::Chunk::ChunkKind; addSimpleChunk(ChunkKind::CallArgumentBegin); if (shouldAnnotateResults()) { if (!Name.empty() || !LocalName.empty()) { llvm::SmallString<16> EscapedKeyword; if (!Name.empty()) { addChunkWithText( CodeCompletionString::Chunk::ChunkKind::CallArgumentName, escapeKeyword(Name.str(), false, EscapedKeyword)); if (!LocalName.empty() && Name != LocalName) { addChunkWithTextNoCopy(ChunkKind::Text, " "); getLastChunk().setIsAnnotation(); addChunkWithText(ChunkKind::CallArgumentInternalName, escapeKeyword(LocalName.str(), false, EscapedKeyword)); getLastChunk().setIsAnnotation(); } } else { assert(!LocalName.empty()); addChunkWithTextNoCopy(ChunkKind::CallArgumentName, "_"); getLastChunk().setIsAnnotation(); addChunkWithTextNoCopy(ChunkKind::Text, " "); getLastChunk().setIsAnnotation(); addChunkWithText(ChunkKind::CallArgumentInternalName, escapeKeyword(LocalName.str(), false, EscapedKeyword)); } addChunkWithTextNoCopy(ChunkKind::CallArgumentColon, ": "); } } else { if (!Name.empty()) { llvm::SmallString<16> EscapedKeyword; addChunkWithText( CodeCompletionString::Chunk::ChunkKind::CallArgumentName, escapeKeyword(Name.str(), false, EscapedKeyword)); addChunkWithTextNoCopy( CodeCompletionString::Chunk::ChunkKind::CallArgumentColon, ": "); } else if (UseUnderscoreLabel) { addChunkWithTextNoCopy( CodeCompletionString::Chunk::ChunkKind::CallArgumentName, "_"); addChunkWithTextNoCopy( CodeCompletionString::Chunk::ChunkKind::CallArgumentColon, ": "); } else if (!LocalName.empty()) { // Use local (non-API) parameter name if we have nothing else. llvm::SmallString<16> EscapedKeyword; addChunkWithText( CodeCompletionString::Chunk::ChunkKind::CallArgumentInternalName, escapeKeyword(LocalName.str(), false, EscapedKeyword)); addChunkWithTextNoCopy( CodeCompletionString::Chunk::ChunkKind::CallArgumentColon, ": "); } } // 'inout' arguments are printed specially. if (IsInOut) { addChunkWithTextNoCopy( CodeCompletionString::Chunk::ChunkKind::Ampersand, "&"); Ty = Ty->getInOutObjectType(); } // If the parameter is of the type @autoclosure ()->output, then the // code completion should show the parameter of the output type // instead of the function type ()->output. if (IsAutoClosure) { // 'Ty' may be ErrorType. if (auto funcTy = Ty->getAs()) Ty = funcTy->getResult(); } PrintOptions PO; PO.SkipAttributes = true; PO.PrintOptionalAsImplicitlyUnwrapped = IsIUO; PO.OpaqueReturnTypePrinting = PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; if (ContextTy) PO.setBaseType(ContextTy); if (shouldAnnotateResults()) { withNestedGroup(ChunkKind::CallArgumentTypeBegin, [&]() { CodeCompletionStringPrinter printer(*this); auto TL = TypeLoc::withoutLoc(Ty); printer.printTypePre(TL); Ty->print(printer, PO); printer.printTypePost(TL); }); } else { std::string TypeName = Ty->getString(PO); addChunkWithText(ChunkKind::CallArgumentType, TypeName); } if (HasDefault) { withNestedGroup(ChunkKind::CallArgumentDefaultBegin, []() { // Possibly add the actual value in the future }); } // Look through optional types and type aliases to find out if we have // function type. Ty = Ty->lookThroughAllOptionalTypes(); if (auto AFT = Ty->getAs()) { // If this is a closure type, add ChunkKind::CallArgumentClosureType or // ChunkKind::CallArgumentClosureExpr for labeled trailing closures. PrintOptions PO; PO.PrintFunctionRepresentationAttrs = PrintOptions::FunctionRepresentationMode::None; PO.SkipAttributes = true; PO.OpaqueReturnTypePrinting = PrintOptions::OpaqueReturnTypePrintingMode::WithoutOpaqueKeyword; PO.AlwaysTryPrintParameterLabels = true; if (ContextTy) PO.setBaseType(ContextTy); if (IsLabeledTrailingClosure) { // Expand the closure body. SmallString<32> buffer; llvm::raw_svector_ostream OS(buffer); bool firstParam = true; for (const auto ¶m : AFT->getParams()) { if (!firstParam) OS << ", "; firstParam = false; if (param.hasLabel()) { OS << param.getLabel(); } else if (param.hasInternalLabel()) { OS << param.getInternalLabel(); } else { OS << "<#"; if (param.isInOut()) OS << "inout "; OS << param.getPlainType()->getString(PO); if (param.isVariadic()) OS << "..."; OS << "#>"; } } if (!firstParam) OS << " in"; addChunkWithText( CodeCompletionString::Chunk::ChunkKind::CallArgumentClosureExpr, OS.str()); } else { // Add the closure type. addChunkWithText( CodeCompletionString::Chunk::ChunkKind::CallArgumentClosureType, AFT->getString(PO)); } } if (IsVarArg) addEllipsis(); --CurrentNestingLevel; } void CodeCompletionResultBuilder::addTypeAnnotation(Type T, PrintOptions PO, StringRef suffix) { T = T->getReferenceStorageReferent(); // Replace '()' with 'Void'. if (T->isVoid()) T = T->getASTContext().getVoidDecl()->getDeclaredInterfaceType(); if (shouldAnnotateResults()) { withNestedGroup(CodeCompletionString::Chunk::ChunkKind::TypeAnnotationBegin, [&]() { CodeCompletionStringPrinter printer(*this); auto TL = TypeLoc::withoutLoc(T); printer.printTypePre(TL); T->print(printer, PO); printer.printTypePost(TL); if (!suffix.empty()) printer.printText(suffix); }); } else { auto str = T.getString(PO); if (!suffix.empty()) str += suffix.str(); addTypeAnnotation(str); } } StringRef CodeCompletionContext::copyString(StringRef Str) { return Str.copy(*CurrentResults.Allocator); } bool shouldCopyAssociatedUSRForDecl(const ValueDecl *VD) { // Avoid trying to generate a USR for some declaration types. if (isa(VD) && !isa(VD)) return false; if (isa(VD)) return false; if (isa(VD)) return false; if (VD->hasClangNode() && !VD->getClangDecl()) return false; return true; } template static void walkValueDeclAndOverriddenDecls(const Decl *D, const FnTy &Fn) { if (auto *VD = dyn_cast(D)) { Fn(VD); walkOverriddenDecls(VD, Fn); } } ArrayRef copyAssociatedUSRs(llvm::BumpPtrAllocator &Allocator, const Decl *D) { llvm::SmallVector USRs; walkValueDeclAndOverriddenDecls(D, [&](llvm::PointerUnion OD) { llvm::SmallString<128> SS; bool Ignored = true; if (auto *OVD = OD.dyn_cast()) { if (shouldCopyAssociatedUSRForDecl(OVD)) { llvm::raw_svector_ostream OS(SS); Ignored = printValueDeclUSR(OVD, OS); } } else if (auto *OND = OD.dyn_cast()) { Ignored = clang::index::generateUSRForDecl(OND, SS); } if (!Ignored) USRs.emplace_back(SS, Allocator); }); if (!USRs.empty()) return makeArrayRef(USRs).copy(Allocator); return {}; } CodeCompletionResult::CodeCompletionResult( const ContextFreeCodeCompletionResult &ContextFree, SemanticContextKind SemanticContext, CodeCompletionFlair Flair, uint8_t NumBytesToErase, const ExpectedTypeContext *TypeContext, const DeclContext *DC, ContextualNotRecommendedReason NotRecommended, CodeCompletionDiagnosticSeverity DiagnosticSeverity, NullTerminatedStringRef DiagnosticMessage) : ContextFree(ContextFree), SemanticContext(SemanticContext), Flair(Flair.toRaw()), NotRecommended(NotRecommended), DiagnosticSeverity(DiagnosticSeverity), DiagnosticMessage(DiagnosticMessage), NumBytesToErase(NumBytesToErase), TypeDistance( ContextFree.getResultType().calculateTypeRelation(TypeContext, DC)) {} CodeCompletionOperatorKind ContextFreeCodeCompletionResult::getCodeCompletionOperatorKind( const CodeCompletionString *str) { StringRef name = str->getFirstTextChunk(/*includeLeadingPunctuation=*/true); using CCOK = CodeCompletionOperatorKind; using OpPair = std::pair; // 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; } CodeCompletionResult *CodeCompletionResultBuilder::takeResult() { auto &Allocator = *Sink.Allocator; auto *CCS = CodeCompletionString::create(Allocator, Chunks); CodeCompletionDiagnosticSeverity ContextFreeDiagnosticSeverity = CodeCompletionDiagnosticSeverity::None; NullTerminatedStringRef ContextFreeDiagnosticMessage; if (ContextFreeNotRecReason != ContextFreeNotRecommendedReason::None) { // FIXME: We should generate the message lazily. if (const auto *VD = dyn_cast(AssociatedDecl)) { CodeCompletionDiagnosticSeverity severity; SmallString<256> message; llvm::raw_svector_ostream messageOS(message); if (!getContextFreeCompletionDiagnostics(ContextFreeNotRecReason, VD, severity, messageOS)) { ContextFreeDiagnosticSeverity = severity; ContextFreeDiagnosticMessage = NullTerminatedStringRef(message, Allocator); } } } /// This variable should be initialized by all the switch cases below. ContextFreeCodeCompletionResult *ContextFreeResult = nullptr; switch (Kind) { case CodeCompletionResultKind::Declaration: { NullTerminatedStringRef ModuleName; if (CurrentModule) { if (Sink.LastModule.first == CurrentModule.getOpaqueValue()) { ModuleName = Sink.LastModule.second; } else { if (auto *C = CurrentModule.dyn_cast()) { ModuleName = NullTerminatedStringRef(C->getFullModuleName(), Allocator); } else { ModuleName = NullTerminatedStringRef( CurrentModule.get()->getName().str(), Allocator); } Sink.LastModule.first = CurrentModule.getOpaqueValue(); Sink.LastModule.second = ModuleName; } } ContextFreeResult = ContextFreeCodeCompletionResult::createDeclResult( Allocator, CCS, AssociatedDecl, ModuleName, NullTerminatedStringRef(BriefDocComment, Allocator), copyAssociatedUSRs(Allocator, AssociatedDecl), ResultType, ContextFreeNotRecReason, ContextFreeDiagnosticSeverity, ContextFreeDiagnosticMessage); break; } case CodeCompletionResultKind::Keyword: ContextFreeResult = ContextFreeCodeCompletionResult::createKeywordResult( Allocator, KeywordKind, CCS, NullTerminatedStringRef(BriefDocComment, Allocator), ResultType); break; case CodeCompletionResultKind::BuiltinOperator: case CodeCompletionResultKind::Pattern: ContextFreeResult = ContextFreeCodeCompletionResult::createPatternOrBuiltInOperatorResult( Allocator, Kind, CCS, CodeCompletionOperatorKind::None, NullTerminatedStringRef(BriefDocComment, Allocator), ResultType, ContextFreeNotRecReason, ContextFreeDiagnosticSeverity, ContextFreeDiagnosticMessage); break; case CodeCompletionResultKind::Literal: assert(LiteralKind.hasValue()); ContextFreeResult = ContextFreeCodeCompletionResult::createLiteralResult( Allocator, *LiteralKind, CCS, ResultType); break; } CodeCompletionDiagnosticSeverity ContextualDiagnosticSeverity = CodeCompletionDiagnosticSeverity::None; NullTerminatedStringRef ContextualDiagnosticMessage; if (ContextualNotRecReason != ContextualNotRecommendedReason::None) { // FIXME: We should generate the message lazily. if (const auto *VD = dyn_cast(AssociatedDecl)) { CodeCompletionDiagnosticSeverity severity; SmallString<256> message; llvm::raw_svector_ostream messageOS(message); if (!getContextualCompletionDiagnostics(ContextualNotRecReason, VD, severity, messageOS)) { ContextualDiagnosticSeverity = severity; ContextualDiagnosticMessage = NullTerminatedStringRef(message, Allocator); } } } assert(ContextFreeResult != nullptr && "ContextFreeResult should have been constructed by the switch above"); CodeCompletionResult *result = new (Allocator) CodeCompletionResult( *ContextFreeResult, SemanticContext, Flair, NumBytesToErase, TypeContext, DC, ContextualNotRecReason, ContextualDiagnosticSeverity, ContextualDiagnosticMessage); return result; } void CodeCompletionResultBuilder::finishResult() { if (!Cancelled) Sink.Results.push_back(takeResult()); } std::vector CodeCompletionContext::sortCompletionResults( ArrayRef Results) { std::vector SortedResults(Results.begin(), Results.end()); std::sort(SortedResults.begin(), SortedResults.end(), [](const auto &LHS, const auto &RHS) { int Result = StringRef(LHS->getFilterName()) .compare_insensitive(RHS->getFilterName()); // If the case insensitive comparison is equal, then secondary // sort order should be case sensitive. if (Result == 0) Result = LHS->getFilterName().compare(RHS->getFilterName()); return Result < 0; }); return SortedResults; } namespace { class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks { CodeCompletionContext &CompletionContext; CodeCompletionConsumer &Consumer; CodeCompletionExpr *CodeCompleteTokenExpr = nullptr; CompletionKind Kind = CompletionKind::None; Expr *ParsedExpr = nullptr; SourceLoc DotLoc; TypeLoc ParsedTypeLoc; DeclContext *CurDeclContext = nullptr; DeclAttrKind AttrKind; /// In situations when \c SyntaxKind hints or determines /// completions, i.e. a precedence group attribute, this /// can be set and used to control the code completion scenario. SyntaxKind SyntxKind; int AttrParamIndex; bool IsInSil = false; bool HasSpace = false; bool ShouldCompleteCallPatternAfterParen = true; bool PreferFunctionReferencesToCalls = false; bool AttTargetIsIndependent = false; bool IsAtStartOfLine = false; Optional AttTargetDK; Optional ParentStmtKind; SmallVector ParsedKeywords; SourceLoc introducerLoc; std::vector> 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()) return; CodeCompletionResultBuilder Builder(Sink, CodeCompletionResultKind::Keyword, SemanticContextKind::CurrentNominal, {}); if (auto *AFD = dyn_cast(CurDeclContext)) { if (AFD->getOverriddenDecl() != nullptr) { Builder.addFlair(CodeCompletionFlairBit::CommonKeywordAtCurrentPosition); } } Builder.setKeywordKind(CodeCompletionKeywordKind::kw_super); Builder.addKeyword("super"); Builder.addTypeAnnotation(ST, PrintOptions()); } Optional> 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()) { return getReferencedDecl(ParsedExpr); } ConcreteDeclRef ReferencedDecl = nullptr; Expr *ModifiedExpr = ParsedExpr; if (auto T = getTypeOfCompletionContextExpr(P.Context, CurDeclContext, CheckKind, ModifiedExpr, ReferencedDecl)) { // FIXME: even though we don't apply the solution, the type checker may // modify the original expression. We should understand what effect that // may have on code completion. ParsedExpr = ModifiedExpr; return std::make_pair(*T, ReferencedDecl); } return None; } /// \returns true on success, false on failure. bool typecheckParsedType() { assert(ParsedTypeLoc.getTypeRepr() && "should have a TypeRepr"); if (ParsedTypeLoc.wasValidated() && !ParsedTypeLoc.isError()) { return true; } const auto ty = swift::performTypeResolution( ParsedTypeLoc.getTypeRepr(), P.Context, /*isSILMode=*/false, /*isSILType=*/false, CurDeclContext->getGenericEnvironmentOfContext(), /*GenericParams=*/nullptr, CurDeclContext, /*ProduceDiagnostics=*/false); if (!ty->hasError()) { ParsedTypeLoc.setType(CurDeclContext->mapTypeIntoContext(ty)); return true; } ParsedTypeLoc.setType(ty); // It doesn't type check as a type, so see if it's a qualifying module name. if (auto *ITR = dyn_cast(ParsedTypeLoc.getTypeRepr())) { const auto &componentRange = ITR->getComponentRange(); // If it has more than one component, it can't be a module name. if (std::distance(componentRange.begin(), componentRange.end()) != 1) return false; const auto &component = componentRange.front(); ImportPath::Module::Builder builder( component->getNameRef().getBaseIdentifier(), component->getLoc()); if (auto Module = Context.getLoadedModule(builder.get())) ParsedTypeLoc.setType(ModuleType::get(Module)); return true; } return false; } public: CodeCompletionCallbacksImpl(Parser &P, CodeCompletionContext &CompletionContext, CodeCompletionConsumer &Consumer) : CodeCompletionCallbacks(P), CompletionContext(CompletionContext), Consumer(Consumer) { } void setAttrTargetDeclKind(Optional DK) override { if (DK == DeclKind::PatternBinding) DK = DeclKind::Var; else if (DK == DeclKind::Param) // For params, consider the attribute is always for the decl. AttTargetIsIndependent = false; if (!AttTargetIsIndependent) AttTargetDK = DK; } void completeDotExpr(CodeCompletionExpr *E, SourceLoc DotLoc) override; void completeStmtOrExpr(CodeCompletionExpr *E) override; void completePostfixExprBeginning(CodeCompletionExpr *E) override; void completeForEachSequenceBeginning(CodeCompletionExpr *E) override; void completePostfixExpr(Expr *E, bool hasSpace) override; void completePostfixExprParen(Expr *E, Expr *CodeCompletionE) override; void completeExprKeyPath(KeyPathExpr *KPE, SourceLoc DotLoc) override; void completeTypeDeclResultBeginning() override; void completeTypeSimpleBeginning() override; void completeTypeIdentifierWithDot(IdentTypeRepr *ITR) override; void completeTypeIdentifierWithoutDot(IdentTypeRepr *ITR) override; void completeCaseStmtKeyword() override; void completeCaseStmtBeginning(CodeCompletionExpr *E) override; void completeDeclAttrBeginning(bool Sil, bool isIndependent) override; void completeDeclAttrParam(DeclAttrKind DK, int Index) override; void completeEffectsSpecifier(bool hasAsync, bool hasThrows) override; void completeInPrecedenceGroup(SyntaxKind SK) override; void completeNominalMemberBeginning( SmallVectorImpl &Keywords, SourceLoc introducerLoc) override; void completeAccessorBeginning(CodeCompletionExpr *E) override; void completePoundAvailablePlatform() override; void completeImportDecl(ImportPath::Builder &Path) override; void completeUnresolvedMember(CodeCompletionExpr *E, SourceLoc DotLoc) override; void completeCallArg(CodeCompletionExpr *E, bool isFirst) override; void completeLabeledTrailingClosure(CodeCompletionExpr *E, bool isAtStartOfLine) override; bool canPerformCompleteLabeledTrailingClosure() const override { return true; } void completeReturnStmt(CodeCompletionExpr *E) override; void completeYieldStmt(CodeCompletionExpr *E, Optional yieldIndex) override; void completeAfterPoundExpr(CodeCompletionExpr *E, Optional ParentKind) override; void completeAfterPoundDirective() override; void completePlatformCondition() override; void completeGenericRequirement() override; void completeAfterIfStmt(bool hasElse) override; void completeStmtLabel(StmtKind ParentKind) override; void completeForEachPatternBeginning(bool hasTry, bool hasAwait) override; void completeTypeAttrBeginning() override; void doneParsing() override; private: void addKeywords(CodeCompletionResultSink &Sink, bool MaybeFuncBody); bool trySolverCompletion(bool MaybeFuncBody); }; } // end anonymous namespace namespace { class CompletionOverrideLookup : public swift::VisibleDeclConsumer { CodeCompletionResultSink &Sink; ASTContext &Ctx; const DeclContext *CurrDeclContext; SmallVectorImpl &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 &ParsedKeywords, SourceLoc introducerLoc) : Sink(Sink), Ctx(Ctx), CurrDeclContext(CurrDeclContext), ParsedKeywords(ParsedKeywords), introducerLoc(introducerLoc) { hasFuncIntroducer = isKeywordSpecified("func"); hasVarIntroducer = isKeywordSpecified("var") || isKeywordSpecified("let"); hasTypealiasIntroducer = isKeywordSpecified("typealias"); hasInitializerModifier = isKeywordSpecified("required") || isKeywordSpecified("convenience"); hasAccessModifier = isKeywordSpecified("private") || isKeywordSpecified("fileprivate") || isKeywordSpecified("internal") || isKeywordSpecified("public") || isKeywordSpecified("open"); hasOverride = isKeywordSpecified("override"); hasOverridabilityModifier = isKeywordSpecified("final") || isKeywordSpecified("open"); hasStaticOrClass = isKeywordSpecified(getTokenText(tok::kw_class)) || isKeywordSpecified(getTokenText(tok::kw_static)); } bool isKeywordSpecified(StringRef Word) { return std::find(ParsedKeywords.begin(), ParsedKeywords.end(), Word) != ParsedKeywords.end(); } bool missingOverride(DeclVisibilityKind Reason) { return !hasOverride && Reason == DeclVisibilityKind::MemberOfSuper && !CurrDeclContext->getSelfProtocolDecl(); } /// Add an access modifier (i.e. `public`) to \p Builder is necessary. /// Returns \c true if the modifier is actually added, \c false otherwise. bool addAccessControl(const ValueDecl *VD, CodeCompletionResultBuilder &Builder) { auto CurrentNominal = CurrDeclContext->getSelfNominalTypeDecl(); assert(CurrentNominal); auto AccessOfContext = CurrentNominal->getFormalAccess(); if (AccessOfContext < AccessLevel::Public) return false; auto Access = VD->getFormalAccess(); // Use the greater access between the protocol requirement and the witness. // In case of: // // public protocol P { func foo() } // public class B { func foo() {} } // public class C: B, P { // // } // // 'VD' is 'B.foo()' which is implicitly 'internal'. But as the overriding // declaration, the user needs to write both 'public' and 'override': // // public class C: B { // public override func foo() {} // } if (Access < AccessLevel::Public && !isa(VD->getDeclContext())) { for (auto Conformance : CurrentNominal->getAllConformances()) { Conformance->getRootConformance()->forEachValueWitness( [&](ValueDecl *req, Witness witness) { if (witness.getDecl() == VD) Access = std::max( Access, Conformance->getProtocol()->getFormalAccess()); }); } } Access = std::min(Access, AccessOfContext); // Only emit 'public', not needed otherwise. if (Access < AccessLevel::Public) return false; Builder.addAccessControlKeyword(Access); return true; } /// Return type if the result type if \p VD should be represented as opaque /// result type. Type getOpaqueResultType(const ValueDecl *VD, DeclVisibilityKind Reason, DynamicLookupInfo dynamicLookupInfo) { if (Reason != DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal) return nullptr; auto currTy = CurrDeclContext->getDeclaredTypeInContext(); if (!currTy) return nullptr; Type ResultT; if (auto *FD = dyn_cast(VD)) { if (FD->getGenericParams()) { // Generic function cannot have opaque result type. return nullptr; } ResultT = FD->getResultInterfaceType(); } else if (auto *SD = dyn_cast(VD)) { if (SD->getGenericParams()) { // Generic subscript cannot have opaque result type. return nullptr; } ResultT = SD->getElementInterfaceType(); } else if (auto *VarD = dyn_cast(VD)) { ResultT = VarD->getInterfaceType(); } else { return nullptr; } if (!ResultT->is() || !ResultT->castTo()->getAssocType()) // The result is not a valid associatedtype. return nullptr; // Try substitution to see if the associated type is resolved to concrete // type. auto substMap = currTy->getMemberSubstitutionMap( CurrDeclContext->getParentModule(), VD); if (!ResultT.subst(substMap)->is()) // If resolved print it. return nullptr; auto genericSig = VD->getDeclContext()->getGenericSignatureOfContext(); if (genericSig->isConcreteType(ResultT)) // If it has same type requrement, we will emit the concrete type. return nullptr; // Collect requirements on the associatedtype. SmallVector opaqueTypes; bool hasExplicitAnyObject = false; if (auto superTy = genericSig->getSuperclassBound(ResultT)) opaqueTypes.push_back(superTy); for (const auto proto : genericSig->getRequiredProtocols(ResultT)) opaqueTypes.push_back(proto->getDeclaredInterfaceType()); if (auto layout = genericSig->getLayoutConstraint(ResultT)) hasExplicitAnyObject = layout->isClass(); if (!hasExplicitAnyObject) { if (opaqueTypes.empty()) return nullptr; if (opaqueTypes.size() == 1) return opaqueTypes.front(); } return ProtocolCompositionType::get( VD->getASTContext(), opaqueTypes, hasExplicitAnyObject); } void addValueOverride(const ValueDecl *VD, DeclVisibilityKind Reason, DynamicLookupInfo dynamicLookupInfo, CodeCompletionResultBuilder &Builder, bool hasDeclIntroducer) { Type opaqueResultType = getOpaqueResultType(VD, Reason, dynamicLookupInfo); class DeclPrinter : public CodeCompletionStringPrinter { Type OpaqueBaseTy; public: DeclPrinter(CodeCompletionResultBuilder &Builder, Type OpaqueBaseTy) : CodeCompletionStringPrinter(Builder), OpaqueBaseTy(OpaqueBaseTy) { } // As for FuncDecl, SubscriptDecl, and VarDecl, substitute the result type // with 'OpaqueBaseTy' if specified. void printDeclResultTypePre(ValueDecl *VD, TypeLoc &TL) override { if (!OpaqueBaseTy.isNull()) { setNextChunkKind(ChunkKind::Keyword); printText("some "); setNextChunkKind(ChunkKind::Text); TL = TypeLoc::withoutLoc(OpaqueBaseTy); } CodeCompletionStringPrinter::printDeclResultTypePre(VD, TL); } }; DeclPrinter Printer(Builder, opaqueResultType); Printer.startPreamble(); bool modifierAdded = false; // 'public' if needed. modifierAdded |= !hasAccessModifier && addAccessControl(VD, Builder); // 'override' if needed if (missingOverride(Reason)) { Builder.addOverrideKeyword(); modifierAdded |= true; } // Erase existing introducer (e.g. 'func') if any modifiers are added. if (hasDeclIntroducer && modifierAdded) { auto dist = Ctx.SourceMgr.getByteDistance( introducerLoc, Ctx.SourceMgr.getCodeCompletionLoc()); if (dist <= CodeCompletionResult::MaxNumBytesToErase) { Builder.setNumBytesToErase(dist); hasDeclIntroducer = false; } } PrintOptions PO; if (auto transformType = CurrDeclContext->getDeclaredTypeInContext()) PO.setBaseType(transformType); PO.PrintPropertyAccessors = false; PO.PrintSubscriptAccessors = false; PO.SkipUnderscoredKeywords = true; PO.PrintImplicitAttrs = false; PO.ExclusiveAttrList.push_back(TAK_escaping); PO.ExclusiveAttrList.push_back(TAK_autoclosure); // Print certain modifiers only when the introducer is not written. // Otherwise, the user can add it after the completion. if (!hasDeclIntroducer) { PO.ExclusiveAttrList.push_back(DAK_Nonisolated); } PO.PrintAccess = false; PO.PrintOverrideKeyword = false; PO.PrintSelfAccessKindKeyword = false; PO.PrintStaticKeyword = !hasStaticOrClass && !hasDeclIntroducer; PO.SkipIntroducerKeywords = hasDeclIntroducer; VD->print(Printer, PO); } void addMethodOverride(const FuncDecl *FD, DeclVisibilityKind Reason, DynamicLookupInfo dynamicLookupInfo) { CodeCompletionResultBuilder Builder(Sink, CodeCompletionResultKind::Declaration, SemanticContextKind::Super, {}); Builder.setResultTypeNotApplicable(); 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, CodeCompletionResultKind::Declaration, SemanticContextKind::Super, {}); Builder.setAssociatedDecl(VD); addValueOverride(VD, Reason, dynamicLookupInfo, Builder, hasVarIntroducer); } void addSubscriptOverride(const SubscriptDecl *SD, DeclVisibilityKind Reason, DynamicLookupInfo dynamicLookupInfo) { CodeCompletionResultBuilder Builder(Sink, CodeCompletionResultKind::Declaration, SemanticContextKind::Super, {}); Builder.setResultTypeNotApplicable(); Builder.setAssociatedDecl(SD); addValueOverride(SD, Reason, dynamicLookupInfo, Builder, false); Builder.addBraceStmtWithCursor(); } void addTypeAlias(const AssociatedTypeDecl *ATD, DeclVisibilityKind Reason, DynamicLookupInfo dynamicLookupInfo) { CodeCompletionResultBuilder Builder(Sink, CodeCompletionResultKind::Declaration, SemanticContextKind::Super, {}); Builder.setResultTypeNotApplicable(); Builder.setAssociatedDecl(ATD); if (!hasTypealiasIntroducer && !hasAccessModifier) (void)addAccessControl(ATD, Builder); if (!hasTypealiasIntroducer) Builder.addDeclIntroducer("typealias "); Builder.addBaseName(ATD->getName().str()); Builder.addTextChunk(" = "); Builder.addSimpleNamedParameter("Type"); } void addConstructor(const ConstructorDecl *CD, DeclVisibilityKind Reason, DynamicLookupInfo dynamicLookupInfo) { CodeCompletionResultBuilder Builder(Sink, CodeCompletionResultKind::Declaration, SemanticContextKind::Super, {}); Builder.setResultTypeNotApplicable(); Builder.setAssociatedDecl(CD); CodeCompletionStringPrinter printer(Builder); printer.startPreamble(); if (!hasAccessModifier) (void)addAccessControl(CD, Builder); if (missingOverride(Reason) && CD->isDesignatedInit() && !CD->isRequired()) Builder.addOverrideKeyword(); // Emit 'required' if we're in class context, 'required' is not specified, // and 1) this is a protocol conformance and the class is not final, or 2) // this is subclass and the initializer is marked as required. bool needRequired = false; auto C = CurrDeclContext->getSelfClassDecl(); if (C && !isKeywordSpecified("required")) { switch (Reason) { case DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal: case DeclVisibilityKind::MemberOfProtocolDerivedByCurrentNominal: if (!C->isSemanticallyFinal()) needRequired = true; break; case DeclVisibilityKind::MemberOfSuper: if (CD->isRequired()) needRequired = true; break; default: break; } } if (needRequired) Builder.addRequiredKeyword(); { PrintOptions Options; if (auto transformType = CurrDeclContext->getDeclaredTypeInContext()) Options.setBaseType(transformType); Options.PrintImplicitAttrs = false; Options.SkipAttributes = true; CD->print(printer, Options); } printer.flush(); 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->isSemanticallyFinal()) 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(D)) { // We cannot override operators as members. if (FD->isBinaryOperator() || FD->isUnaryOperator()) return; // We cannot override individual accessors. if (isa(FD)) return; if (hasFuncIntroducer || (!hasIntroducer && !hasInitializerModifier)) addMethodOverride(FD, Reason, dynamicLookupInfo); return; } if (auto *VD = dyn_cast(D)) { if (hasVarIntroducer || (!hasIntroducer && !hasInitializerModifier)) addVarOverride(VD, Reason, dynamicLookupInfo); return; } if (auto *SD = dyn_cast(D)) { if (!hasIntroducer && !hasInitializerModifier) addSubscriptOverride(SD, Reason, dynamicLookupInfo); } if (auto *CD = dyn_cast(D)) { if (!isa(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(NTD); if (!CD) return; if (!CD->hasSuperclass()) return; CD = CD->getSuperclassDecl(); for (const auto *Member : CD->getMembers()) { const auto *Constructor = dyn_cast(Member); if (!Constructor) continue; if (Constructor->hasStubImplementation()) continue; if (Constructor->isDesignatedInit()) addConstructor(Constructor, DeclVisibilityKind::MemberOfSuper, {}); } } void addAssociatedTypes(NominalTypeDecl *NTD) { if (!hasTypealiasIntroducer && (hasFuncIntroducer || hasVarIntroducer || hasInitializerModifier || hasOverride || hasOverridabilityModifier || hasStaticOrClass)) return; for (auto Conformance : NTD->getAllConformances()) { auto Proto = Conformance->getProtocol(); if (!Proto->isAccessibleFrom(CurrDeclContext)) continue; for (auto *ATD : Proto->getAssociatedTypeMembers()) { // FIXME: Also exclude the type alias that has already been specified. if (!Conformance->hasTypeWitness(ATD) || ATD->hasDefaultDefinitionType()) continue; addTypeAlias( ATD, DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal, {}); } } } static StringRef getResultBuilderDocComment( ResultBuilderBuildFunction function) { switch (function) { case ResultBuilderBuildFunction::BuildArray: return "Enables support for..in loops in a result builder by " "combining the results of all iterations into a single result"; case ResultBuilderBuildFunction::BuildBlock: return "Required by every result builder to build combined results " "from statement blocks"; case ResultBuilderBuildFunction::BuildEitherFirst: return "With buildEither(second:), enables support for 'if-else' and " "'switch' statements by folding conditional results into a single " "result"; case ResultBuilderBuildFunction::BuildEitherSecond: return "With buildEither(first:), enables support for 'if-else' and " "'switch' statements by folding conditional results into a single " "result"; case ResultBuilderBuildFunction::BuildExpression: return "If declared, provides contextual type information for statement " "expressions to translate them into partial results"; case ResultBuilderBuildFunction::BuildFinalResult: return "If declared, this will be called on the partial result from the " "outermost block statement to produce the final returned result"; case ResultBuilderBuildFunction::BuildLimitedAvailability: return "If declared, this will be called on the partial result of " "an 'if #available' block to allow the result builder to erase " "type information"; case ResultBuilderBuildFunction::BuildOptional: return "Enables support for `if` statements that do not have an `else`"; } } void addResultBuilderBuildCompletion( NominalTypeDecl *builder, Type componentType, ResultBuilderBuildFunction function) { CodeCompletionResultBuilder Builder(Sink, CodeCompletionResultKind::Pattern, SemanticContextKind::CurrentNominal, {}); Builder.setResultTypeNotApplicable(); if (!hasFuncIntroducer) { if (!hasAccessModifier && builder->getFormalAccess() >= AccessLevel::Public) Builder.addAccessControlKeyword(AccessLevel::Public); if (!hasStaticOrClass) Builder.addTextChunk("static "); Builder.addTextChunk("func "); } std::string declStringWithoutFunc; { llvm::raw_string_ostream out(declStringWithoutFunc); printResultBuilderBuildFunction( builder, componentType, function, None, out); } Builder.addTextChunk(declStringWithoutFunc); Builder.addBraceStmtWithCursor(); Builder.setBriefDocComment(getResultBuilderDocComment(function)); } /// Add completions for the various "build" functions in a result builder. void addResultBuilderBuildCompletions(NominalTypeDecl *builder) { Type componentType = inferResultBuilderComponentType(builder); addResultBuilderBuildCompletion( builder, componentType, ResultBuilderBuildFunction::BuildBlock); addResultBuilderBuildCompletion( builder, componentType, ResultBuilderBuildFunction::BuildExpression); addResultBuilderBuildCompletion( builder, componentType, ResultBuilderBuildFunction::BuildOptional); addResultBuilderBuildCompletion( builder, componentType, ResultBuilderBuildFunction::BuildEitherFirst); addResultBuilderBuildCompletion( builder, componentType, ResultBuilderBuildFunction::BuildEitherSecond); addResultBuilderBuildCompletion( builder, componentType, ResultBuilderBuildFunction::BuildArray); addResultBuilderBuildCompletion( builder, componentType, ResultBuilderBuildFunction::BuildLimitedAvailability); addResultBuilderBuildCompletion( builder, componentType, ResultBuilderBuildFunction::BuildFinalResult); } void getOverrideCompletions(SourceLoc Loc) { if (!CurrDeclContext->isTypeContext()) return; if (isa(CurrDeclContext)) return; Type CurrTy = CurrDeclContext->getSelfTypeInContext(); auto *NTD = CurrDeclContext->getSelfNominalTypeDecl(); if (CurrTy && !CurrTy->is()) { // Look for overridable static members too. Type Meta = MetatypeType::get(CurrTy); lookupVisibleMemberDecls(*this, Meta, CurrDeclContext, /*includeInstanceMembers=*/true, /*includeDerivedRequirements*/true, /*includeProtocolExtensionMembers*/false); addDesignatedInitializers(NTD); addAssociatedTypes(NTD); } if (NTD && NTD->getAttrs().hasAttribute()) { addResultBuilderBuildCompletions(NTD); } } }; } // end anonymous namespace static void addSelectorModifierKeywords(CodeCompletionResultSink &sink) { auto addKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind) { CodeCompletionResultBuilder Builder(sink, CodeCompletionResultKind::Keyword, SemanticContextKind::None, {}); Builder.setKeywordKind(Kind); Builder.addTextChunk(Name); Builder.addCallParameterColon(); Builder.addSimpleTypedParameter("@objc property", /*IsVarArg=*/false); }; addKeyword("getter", CodeCompletionKeywordKind::None); addKeyword("setter", CodeCompletionKeywordKind::None); } void CodeCompletionCallbacksImpl::completeDotExpr(CodeCompletionExpr *E, SourceLoc DotLoc) { assert(P.Tok.is(tok::code_complete)); // Don't produce any results in an enum element. if (InEnumElementRawValue) return; Kind = CompletionKind::DotExpr; if (ParseExprSelectorContext != ObjCSelectorContext::None) { PreferFunctionReferencesToCalls = true; CompleteExprSelectorContext = ParseExprSelectorContext; } ParsedExpr = E->getBase(); this->DotLoc = DotLoc; CurDeclContext = P.CurDeclContext; CodeCompleteTokenExpr = E; } void CodeCompletionCallbacksImpl::completeStmtOrExpr(CodeCompletionExpr *E) { assert(P.Tok.is(tok::code_complete)); Kind = CompletionKind::StmtOrExpr; CurDeclContext = P.CurDeclContext; CodeCompleteTokenExpr = E; } void CodeCompletionCallbacksImpl::completePostfixExprBeginning(CodeCompletionExpr *E) { assert(P.Tok.is(tok::code_complete)); // Don't produce any results in an enum element. if (InEnumElementRawValue) return; Kind = CompletionKind::PostfixExprBeginning; if (ParseExprSelectorContext != ObjCSelectorContext::None) { PreferFunctionReferencesToCalls = true; CompleteExprSelectorContext = ParseExprSelectorContext; if (CompleteExprSelectorContext == ObjCSelectorContext::MethodSelector) { addSelectorModifierKeywords(CompletionContext.getResultSink()); } } CurDeclContext = P.CurDeclContext; CodeCompleteTokenExpr = E; } void CodeCompletionCallbacksImpl::completeForEachSequenceBeginning( CodeCompletionExpr *E) { assert(P.Tok.is(tok::code_complete)); Kind = CompletionKind::ForEachSequence; CurDeclContext = P.CurDeclContext; CodeCompleteTokenExpr = E; } void CodeCompletionCallbacksImpl::completePostfixExpr(Expr *E, bool hasSpace) { assert(P.Tok.is(tok::code_complete)); // Don't produce any results in an enum element. if (InEnumElementRawValue) return; HasSpace = hasSpace; Kind = CompletionKind::PostfixExpr; if (ParseExprSelectorContext != ObjCSelectorContext::None) { PreferFunctionReferencesToCalls = true; CompleteExprSelectorContext = ParseExprSelectorContext; } ParsedExpr = E; CurDeclContext = P.CurDeclContext; } void CodeCompletionCallbacksImpl::completePostfixExprParen(Expr *E, Expr *CodeCompletionE) { assert(P.Tok.is(tok::code_complete)); // Don't produce any results in an enum element. if (InEnumElementRawValue) return; Kind = CompletionKind::PostfixExprParen; ParsedExpr = E; CurDeclContext = P.CurDeclContext; CodeCompleteTokenExpr = static_cast(CodeCompletionE); ShouldCompleteCallPatternAfterParen = true; if (CompletionContext.getCallPatternHeuristics()) { // Lookahead one token to decide what kind of call completions to provide. // When it appears that there is already code for the call present, just // complete values and/or argument labels. Otherwise give the entire call // pattern. Token next = P.peekToken(); if (!next.isAtStartOfLine() && !next.is(tok::eof) && !next.is(tok::r_paren)) { ShouldCompleteCallPatternAfterParen = false; } } } void CodeCompletionCallbacksImpl::completeExprKeyPath(KeyPathExpr *KPE, SourceLoc DotLoc) { Kind = (!KPE || KPE->isObjC()) ? CompletionKind::KeyPathExprObjC : CompletionKind::KeyPathExprSwift; ParsedExpr = KPE; this->DotLoc = DotLoc; CurDeclContext = P.CurDeclContext; } void CodeCompletionCallbacksImpl::completePoundAvailablePlatform() { Kind = CompletionKind::PoundAvailablePlatform; CurDeclContext = P.CurDeclContext; } void CodeCompletionCallbacksImpl::completeTypeDeclResultBeginning() { Kind = CompletionKind::TypeDeclResultBeginning; CurDeclContext = P.CurDeclContext; } void CodeCompletionCallbacksImpl::completeTypeSimpleBeginning() { Kind = CompletionKind::TypeSimpleBeginning; CurDeclContext = P.CurDeclContext; } void CodeCompletionCallbacksImpl::completeDeclAttrParam(DeclAttrKind DK, int Index) { Kind = CompletionKind::AttributeDeclParen; AttrKind = DK; AttrParamIndex = Index; CurDeclContext = P.CurDeclContext; } void CodeCompletionCallbacksImpl::completeEffectsSpecifier(bool hasAsync, bool hasThrows) { Kind = CompletionKind::EffectsSpecifier; CurDeclContext = P.CurDeclContext; ParsedKeywords.clear(); if (hasAsync) ParsedKeywords.emplace_back("async"); if (hasThrows) ParsedKeywords.emplace_back("throws"); } void CodeCompletionCallbacksImpl::completeDeclAttrBeginning( bool Sil, bool isIndependent) { Kind = CompletionKind::AttributeBegin; IsInSil = Sil; CurDeclContext = P.CurDeclContext; AttTargetIsIndependent = isIndependent; } void CodeCompletionCallbacksImpl::completeInPrecedenceGroup(SyntaxKind SK) { assert(P.Tok.is(tok::code_complete)); SyntxKind = SK; Kind = CompletionKind::PrecedenceGroup; CurDeclContext = P.CurDeclContext; } void CodeCompletionCallbacksImpl::completeTypeIdentifierWithDot( IdentTypeRepr *ITR) { if (!ITR) { completeTypeSimpleBeginning(); return; } Kind = CompletionKind::TypeIdentifierWithDot; ParsedTypeLoc = TypeLoc(ITR); CurDeclContext = P.CurDeclContext; } void CodeCompletionCallbacksImpl::completeTypeIdentifierWithoutDot( IdentTypeRepr *ITR) { assert(ITR); Kind = CompletionKind::TypeIdentifierWithoutDot; ParsedTypeLoc = TypeLoc(ITR); CurDeclContext = P.CurDeclContext; } void CodeCompletionCallbacksImpl::completeCaseStmtKeyword() { Kind = CompletionKind::CaseStmtKeyword; CurDeclContext = P.CurDeclContext; } void CodeCompletionCallbacksImpl::completeCaseStmtBeginning(CodeCompletionExpr *E) { assert(!InEnumElementRawValue); Kind = CompletionKind::CaseStmtBeginning; CurDeclContext = P.CurDeclContext; CodeCompleteTokenExpr = E; } void CodeCompletionCallbacksImpl::completeImportDecl( ImportPath::Builder &Path) { Kind = CompletionKind::Import; CurDeclContext = P.CurDeclContext; DotLoc = Path.empty() ? SourceLoc() : Path.back().Loc; if (DotLoc.isInvalid()) return; auto Importer = static_cast(CurDeclContext->getASTContext(). getClangModuleLoader()); std::vector SubNames; Importer->collectSubModuleNames(Path.get().getModulePath(false), SubNames); ASTContext &Ctx = CurDeclContext->getASTContext(); Path.push_back(Identifier()); for (StringRef Sub : SubNames) { Path.back().Item = Ctx.getIdentifier(Sub); SubModuleNameVisibilityPairs.push_back( std::make_pair(Sub.str(), Ctx.getLoadedModule(Path.get().getModulePath(false)))); } Path.pop_back(); } void CodeCompletionCallbacksImpl::completeUnresolvedMember(CodeCompletionExpr *E, SourceLoc DotLoc) { Kind = CompletionKind::UnresolvedMember; CurDeclContext = P.CurDeclContext; CodeCompleteTokenExpr = E; this->DotLoc = DotLoc; } void CodeCompletionCallbacksImpl::completeCallArg(CodeCompletionExpr *E, bool isFirst) { CurDeclContext = P.CurDeclContext; CodeCompleteTokenExpr = E; Kind = CompletionKind::CallArg; ShouldCompleteCallPatternAfterParen = false; if (isFirst) { ShouldCompleteCallPatternAfterParen = true; if (CompletionContext.getCallPatternHeuristics()) { // Lookahead one token to decide what kind of call completions to provide. // When it appears that there is already code for the call present, just // complete values and/or argument labels. Otherwise give the entire call // pattern. Token next = P.peekToken(); if (!next.isAtStartOfLine() && !next.is(tok::eof) && !next.is(tok::r_paren)) { ShouldCompleteCallPatternAfterParen = false; } } } } void CodeCompletionCallbacksImpl::completeLabeledTrailingClosure( CodeCompletionExpr *E, bool isAtStartOfLine) { CurDeclContext = P.CurDeclContext; CodeCompleteTokenExpr = E; Kind = CompletionKind::LabeledTrailingClosure; IsAtStartOfLine = isAtStartOfLine; } void CodeCompletionCallbacksImpl::completeReturnStmt(CodeCompletionExpr *E) { CurDeclContext = P.CurDeclContext; CodeCompleteTokenExpr = E; Kind = CompletionKind::ReturnStmtExpr; } void CodeCompletionCallbacksImpl::completeYieldStmt(CodeCompletionExpr *E, Optional 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 ParentKind) { CurDeclContext = P.CurDeclContext; CodeCompleteTokenExpr = E; Kind = CompletionKind::AfterPoundExpr; ParentStmtKind = ParentKind; } void CodeCompletionCallbacksImpl::completeAfterPoundDirective() { CurDeclContext = P.CurDeclContext; Kind = CompletionKind::AfterPoundDirective; } void CodeCompletionCallbacksImpl::completePlatformCondition() { CurDeclContext = P.CurDeclContext; Kind = CompletionKind::PlatformConditon; } void CodeCompletionCallbacksImpl::completeAfterIfStmt(bool hasElse) { CurDeclContext = P.CurDeclContext; if (hasElse) { Kind = CompletionKind::AfterIfStmtElse; } else { Kind = CompletionKind::StmtOrExpr; } } void CodeCompletionCallbacksImpl::completeGenericRequirement() { CurDeclContext = P.CurDeclContext; Kind = CompletionKind::GenericRequirement; } void CodeCompletionCallbacksImpl::completeNominalMemberBeginning( SmallVectorImpl &Keywords, SourceLoc introducerLoc) { assert(!InEnumElementRawValue); this->introducerLoc = introducerLoc; ParsedKeywords.clear(); ParsedKeywords.append(Keywords.begin(), Keywords.end()); Kind = CompletionKind::NominalMemberBeginning; CurDeclContext = P.CurDeclContext; } void CodeCompletionCallbacksImpl::completeAccessorBeginning( CodeCompletionExpr *E) { Kind = CompletionKind::AccessorBeginning; CurDeclContext = P.CurDeclContext; CodeCompleteTokenExpr = E; } void CodeCompletionCallbacksImpl::completeStmtLabel(StmtKind ParentKind) { CurDeclContext = P.CurDeclContext; Kind = CompletionKind::StmtLabel; ParentStmtKind = ParentKind; } void CodeCompletionCallbacksImpl::completeForEachPatternBeginning( bool hasTry, bool hasAwait) { CurDeclContext = P.CurDeclContext; Kind = CompletionKind::ForEachPatternBeginning; ParsedKeywords.clear(); if (hasTry) ParsedKeywords.emplace_back("try"); if (hasAwait) ParsedKeywords.emplace_back("await"); } void CodeCompletionCallbacksImpl::completeTypeAttrBeginning() { CurDeclContext = P.CurDeclContext; Kind = CompletionKind::TypeAttrBeginning; } 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 = "", CodeCompletionFlair Flair = {}) { CodeCompletionResultBuilder Builder(Sink, CodeCompletionResultKind::Keyword, SemanticContextKind::None, {}); Builder.setKeywordKind(Kind); Builder.addKeyword(Name); Builder.addFlair(Flair); if (!TypeAnnotation.empty()) Builder.addTypeAnnotation(TypeAnnotation); Builder.setResultTypeNotApplicable(); } static void addDeclKeywords(CodeCompletionResultSink &Sink, DeclContext *DC, bool IsConcurrencyEnabled, bool IsDistributedEnabled) { auto isTypeDeclIntroducer = [](CodeCompletionKeywordKind Kind, Optional DAK) -> bool { switch (Kind) { case CodeCompletionKeywordKind::kw_protocol: case CodeCompletionKeywordKind::kw_class: case CodeCompletionKeywordKind::kw_struct: case CodeCompletionKeywordKind::kw_enum: case CodeCompletionKeywordKind::kw_extension: return true; case CodeCompletionKeywordKind::None: if (DAK && *DAK == DeclAttrKind::DAK_Actor) { return true; } break; default: break; } return false; }; auto isTopLevelOnlyDeclIntroducer = [](CodeCompletionKeywordKind Kind, Optional DAK) -> bool { switch (Kind) { case CodeCompletionKeywordKind::kw_operator: case CodeCompletionKeywordKind::kw_precedencegroup: case CodeCompletionKeywordKind::kw_import: case CodeCompletionKeywordKind::kw_protocol: case CodeCompletionKeywordKind::kw_extension: return true; default: return false; } }; auto getFlair = [&](CodeCompletionKeywordKind Kind, Optional DAK) -> CodeCompletionFlair { if (isCodeCompletionAtTopLevelOfLibraryFile(DC)) { // Type decls are common in library file top-level. if (isTypeDeclIntroducer(Kind, DAK)) return CodeCompletionFlairBit::CommonKeywordAtCurrentPosition; } if (isa(DC)) { // Protocols cannot have nested type decls (other than 'typealias'). if (isTypeDeclIntroducer(Kind, DAK)) return CodeCompletionFlairBit::RareKeywordAtCurrentPosition; } if (DC->isTypeContext()) { // Top-level only decls are invalid in type context. if (isTopLevelOnlyDeclIntroducer(Kind, DAK)) return CodeCompletionFlairBit::RareKeywordAtCurrentPosition; } if (isCompletionDeclContextLocalContext(DC)) { // Local type decl are valid, but not common. if (isTypeDeclIntroducer(Kind, DAK)) return CodeCompletionFlairBit::RareKeywordAtCurrentPosition; // Top-level only decls are invalid in function body. if (isTopLevelOnlyDeclIntroducer(Kind, DAK)) return CodeCompletionFlairBit::RareKeywordAtCurrentPosition; // 'init', 'deinit' and 'subscript' are invalid in function body. // Access control modifiers are invalid in function body. switch (Kind) { case CodeCompletionKeywordKind::kw_init: case CodeCompletionKeywordKind::kw_deinit: case CodeCompletionKeywordKind::kw_subscript: case CodeCompletionKeywordKind::kw_private: case CodeCompletionKeywordKind::kw_fileprivate: case CodeCompletionKeywordKind::kw_internal: case CodeCompletionKeywordKind::kw_public: case CodeCompletionKeywordKind::kw_static: return CodeCompletionFlairBit::RareKeywordAtCurrentPosition; default: break; } // These modifiers are invalid for decls in function body. if (DAK) { switch (*DAK) { case DeclAttrKind::DAK_Lazy: case DeclAttrKind::DAK_Final: case DeclAttrKind::DAK_Infix: case DeclAttrKind::DAK_Frozen: case DeclAttrKind::DAK_Prefix: case DeclAttrKind::DAK_Postfix: case DeclAttrKind::DAK_Dynamic: case DeclAttrKind::DAK_Override: case DeclAttrKind::DAK_Optional: case DeclAttrKind::DAK_Required: case DeclAttrKind::DAK_Convenience: case DeclAttrKind::DAK_AccessControl: case DeclAttrKind::DAK_Nonisolated: return CodeCompletionFlairBit::RareKeywordAtCurrentPosition; default: break; } } } return None; }; auto AddDeclKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind, Optional DAK) { if (Name == "let" || Name == "var") { // Treat keywords that could be the start of a pattern specially. return; } // FIXME: This should use canUseAttributeOnDecl. // Remove user inaccessible keywords. if (DAK.hasValue() && DeclAttribute::isUserInaccessible(*DAK)) return; // Remove keywords only available when concurrency is enabled. if (DAK.hasValue() && !IsConcurrencyEnabled && DeclAttribute::isConcurrencyOnly(*DAK)) return; // Remove keywords only available when distributed is enabled. if (DAK.hasValue() && !IsDistributedEnabled && DeclAttribute::isDistributedOnly(*DAK)) return; addKeyword(Sink, Name, Kind, /*TypeAnnotation=*/"", getFlair(Kind, DAK)); }; #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 #undef CONTEXTUAL_CASE } static void addStmtKeywords(CodeCompletionResultSink &Sink, DeclContext *DC, bool MaybeFuncBody) { CodeCompletionFlair flair; // Starting a statement at top-level in non-script files is invalid. if (isCodeCompletionAtTopLevelOfLibraryFile(DC)) { flair |= CodeCompletionFlairBit::ExpressionAtNonScriptOrMainFileScope; } auto AddStmtKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind) { if (!MaybeFuncBody && Kind == CodeCompletionKeywordKind::kw_return) return; addKeyword(Sink, Name, Kind, "", flair); }; #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, DeclContext *DC) { // Expression is invalid at top-level of non-script files. CodeCompletionFlair flair; if (isCodeCompletionAtTopLevelOfLibraryFile(DC)) { flair |= CodeCompletionFlairBit::ExpressionAtNonScriptOrMainFileScope; } // Expr keywords. addKeyword(Sink, "try", CodeCompletionKeywordKind::kw_try, "", flair); addKeyword(Sink, "try!", CodeCompletionKeywordKind::kw_try, "", flair); addKeyword(Sink, "try?", CodeCompletionKeywordKind::kw_try, "", flair); addKeyword(Sink, "await", CodeCompletionKeywordKind::None, "", flair); } static void addOpaqueTypeKeyword(CodeCompletionResultSink &Sink) { addKeyword(Sink, "some", CodeCompletionKeywordKind::None, "some"); } static void addAnyTypeKeyword(CodeCompletionResultSink &Sink, Type T) { CodeCompletionResultBuilder Builder(Sink, CodeCompletionResultKind::Keyword, SemanticContextKind::None, {}); Builder.setKeywordKind(CodeCompletionKeywordKind::None); Builder.addKeyword("Any"); Builder.addTypeAnnotation(T, PrintOptions()); } void CodeCompletionCallbacksImpl::addKeywords(CodeCompletionResultSink &Sink, bool MaybeFuncBody) { switch (Kind) { case CompletionKind::None: case CompletionKind::DotExpr: case CompletionKind::AttributeDeclParen: case CompletionKind::AttributeBegin: case CompletionKind::PoundAvailablePlatform: case CompletionKind::Import: case CompletionKind::UnresolvedMember: case CompletionKind::LabeledTrailingClosure: case CompletionKind::AfterPoundExpr: case CompletionKind::AfterPoundDirective: case CompletionKind::PlatformConditon: case CompletionKind::GenericRequirement: case CompletionKind::KeyPathExprObjC: case CompletionKind::KeyPathExprSwift: case CompletionKind::PrecedenceGroup: case CompletionKind::StmtLabel: case CompletionKind::TypeAttrBeginning: break; case CompletionKind::EffectsSpecifier: { if (!llvm::is_contained(ParsedKeywords, "async")) addKeyword(Sink, "async", CodeCompletionKeywordKind::None); if (!llvm::is_contained(ParsedKeywords, "throws")) addKeyword(Sink, "throws", CodeCompletionKeywordKind::kw_throws); break; } case CompletionKind::AccessorBeginning: { // TODO: Omit already declared or mutally exclusive accessors. // E.g. If 'get' is already declared, emit 'set' only. addAccessorKeywords(Sink); // Only 'var' for non-protocol context can have 'willSet' and 'didSet'. assert(ParsedDecl); VarDecl *var = dyn_cast(ParsedDecl); if (auto accessor = dyn_cast(ParsedDecl)) var = dyn_cast(accessor->getStorage()); if (var && !var->getDeclContext()->getSelfProtocolDecl()) addObserverKeywords(Sink); if (!isa(ParsedDecl)) break; MaybeFuncBody = true; LLVM_FALLTHROUGH; } case CompletionKind::StmtOrExpr: addDeclKeywords(Sink, CurDeclContext, Context.LangOpts.EnableExperimentalConcurrency, Context.LangOpts.EnableExperimentalDistributed); addStmtKeywords(Sink, CurDeclContext, MaybeFuncBody); LLVM_FALLTHROUGH; case CompletionKind::ReturnStmtExpr: case CompletionKind::YieldStmtExpr: case CompletionKind::PostfixExprBeginning: case CompletionKind::ForEachSequence: addSuperKeyword(Sink); addLetVarKeywords(Sink); addExprKeywords(Sink, CurDeclContext); addAnyTypeKeyword(Sink, CurDeclContext->getASTContext().TheAnyType); break; case CompletionKind::CallArg: case CompletionKind::PostfixExprParen: // Note that we don't add keywords here as the completion might be for // an argument list pattern. We instead add keywords later in // CodeCompletionCallbacksImpl::doneParsing when we know we're not // completing for a argument list pattern. break; case CompletionKind::CaseStmtKeyword: addCaseStmtKeywords(Sink); break; case CompletionKind::PostfixExpr: case CompletionKind::CaseStmtBeginning: case CompletionKind::TypeIdentifierWithDot: case CompletionKind::TypeIdentifierWithoutDot: break; case CompletionKind::TypeDeclResultBeginning: { auto DC = CurDeclContext; if (ParsedDecl && ParsedDecl == CurDeclContext->getAsDecl()) DC = ParsedDecl->getDeclContext(); if (!isa(DC)) if (DC->isTypeContext() || isa_and_nonnull(ParsedDecl)) addOpaqueTypeKeyword(Sink); LLVM_FALLTHROUGH; } case CompletionKind::TypeSimpleBeginning: addAnyTypeKeyword(Sink, CurDeclContext->getASTContext().TheAnyType); break; case CompletionKind::NominalMemberBeginning: { bool HasDeclIntroducer = llvm::find_if(ParsedKeywords, [this](const StringRef kw) { return llvm::StringSwitch(kw) .Case("associatedtype", true) .Case("class", !CurDeclContext || !isa(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, CurDeclContext, Context.LangOpts.EnableExperimentalConcurrency, Context.LangOpts.EnableExperimentalDistributed); addLetVarKeywords(Sink); } break; } case CompletionKind::AfterIfStmtElse: addKeyword(Sink, "if", CodeCompletionKeywordKind::kw_if); break; case CompletionKind::ForEachPatternBeginning: if (!llvm::is_contained(ParsedKeywords, "try")) addKeyword(Sink, "try", CodeCompletionKeywordKind::kw_try); if (!llvm::is_contained(ParsedKeywords, "await")) addKeyword(Sink, "await", CodeCompletionKeywordKind::None); addKeyword(Sink, "var", CodeCompletionKeywordKind::kw_var); addKeyword(Sink, "case", CodeCompletionKeywordKind::kw_case); } } static void addPoundDirectives(CodeCompletionResultSink &Sink) { auto addWithName = [&](StringRef name, CodeCompletionKeywordKind K, llvm::function_ref consumer = nullptr) { CodeCompletionResultBuilder Builder(Sink, CodeCompletionResultKind::Keyword, SemanticContextKind::None, {}); Builder.addBaseName(name); Builder.setKeywordKind(K); if (consumer) consumer(Builder); }; addWithName("sourceLocation", CodeCompletionKeywordKind::pound_sourceLocation, [&] (CodeCompletionResultBuilder &Builder) { Builder.addLeftParen(); Builder.addTextChunk("file"); Builder.addCallParameterColon(); Builder.addSimpleTypedParameter("String"); Builder.addComma(); Builder.addTextChunk("line"); Builder.addCallParameterColon(); Builder.addSimpleTypedParameter("Int"); Builder.addRightParen(); }); addWithName("warning", CodeCompletionKeywordKind::pound_warning, [&] (CodeCompletionResultBuilder &Builder) { Builder.addLeftParen(); Builder.addTextChunk("\""); Builder.addSimpleNamedParameter("message"); Builder.addTextChunk("\""); Builder.addRightParen(); }); addWithName("error", CodeCompletionKeywordKind::pound_error, [&] (CodeCompletionResultBuilder &Builder) { Builder.addLeftParen(); Builder.addTextChunk("\""); Builder.addSimpleNamedParameter("message"); Builder.addTextChunk("\""); Builder.addRightParen(); }); addWithName("if ", CodeCompletionKeywordKind::pound_if, [&] (CodeCompletionResultBuilder &Builder) { Builder.addSimpleNamedParameter("condition"); }); // FIXME: These directives are only valid in conditional completion block. addWithName("elseif ", CodeCompletionKeywordKind::pound_elseif, [&] (CodeCompletionResultBuilder &Builder) { Builder.addSimpleNamedParameter("condition"); }); addWithName("else", CodeCompletionKeywordKind::pound_else); addWithName("endif", CodeCompletionKeywordKind::pound_endif); } /// Add platform conditions used in '#if' and '#elseif' directives. static void addPlatformConditions(CodeCompletionResultSink &Sink) { auto addWithName = [&](StringRef Name, llvm::function_ref consumer) { CodeCompletionResultBuilder Builder( Sink, CodeCompletionResultKind::Pattern, // FIXME: SemanticContextKind::CurrentModule is not correct. // Use 'None' (and fix prioritization) or introduce a new context. SemanticContextKind::CurrentModule, {}); Builder.addFlair(CodeCompletionFlairBit::ExpressionSpecific); Builder.addBaseName(Name); Builder.addLeftParen(); consumer(Builder); Builder.addRightParen(); }; addWithName("os", [](CodeCompletionResultBuilder &Builder) { Builder.addSimpleNamedParameter("name"); }); addWithName("arch", [](CodeCompletionResultBuilder &Builder) { Builder.addSimpleNamedParameter("name"); }); addWithName("canImport", [](CodeCompletionResultBuilder &Builder) { Builder.addSimpleNamedParameter("module"); }); addWithName("targetEnvironment", [](CodeCompletionResultBuilder &Builder) { Builder.addTextChunk("simulator"); }); addWithName("swift", [](CodeCompletionResultBuilder &Builder) { Builder.addTextChunk(">="); Builder.addSimpleNamedParameter("version"); }); addWithName("swift", [](CodeCompletionResultBuilder &Builder) { Builder.addTextChunk("<"); Builder.addSimpleNamedParameter("version"); }); addWithName("compiler", [](CodeCompletionResultBuilder &Builder) { Builder.addTextChunk(">="); Builder.addSimpleNamedParameter("version"); }); addWithName("compiler", [](CodeCompletionResultBuilder &Builder) { Builder.addTextChunk("<"); Builder.addSimpleNamedParameter("version"); }); addKeyword(Sink, "true", CodeCompletionKeywordKind::kw_true, "Bool"); addKeyword(Sink, "false", CodeCompletionKeywordKind::kw_false, "Bool"); } /// Add flags specified by '-D' to completion results. static void addConditionalCompilationFlags(ASTContext &Ctx, CodeCompletionResultSink &Sink) { for (auto Flag : Ctx.LangOpts.getCustomConditionalCompilationFlags()) { // TODO: Should we filter out some flags? CodeCompletionResultBuilder Builder( Sink, CodeCompletionResultKind::Keyword, // FIXME: SemanticContextKind::CurrentModule is not correct. // Use 'None' (and fix prioritization) or introduce a new context. SemanticContextKind::CurrentModule, {}); Builder.addFlair(CodeCompletionFlairBit::ExpressionSpecific); Builder.addTextChunk(Flag); } } /// Add flairs to the each item in \p results . /// /// If \p Sink is passed, the pointer of the each result may be replaced with a /// pointer to the new item allocated in \p Sink. /// If \p Sink is nullptr, the pointee of each result may be modified in place. static void postProcessResults(MutableArrayRef results, CompletionKind Kind, DeclContext *DC, CodeCompletionResultSink *Sink) { for (CodeCompletionResult *&result : results) { bool modified = false; auto flair = result->getFlair(); // Starting a statement with a protocol name is not common. So protocol // names at non-type name position are "rare". if (result->getKind() == CodeCompletionResultKind::Declaration && result->getAssociatedDeclKind() == CodeCompletionDeclKind::Protocol && Kind != CompletionKind::TypeSimpleBeginning && Kind != CompletionKind::TypeIdentifierWithoutDot && Kind != CompletionKind::TypeIdentifierWithDot && Kind != CompletionKind::TypeDeclResultBeginning && Kind != CompletionKind::GenericRequirement) { flair |= CodeCompletionFlairBit::RareTypeAtCurrentPosition; modified = true; } // Starting a statement at top-level in non-script files is invalid. if (Kind == CompletionKind::StmtOrExpr && result->getKind() == CodeCompletionResultKind::Declaration && isCodeCompletionAtTopLevelOfLibraryFile(DC)) { flair |= CodeCompletionFlairBit::ExpressionAtNonScriptOrMainFileScope; modified = true; } if (!modified) continue; if (Sink) { // Replace the result with a new result with the flair. result = result->withFlair(flair, *Sink); } else { // 'Sink' == nullptr means the result is modifiable in place. result->setFlair(flair); } } } static void deliverCompletionResults(CodeCompletionContext &CompletionContext, CompletionLookup &Lookup, DeclContext *DC, CodeCompletionConsumer &Consumer) { auto &SF = *DC->getParentSourceFile(); llvm::SmallPtrSet seenModuleNames; std::vector RequestedModules; SmallPtrSet explictlyImportedModules; { // Collect modules directly imported in this SourceFile. SmallVector directImport; SF.getImportedModules(directImport, {ModuleDecl::ImportFilterKind::Default, ModuleDecl::ImportFilterKind::ImplementationOnly}); for (auto import : directImport) explictlyImportedModules.insert(import.importedModule); // Exclude modules implicitly imported in the current module. auto implicitImports = SF.getParentModule()->getImplicitImports(); for (auto import : implicitImports.imports) explictlyImportedModules.erase(import.module.importedModule); // Consider the current module "explicit". explictlyImportedModules.insert(SF.getParentModule()); } for (auto &Request: Lookup.RequestedCachedResults) { llvm::DenseSet ImportsSeen; auto handleImport = [&](ImportedModule Import) { ModuleDecl *TheModule = Import.importedModule; ImportPath::Access Path = Import.accessPath; if (TheModule->getFiles().empty()) return; // Clang submodules are ignored and there's no lookup cost involved, // so just ignore them and don't put the empty results in the cache // because putting a lot of objects in the cache will push out // other lookups. if (isClangSubModule(TheModule)) return; std::vector AccessPath; for (auto Piece : Path) { AccessPath.push_back(std::string(Piece.Item)); } StringRef ModuleFilename = TheModule->getModuleFilename(); // ModuleFilename can be empty if something strange happened during // module loading, for example, the module file is corrupted. if (!ModuleFilename.empty()) { CodeCompletionCache::Key K{ ModuleFilename.str(), std::string(TheModule->getName()), AccessPath, Request.NeedLeadingDot, SF.hasTestableOrPrivateImport( AccessLevel::Internal, TheModule, SourceFile::ImportQueryKind::TestableOnly), SF.hasTestableOrPrivateImport( AccessLevel::Internal, TheModule, SourceFile::ImportQueryKind::PrivateOnly), CompletionContext.getAddInitsToTopLevel(), CompletionContext.addCallWithNoDefaultArgs(), CompletionContext.getAnnotateResult()}; using PairType = llvm::DenseSet>::iterator; std::pair Result = ImportsSeen.insert(K); if (!Result.second) return; // already handled. RequestedModules.push_back({std::move(K), TheModule, Request.OnlyTypes, Request.OnlyPrecedenceGroups}); auto TheModuleName = TheModule->getName(); if (Request.IncludeModuleQualifier && (!Lookup.isHiddenModuleName(TheModuleName) || explictlyImportedModules.contains(TheModule)) && seenModuleNames.insert(TheModuleName).second) Lookup.addModuleName(TheModule); } }; if (Request.TheModule) { // FIXME: actually check imports. for (auto Import : namelookup::getAllImports(Request.TheModule)) { handleImport(Import); } } else { // Add results from current module. Lookup.getToplevelCompletions(Request.OnlyTypes); // Add the qualifying module name auto curModule = SF.getParentModule(); if (Request.IncludeModuleQualifier && seenModuleNames.insert(curModule->getName()).second) Lookup.addModuleName(curModule); // Add results for all imported modules. SmallVector Imports; SF.getImportedModules( Imports, {ModuleDecl::ImportFilterKind::Exported, ModuleDecl::ImportFilterKind::Default, ModuleDecl::ImportFilterKind::ImplementationOnly}); for (auto Imported : Imports) { for (auto Import : namelookup::getAllImports(Imported.importedModule)) handleImport(Import); } } } Lookup.RequestedCachedResults.clear(); CompletionContext.typeContextKind = Lookup.typeContextKind(); postProcessResults(CompletionContext.getResultSink().Results, CompletionContext.CodeCompletionKind, DC, /*Sink=*/nullptr); Consumer.handleResultsAndModules(CompletionContext, RequestedModules, DC); } void deliverUnresolvedMemberResults( ArrayRef Results, ArrayRef EnumPatternTypes, DeclContext *DC, SourceLoc DotLoc, ide::CodeCompletionContext &CompletionCtx, CodeCompletionConsumer &Consumer) { ASTContext &Ctx = DC->getASTContext(); CompletionLookup Lookup(CompletionCtx.getResultSink(), Ctx, DC, &CompletionCtx); assert(DotLoc.isValid()); Lookup.setHaveDot(DotLoc); Lookup.shouldCheckForDuplicates(Results.size() + EnumPatternTypes.size() > 1); // Get the canonical versions of the top-level types SmallPtrSet originalTypes; for (auto &Result: Results) originalTypes.insert(Result.ExpectedTy->getCanonicalType()); for (auto &Result: Results) { Lookup.setExpectedTypes({Result.ExpectedTy}, Result.IsImplicitSingleExpressionReturn, /*expectsNonVoid*/true); Lookup.setIdealExpectedType(Result.ExpectedTy); // For optional types, also get members of the unwrapped type if it's not // already equivalent to one of the top-level types. Handling it via the top // level type and not here ensures we give the correct type relation // (identical, rather than convertible). if (Result.ExpectedTy->getOptionalObjectType()) { Type Unwrapped = Result.ExpectedTy->lookThroughAllOptionalTypes(); if (originalTypes.insert(Unwrapped->getCanonicalType()).second) Lookup.getUnresolvedMemberCompletions(Unwrapped); } Lookup.getUnresolvedMemberCompletions(Result.ExpectedTy); } // Offer completions when interpreting the pattern match as an // EnumElementPattern. for (auto &Ty : EnumPatternTypes) { Lookup.setExpectedTypes({Ty}, /*IsImplicitSingleExpressionReturn=*/false, /*expectsNonVoid=*/true); Lookup.setIdealExpectedType(Ty); // We can pattern match MyEnum against Optional if (Ty->getOptionalObjectType()) { Type Unwrapped = Ty->lookThroughAllOptionalTypes(); Lookup.getEnumElementPatternCompletions(Unwrapped); } Lookup.getEnumElementPatternCompletions(Ty); } deliverCompletionResults(CompletionCtx, Lookup, DC, Consumer); } void deliverKeyPathResults( ArrayRef Results, DeclContext *DC, SourceLoc DotLoc, ide::CodeCompletionContext &CompletionCtx, CodeCompletionConsumer &Consumer) { ASTContext &Ctx = DC->getASTContext(); CompletionLookup Lookup(CompletionCtx.getResultSink(), Ctx, DC, &CompletionCtx); if (DotLoc.isValid()) { Lookup.setHaveDot(DotLoc); } Lookup.shouldCheckForDuplicates(Results.size() > 1); for (auto &Result : Results) { Lookup.setIsSwiftKeyPathExpr(Result.OnRoot); Lookup.getValueExprCompletions(Result.BaseType); } deliverCompletionResults(CompletionCtx, Lookup, DC, Consumer); } void deliverDotExprResults( ArrayRef Results, Expr *BaseExpr, DeclContext *DC, SourceLoc DotLoc, bool IsInSelector, ide::CodeCompletionContext &CompletionCtx, CodeCompletionConsumer &Consumer) { ASTContext &Ctx = DC->getASTContext(); CompletionLookup Lookup(CompletionCtx.getResultSink(), Ctx, DC, &CompletionCtx); if (DotLoc.isValid()) Lookup.setHaveDot(DotLoc); Lookup.setIsSuperRefExpr(isa(BaseExpr)); if (auto *DRE = dyn_cast(BaseExpr)) Lookup.setIsSelfRefExpr(DRE->getDecl()->getName() == Ctx.Id_self); if (isa(BaseExpr) || isa(BaseExpr)) Lookup.setIsUnwrappedOptional(true); if (IsInSelector) { Lookup.includeInstanceMembers(); Lookup.setPreferFunctionReferencesToCalls(); } Lookup.shouldCheckForDuplicates(Results.size() > 1); for (auto &Result: Results) { Lookup.setIsStaticMetatype(Result.BaseIsStaticMetaType); Lookup.getPostfixKeywordCompletions(Result.BaseTy, BaseExpr); Lookup.setExpectedTypes(Result.ExpectedTypes, Result.IsImplicitSingleExpressionReturn, Result.ExpectsNonVoid); if (isDynamicLookup(Result.BaseTy)) Lookup.setIsDynamicLookup(); Lookup.getValueExprCompletions(Result.BaseTy, Result.BaseDecl); } deliverCompletionResults(CompletionCtx, Lookup, DC, Consumer); } bool CodeCompletionCallbacksImpl::trySolverCompletion(bool MaybeFuncBody) { assert(ParsedExpr || CurDeclContext); SourceLoc CompletionLoc = ParsedExpr ? ParsedExpr->getLoc() : CurDeclContext->getASTContext().SourceMgr.getCodeCompletionLoc(); switch (Kind) { case CompletionKind::DotExpr: { assert(CodeCompleteTokenExpr); assert(CurDeclContext); DotExprTypeCheckCompletionCallback Lookup(CurDeclContext, CodeCompleteTokenExpr); llvm::SaveAndRestore CompletionCollector(Context.CompletionCallback, &Lookup); typeCheckContextAt(CurDeclContext, CompletionLoc); // This (hopefully) only happens in cases where the expression isn't // typechecked during normal compilation either (e.g. member completion in a // switch case where there control expression is invalid). Having normal // typechecking still resolve even these cases would be beneficial for // tooling in general though. if (!Lookup.gotCallback()) Lookup.fallbackTypeCheck(); addKeywords(CompletionContext.getResultSink(), MaybeFuncBody); Expr *CheckedBase = CodeCompleteTokenExpr->getBase(); deliverDotExprResults(Lookup.getResults(), CheckedBase, CurDeclContext, DotLoc, isInsideObjCSelector(), CompletionContext, Consumer); return true; } case CompletionKind::UnresolvedMember: { assert(CodeCompleteTokenExpr); assert(CurDeclContext); UnresolvedMemberTypeCheckCompletionCallback Lookup(CodeCompleteTokenExpr); llvm::SaveAndRestore CompletionCollector(Context.CompletionCallback, &Lookup); typeCheckContextAt(CurDeclContext, CompletionLoc); if (!Lookup.gotCallback()) Lookup.fallbackTypeCheck(CurDeclContext); addKeywords(CompletionContext.getResultSink(), MaybeFuncBody); deliverUnresolvedMemberResults(Lookup.getExprResults(), Lookup.getEnumPatternTypes(), CurDeclContext, DotLoc, CompletionContext, Consumer); return true; } case CompletionKind::KeyPathExprSwift: { assert(CurDeclContext); // CodeCompletionCallbacks::completeExprKeyPath takes a \c KeyPathExpr, // so we can safely cast the \c ParsedExpr back to a \c KeyPathExpr. auto KeyPath = cast(ParsedExpr); KeyPathTypeCheckCompletionCallback Lookup(KeyPath); llvm::SaveAndRestore CompletionCollector( Context.CompletionCallback, &Lookup); typeCheckContextAt(CurDeclContext, CompletionLoc); deliverKeyPathResults(Lookup.getResults(), CurDeclContext, DotLoc, CompletionContext, Consumer); return true; } default: return false; } } // Undoes the single-expression closure/function body transformation on the // given DeclContext and its parent contexts if they have a single expression // body that contains the code completion location. // // FIXME: Remove this once all expression position completions are migrated // to work via TypeCheckCompletionCallback. static void undoSingleExpressionReturn(DeclContext *DC) { auto updateBody = [](BraceStmt *BS, ASTContext &Ctx) -> bool { ASTNode LastElem = BS->getLastElement(); auto *RS = dyn_cast_or_null(LastElem.dyn_cast()); if (!RS || !RS->isImplicit()) return false; BS->setLastElement(RS->getResult()); return true; }; while (ClosureExpr *CE = dyn_cast_or_null(DC)) { if (CE->hasSingleExpressionBody()) { if (updateBody(CE->getBody(), CE->getASTContext())) CE->setBody(CE->getBody(), false); } DC = DC->getParent(); } if (FuncDecl *FD = dyn_cast_or_null(DC)) { if (FD->hasSingleExpressionBody()) { if (updateBody(FD->getBody(), FD->getASTContext())) FD->setHasSingleExpressionBody(false); } } } void CodeCompletionCallbacksImpl::doneParsing() { CompletionContext.CodeCompletionKind = Kind; if (Kind == CompletionKind::None) { return; } bool MaybeFuncBody = true; if (CurDeclContext) { auto *CD = CurDeclContext->getLocalContext(); if (!CD || CD->getContextKind() == DeclContextKind::Initializer || CD->getContextKind() == DeclContextKind::TopLevelCodeDecl) MaybeFuncBody = false; } if (auto *DC = dyn_cast_or_null(ParsedDecl)) { if (DC->isChildContextOf(CurDeclContext)) CurDeclContext = DC; } if (trySolverCompletion(MaybeFuncBody)) return; undoSingleExpressionReturn(CurDeclContext); typeCheckContextAt( CurDeclContext, ParsedExpr ? ParsedExpr->getLoc() : CurDeclContext->getASTContext().SourceMgr.getCodeCompletionLoc()); // Add keywords even if type checking fails completely. addKeywords(CompletionContext.getResultSink(), MaybeFuncBody); Optional 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(ParsedExpr)) { Lookup.setIsSelfRefExpr(DRE->getDecl()->getName() == Context.Id_self); } else if (isa_and_nonnull(ParsedExpr)) { Lookup.setIsSuperRefExpr(); } if (isInsideObjCSelector()) Lookup.includeInstanceMembers(); if (PreferFunctionReferencesToCalls) Lookup.setPreferFunctionReferencesToCalls(); auto DoPostfixExprBeginning = [&] (){ SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc(); Lookup.getValueCompletionsInDeclContext(Loc); Lookup.getSelfTypeCompletionInDeclContext(Loc, /*isForDeclResult=*/false); }; switch (Kind) { case CompletionKind::None: case CompletionKind::DotExpr: case CompletionKind::UnresolvedMember: case CompletionKind::KeyPathExprSwift: llvm_unreachable("should be already handled"); return; case CompletionKind::StmtOrExpr: case CompletionKind::ForEachSequence: case CompletionKind::PostfixExprBeginning: { ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr); Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(), ContextInfo.isImplicitSingleExpressionReturn()); DoPostfixExprBeginning(); break; } case CompletionKind::PostfixExpr: { Lookup.setHaveLeadingSpace(HasSpace); if (isDynamicLookup(*ExprType)) Lookup.setIsDynamicLookup(); Lookup.getValueExprCompletions(*ExprType, ReferencedDecl.getDecl()); /// We set the type of ParsedExpr explicitly above. But we don't want an /// unresolved type in our AST when we type check again for operator /// completions. Remove the type of the ParsedExpr and see if we can come up /// with something more useful based on the the full sequence expression. if (ParsedExpr->getType()->is()) { ParsedExpr->setType(nullptr); } Lookup.getOperatorCompletions(ParsedExpr, leadingSequenceExprs); Lookup.getPostfixKeywordCompletions(*ExprType, ParsedExpr); break; } case CompletionKind::PostfixExprParen: { Lookup.setHaveLParen(true); ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr); if (ShouldCompleteCallPatternAfterParen) { ExprContextInfo ParentContextInfo(CurDeclContext, ParsedExpr); Lookup.setExpectedTypes( ParentContextInfo.getPossibleTypes(), ParentContextInfo.isImplicitSingleExpressionReturn()); if (!ContextInfo.getPossibleCallees().empty()) { for (auto &typeAndDecl : ContextInfo.getPossibleCallees()) Lookup.tryFunctionCallCompletions(typeAndDecl.Type, typeAndDecl.Decl, typeAndDecl.SemanticContext); } else if (ExprType && ((*ExprType)->is() || (*ExprType)->is())) { Lookup.getValueExprCompletions(*ExprType, ReferencedDecl.getDecl()); } } else { // Add argument labels, then fallthrough to get values. Lookup.addCallArgumentCompletionResults(ContextInfo.getPossibleParams()); } if (!Lookup.FoundFunctionCalls || (Lookup.FoundFunctionCalls && Lookup.FoundFunctionsWithoutFirstKeyword)) { Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(), ContextInfo.isImplicitSingleExpressionReturn()); Lookup.setHaveLParen(false); // Add any keywords that can be used in an argument expr position. addSuperKeyword(CompletionContext.getResultSink()); addExprKeywords(CompletionContext.getResultSink(), CurDeclContext); DoPostfixExprBeginning(); } break; } case CompletionKind::KeyPathExprObjC: { if (DotLoc.isValid()) Lookup.setHaveDot(DotLoc); Lookup.setIsKeyPathExpr(); Lookup.includeInstanceMembers(); if (ExprType) { if (isDynamicLookup(*ExprType)) Lookup.setIsDynamicLookup(); Lookup.getValueExprCompletions(*ExprType, ReferencedDecl.getDecl()); } else { SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc(); Lookup.getValueCompletionsInDeclContext(Loc, KeyPathFilter, /*LiteralCompletions=*/false); } break; } case CompletionKind::TypeDeclResultBeginning: case CompletionKind::TypeSimpleBeginning: { auto Loc = Context.SourceMgr.getCodeCompletionLoc(); Lookup.getTypeCompletionsInDeclContext(Loc); Lookup.getSelfTypeCompletionInDeclContext( Loc, Kind == CompletionKind::TypeDeclResultBeginning); break; } case CompletionKind::TypeIdentifierWithDot: { Lookup.setHaveDot(SourceLoc()); Lookup.getTypeCompletions(ParsedTypeLoc.getType()); break; } case CompletionKind::TypeIdentifierWithoutDot: { Lookup.getTypeCompletions(ParsedTypeLoc.getType()); break; } case CompletionKind::CaseStmtBeginning: { ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr); Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(), ContextInfo.isImplicitSingleExpressionReturn()); Lookup.setIdealExpectedType(CodeCompleteTokenExpr->getType()); Lookup.getUnresolvedMemberCompletions(ContextInfo.getPossibleTypes()); DoPostfixExprBeginning(); break; } case CompletionKind::NominalMemberBeginning: { CompletionOverrideLookup OverrideLookup(CompletionContext.getResultSink(), P.Context, CurDeclContext, ParsedKeywords, introducerLoc); OverrideLookup.getOverrideCompletions(SourceLoc()); break; } case CompletionKind::AccessorBeginning: { if (isa(ParsedDecl)) { ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr); Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(), ContextInfo.isImplicitSingleExpressionReturn()); DoPostfixExprBeginning(); } break; } case CompletionKind::AttributeBegin: { Lookup.getAttributeDeclCompletions(IsInSil, AttTargetDK); // TypeName at attribute position after '@'. // - VarDecl: Property Wrappers. // - ParamDecl/VarDecl/FuncDecl: Function Builders. if (!AttTargetDK || *AttTargetDK == DeclKind::Var || *AttTargetDK == DeclKind::Param || *AttTargetDK == DeclKind::Func) Lookup.getTypeCompletionsInDeclContext( P.Context.SourceMgr.getCodeCompletionLoc()); break; } case CompletionKind::AttributeDeclParen: { Lookup.getAttributeDeclParamCompletions(AttrKind, AttrParamIndex); break; } case CompletionKind::PoundAvailablePlatform: { Lookup.getPoundAvailablePlatformCompletions(); break; } case CompletionKind::Import: { if (DotLoc.isValid()) Lookup.addSubModuleNames(SubModuleNameVisibilityPairs); else Lookup.addImportModuleNames(); break; } case CompletionKind::CallArg: { ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr); bool shouldPerformGlobalCompletion = true; if (ShouldCompleteCallPatternAfterParen && !ContextInfo.getPossibleCallees().empty()) { Lookup.setHaveLParen(true); for (auto &typeAndDecl : ContextInfo.getPossibleCallees()) { auto apply = ContextInfo.getAnalyzedExpr(); if (isa_and_nonnull(apply)) { Lookup.addSubscriptCallPattern( typeAndDecl.Type, dyn_cast_or_null(typeAndDecl.Decl), typeAndDecl.SemanticContext); } else { Lookup.addFunctionCallPattern( typeAndDecl.Type, dyn_cast_or_null(typeAndDecl.Decl), typeAndDecl.SemanticContext); } } Lookup.setHaveLParen(false); shouldPerformGlobalCompletion = !Lookup.FoundFunctionCalls || (Lookup.FoundFunctionCalls && Lookup.FoundFunctionsWithoutFirstKeyword); } else if (!ContextInfo.getPossibleParams().empty()) { auto params = ContextInfo.getPossibleParams(); Lookup.addCallArgumentCompletionResults(params); shouldPerformGlobalCompletion = !ContextInfo.getPossibleTypes().empty(); // Fallback to global completion if the position is out of number. It's // better than suggest nothing. shouldPerformGlobalCompletion |= llvm::all_of( params, [](const PossibleParamInfo &P) { return !P.Param; }); } if (shouldPerformGlobalCompletion) { Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(), ContextInfo.isImplicitSingleExpressionReturn()); // Add any keywords that can be used in an argument expr position. addSuperKeyword(CompletionContext.getResultSink()); addExprKeywords(CompletionContext.getResultSink(), CurDeclContext); DoPostfixExprBeginning(); } break; } case CompletionKind::LabeledTrailingClosure: { ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr); SmallVector params; // Only complete function type parameters llvm::copy_if(ContextInfo.getPossibleParams(), std::back_inserter(params), [](const PossibleParamInfo &P) { // nullptr indicates out of bounds. if (!P.Param) return true; return P.Param->getPlainType() ->lookThroughAllOptionalTypes() ->is(); }); bool allRequired = false; if (!params.empty()) { Lookup.addCallArgumentCompletionResults( params, /*isLabeledTrailingClosure=*/true); allRequired = llvm::all_of( params, [](const PossibleParamInfo &P) { return P.IsRequired; }); } // If there're optional parameters, do global completion or member // completion depending on the completion is happening at the start of line. if (!allRequired) { if (IsAtStartOfLine) { // foo() {} // auto &Sink = CompletionContext.getResultSink(); if (isa(CurDeclContext)) CurDeclContext = CurDeclContext->getParent(); if (CurDeclContext->isTypeContext()) { // Override completion (CompletionKind::NominalMemberBeginning). addDeclKeywords(Sink, CurDeclContext, Context.LangOpts.EnableExperimentalConcurrency, Context.LangOpts.EnableExperimentalDistributed); addLetVarKeywords(Sink); SmallVector ParsedKeywords; CompletionOverrideLookup OverrideLookup(Sink, Context, CurDeclContext, ParsedKeywords, SourceLoc()); OverrideLookup.getOverrideCompletions(SourceLoc()); } else { // Global completion (CompletionKind::PostfixExprBeginning). addDeclKeywords(Sink, CurDeclContext, Context.LangOpts.EnableExperimentalConcurrency, Context.LangOpts.EnableExperimentalDistributed); addStmtKeywords(Sink, CurDeclContext, MaybeFuncBody); addSuperKeyword(Sink); addLetVarKeywords(Sink); addExprKeywords(Sink, CurDeclContext); addAnyTypeKeyword(Sink, Context.TheAnyType); DoPostfixExprBeginning(); } } else { // foo() {} // Member completion. Expr *analyzedExpr = ContextInfo.getAnalyzedExpr(); if (!analyzedExpr) break; // Only if the completion token is the last token in the call. if (analyzedExpr->getEndLoc() != CodeCompleteTokenExpr->getLoc()) break; Type resultTy = analyzedExpr->getType(); // If the call expression doesn't have a type, fallback to: if (!resultTy || resultTy->is()) { // 1) Try to type check removing CodeCompletionExpr from the call. Expr *removedExpr = analyzedExpr; removeCodeCompletionExpr(CurDeclContext->getASTContext(), removedExpr); ConcreteDeclRef referencedDecl; auto optT = getTypeOfCompletionContextExpr( CurDeclContext->getASTContext(), CurDeclContext, CompletionTypeCheckKind::Normal, removedExpr, referencedDecl); if (optT) { resultTy = *optT; analyzedExpr->setType(resultTy); } } if (!resultTy || resultTy->is()) { // 2) Infer it from the possible callee info. if (!ContextInfo.getPossibleCallees().empty()) { auto calleeInfo = ContextInfo.getPossibleCallees()[0]; resultTy = calleeInfo.Type->getResult(); analyzedExpr->setType(resultTy); } } if (!resultTy || resultTy->is()) { // 3) Give up providing postfix completions. break; } auto &SM = CurDeclContext->getASTContext().SourceMgr; auto leadingChar = SM.extractText({SM.getCodeCompletionLoc().getAdvancedLoc(-1), 1}); Lookup.setHaveLeadingSpace(leadingChar.find_first_of(" \t\f\v") != StringRef::npos); if (isDynamicLookup(resultTy)) Lookup.setIsDynamicLookup(); Lookup.getValueExprCompletions(resultTy, /*VD=*/nullptr); Lookup.getOperatorCompletions(analyzedExpr, leadingSequenceExprs); Lookup.getPostfixKeywordCompletions(resultTy, analyzedExpr); } } break; } case CompletionKind::ReturnStmtExpr : { SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc(); SmallVector possibleReturnTypes; collectPossibleReturnTypesFromContext(CurDeclContext, possibleReturnTypes); Lookup.setExpectedTypes(possibleReturnTypes, /*isImplicitSingleExpressionReturn*/ false); Lookup.getValueCompletionsInDeclContext(Loc); break; } case CompletionKind::YieldStmtExpr: { SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc(); if (auto FD = dyn_cast(CurDeclContext)) { if (FD->isCoroutine()) { // TODO: handle multi-value yields. Lookup.setExpectedTypes(FD->getStorage()->getValueInterfaceType(), /*isImplicitSingleExpressionReturn*/ false); } } Lookup.getValueCompletionsInDeclContext(Loc); break; } case CompletionKind::AfterPoundExpr: { ExprContextInfo ContextInfo(CurDeclContext, CodeCompleteTokenExpr); Lookup.setExpectedTypes(ContextInfo.getPossibleTypes(), ContextInfo.isImplicitSingleExpressionReturn()); Lookup.addPoundAvailable(ParentStmtKind); Lookup.addPoundLiteralCompletions(/*needPound=*/false); Lookup.addObjCPoundKeywordCompletions(/*needPound=*/false); break; } case CompletionKind::AfterPoundDirective: { addPoundDirectives(CompletionContext.getResultSink()); // FIXME: Add pound expressions (e.g. '#selector()') if it's at statements // position. break; } case CompletionKind::PlatformConditon: { addPlatformConditions(CompletionContext.getResultSink()); addConditionalCompilationFlags(CurDeclContext->getASTContext(), CompletionContext.getResultSink()); break; } case CompletionKind::GenericRequirement: { auto Loc = Context.SourceMgr.getCodeCompletionLoc(); Lookup.getGenericRequirementCompletions(CurDeclContext, Loc); break; } case CompletionKind::PrecedenceGroup: Lookup.getPrecedenceGroupCompletions(SyntxKind); break; case CompletionKind::StmtLabel: { SourceLoc Loc = P.Context.SourceMgr.getCodeCompletionLoc(); Lookup.getStmtLabelCompletions(Loc, ParentStmtKind == StmtKind::Continue); break; } case CompletionKind::TypeAttrBeginning: { Lookup.getTypeAttributeKeywordCompletions(); // Type names at attribute position after '@'. Lookup.getTypeCompletionsInDeclContext( P.Context.SourceMgr.getCodeCompletionLoc()); break; } case CompletionKind::AfterIfStmtElse: case CompletionKind::CaseStmtKeyword: case CompletionKind::EffectsSpecifier: case CompletionKind::ForEachPatternBeginning: // Handled earlier by keyword completions. break; } deliverCompletionResults(CompletionContext, Lookup, CurDeclContext, Consumer); } 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 accessPath, bool needLeadingDot, const SourceFile *SF) { // Use the SourceFile as the decl context, to avoid decl context specific // behaviors. CompletionLookup Lookup(targetSink, module->getASTContext(), SF); Lookup.lookupExternalModuleDecls(module, accessPath, needLeadingDot); } static MutableArrayRef copyCodeCompletionResults(CodeCompletionResultSink &targetSink, CodeCompletionCache::Value &source, 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(source.Allocator); auto startSize = targetSink.Results.size(); std::function shouldIncludeResult; if (onlyTypes) { shouldIncludeResult = [](const ContextFreeCodeCompletionResult *R) -> bool { if (R->getKind() != CodeCompletionResultKind::Declaration) return false; switch (R->getAssociatedDeclKind()) { case CodeCompletionDeclKind::Module: case CodeCompletionDeclKind::Class: case CodeCompletionDeclKind::Actor: 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) { shouldIncludeResult = [](const ContextFreeCodeCompletionResult *R) -> bool { return R->getAssociatedDeclKind() == CodeCompletionDeclKind::PrecedenceGroup; }; } else { shouldIncludeResult = [](const ContextFreeCodeCompletionResult *R) -> bool { return true; }; } for (auto contextFreeResult : source.Results) { if (!shouldIncludeResult(contextFreeResult)) { continue; } auto contextualResult = new (*targetSink.Allocator) CodeCompletionResult( *contextFreeResult, SemanticContextKind::OtherModule, CodeCompletionFlair(), /*numBytesToErase=*/0, /*TypeContext=*/nullptr, /*DC=*/nullptr, ContextualNotRecommendedReason::None, CodeCompletionDiagnosticSeverity::None, /*DiagnosticMessage=*/""); targetSink.Results.push_back(contextualResult); } return llvm::makeMutableArrayRef(targetSink.Results.data() + startSize, targetSink.Results.size() - startSize); } void SimpleCachingCodeCompletionConsumer::handleResultsAndModules( CodeCompletionContext &context, ArrayRef requestedModules, DeclContext *DC) { // 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. Also it cannot use 'DC' since it would apply decl // context changes to cached results. const SourceFile *SF = DC->getParentSourceFile(); for (auto &R : requestedModules) { // FIXME(thread-safety): lock the whole AST context. We might load a // module. llvm::Optional V = context.Cache.get(R.Key); if (!V.hasValue()) { // No cached results found. Fill the cache. V = context.Cache.createValue(); // Temporary sink in which we gather the result. The cache value retains // the sink's allocator. CodeCompletionResultSink Sink; Sink.annotateResult = context.getAnnotateResult(); Sink.addInitsToTopLevel = context.getAddInitsToTopLevel(); Sink.enableCallPatternHeuristics = context.getCallPatternHeuristics(); Sink.includeObjectLiterals = context.includeObjectLiterals(); Sink.addCallWithNoDefaultArgs = context.addCallWithNoDefaultArgs(); lookupCodeCompletionResultsFromModule(Sink, R.TheModule, R.Key.AccessPath, R.Key.ResultsHaveLeadingDot, SF); (*V)->Allocator = Sink.Allocator; auto &CachedResults = (*V)->Results; CachedResults.reserve(Sink.Results.size()); // Instead of copying the context free results out of the sink's allocator // retain the sink's entire allocator (which also includes the contextual // properities) and simply store pointers to the context free results that // back the contextual results. for (auto Result : Sink.Results) { CachedResults.push_back(Result->getContextFreeResultPtr()); } context.Cache.set(R.Key, *V); } assert(V.hasValue()); auto newItems = copyCodeCompletionResults( context.getResultSink(), **V, R.OnlyTypes, R.OnlyPrecedenceGroups); postProcessResults(newItems, context.CodeCompletionKind, DC, &context.getResultSink()); } handleResults(context); } //===----------------------------------------------------------------------===// // ImportDepth //===----------------------------------------------------------------------===// ImportDepth::ImportDepth(ASTContext &context, const FrontendOptions &frontendOptions) { llvm::DenseSet seen; std::deque> worklist; StringRef mainModule = frontendOptions.ModuleName; auto *main = context.getLoadedModule(context.getIdentifier(mainModule)); assert(main && "missing main module"); worklist.emplace_back(main, uint8_t(0)); // Imports from -import-name such as Playground auxiliary sources are treated // specially by applying import depth 0. llvm::StringSet<> auxImports; for (const auto &pair : frontendOptions.getImplicitImportModuleNames()) auxImports.insert(pair.first); // Private imports from this module. // FIXME: only the private imports from the current source file. // FIXME: ImportFilterKind::ShadowedByCrossImportOverlay? SmallVector mainImports; main->getImportedModules(mainImports, {ModuleDecl::ImportFilterKind::Default, ModuleDecl::ImportFilterKind::ImplementationOnly}); for (auto &import : mainImports) { uint8_t depth = 1; if (auxImports.count(import.importedModule->getName().str())) depth = 0; worklist.emplace_back(import.importedModule, depth); } // Fill depths with BFS over module imports. while (!worklist.empty()) { ModuleDecl *module; uint8_t depth; std::tie(module, depth) = worklist.front(); worklist.pop_front(); if (!seen.insert(module).second) continue; // Insert new module:depth mapping. const clang::Module *CM = module->findUnderlyingClangModule(); if (CM) { depths[CM->getFullModuleName()] = depth; } else { depths[module->getName().str()] = depth; } // Add imports to the worklist. SmallVector imports; module->getImportedModules(imports); for (auto &import : imports) { uint8_t next = std::max(depth, uint8_t(depth + 1)); // unsigned wrap // Implicitly imported sub-modules get the same depth as their parent. if (const clang::Module *CMI = import.importedModule->findUnderlyingClangModule()) if (CM && CMI->isSubModuleOf(CM)) next = depth; worklist.emplace_back(import.importedModule, next); } } }