//===--- ConformingMethodList.cpp -----------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2019 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/ConformingMethodList.h" #include "ExprContextAnalysis.h" #include "swift/AST/ASTDemangler.h" #include "swift/AST/GenericEnvironment.h" #include "swift/AST/NameLookup.h" #include "swift/AST/USRGeneration.h" #include "swift/Parse/CodeCompletionCallbacks.h" #include "swift/Sema/IDETypeChecking.h" #include "clang/AST/Attr.h" #include "clang/AST/Decl.h" using namespace swift; using namespace ide; namespace { class ConformingMethodListCallbacks : public CodeCompletionCallbacks { ArrayRef ExpectedTypeNames; ConformingMethodListConsumer &Consumer; SourceLoc Loc; Expr *ParsedExpr = nullptr; DeclContext *CurDeclContext = nullptr; void getMatchingMethods(Type T, llvm::SmallPtrSetImpl &expectedTypes, SmallVectorImpl &result); public: ConformingMethodListCallbacks(Parser &P, ArrayRef ExpectedTypeNames, ConformingMethodListConsumer &Consumer) : CodeCompletionCallbacks(P), ExpectedTypeNames(ExpectedTypeNames), Consumer(Consumer) {} // Only handle callbacks for suffix completions. // { void completeDotExpr(Expr *E, SourceLoc DotLoc) override; void completePostfixExpr(Expr *E, bool hasSpace) override; // } void doneParsing() override; }; void ConformingMethodListCallbacks::completeDotExpr(Expr *E, SourceLoc DotLoc) { CurDeclContext = P.CurDeclContext; ParsedExpr = E; } void ConformingMethodListCallbacks::completePostfixExpr(Expr *E, bool hasSpace) { CurDeclContext = P.CurDeclContext; ParsedExpr = E; } void ConformingMethodListCallbacks::doneParsing() { if (!ParsedExpr) return; typeCheckContextUntil( CurDeclContext, CurDeclContext->getASTContext().SourceMgr.getCodeCompletionLoc()); Type T = ParsedExpr->getType(); // Type check the expression if needed. if (!T || T->is()) { ConcreteDeclRef ReferencedDecl = nullptr; auto optT = getTypeOfCompletionContextExpr(P.Context, CurDeclContext, CompletionTypeCheckKind::Normal, ParsedExpr, ReferencedDecl); if (!optT) return; T = *optT; } if (!T || T->is() || T->is()) return; T = T->getRValueType(); if (T->hasArchetype()) T = T->mapTypeOutOfContext(); llvm::SmallPtrSet expectedProtocols; for (auto Name: ExpectedTypeNames) { if (auto *PD = resolveProtocolName(CurDeclContext, Name)) { expectedProtocols.insert(PD); } } // Collect the matching methods. ConformingMethodListResult result(CurDeclContext, T); getMatchingMethods(T, expectedProtocols, result.Members); Consumer.handleResult(result); } void ConformingMethodListCallbacks::getMatchingMethods( Type T, llvm::SmallPtrSetImpl &expectedTypes, SmallVectorImpl &result) { if (!T->mayHaveMembers()) return; class LocalConsumer : public VisibleDeclConsumer { ModuleDecl *CurModule; /// The type of the parsed expression. Type T; /// The list of expected types. llvm::SmallPtrSetImpl &ExpectedTypes; /// Result sink to populate. SmallVectorImpl &Result; /// Returns true if \p VD is a instance method whose return type conforms /// to the requested protocols. bool isMatchingMethod(ValueDecl *VD) { if (!isa(VD)) return false; if (VD->isStatic() || VD->isOperator()) return false; auto declTy = T->getTypeOfMember(CurModule, VD); if (declTy->is()) return false; // Strip '(Self.Type) ->' and parameters. declTy = declTy->castTo()->getResult(); declTy = declTy->castTo()->getResult(); // The return type conforms to any of the requested protocols. for (auto Proto : ExpectedTypes) { if (CurModule->conformsToProtocol(declTy, Proto)) return true; } return false; } public: LocalConsumer(DeclContext *DC, Type T, llvm::SmallPtrSetImpl &expectedTypes, SmallVectorImpl &result) : CurModule(DC->getParentModule()), T(T), ExpectedTypes(expectedTypes), Result(result) {} void foundDecl(ValueDecl *VD, DeclVisibilityKind reason, DynamicLookupInfo) { if (isMatchingMethod(VD) && !VD->shouldHideFromEditor()) Result.push_back(VD); } } LocalConsumer(CurDeclContext, T, expectedTypes, result); lookupVisibleMemberDecls(LocalConsumer, MetatypeType::get(T), CurDeclContext, /*includeInstanceMembers=*/false); } } // anonymous namespace. void PrintingConformingMethodListConsumer::handleResult( const ConformingMethodListResult &result) { OS << "-----BEGIN CONFORMING METHOD LIST-----\n"; OS << "- TypeName: "; result.ExprType.print(OS); OS << "\n"; OS << "- Members: "; if (result.Members.empty()) OS << " []"; OS << "\n"; for (auto VD : result.Members) { auto funcTy = cast(VD)->getMethodInterfaceType(); funcTy = result.ExprType->getTypeOfMember(result.DC->getParentModule(), VD, funcTy); auto resultTy = funcTy->castTo()->getResult(); OS << " - Name: "; VD->getFullName().print(OS); OS << "\n"; OS << " TypeName: "; resultTy.print(OS); OS << "\n"; StringRef BriefDoc = VD->getBriefComment(); if (!BriefDoc.empty()) { OS << " DocBrief: \""; OS << VD->getBriefComment(); OS << "\"\n"; } } OS << "-----END CONFORMING METHOD LIST-----\n"; } CodeCompletionCallbacksFactory * swift::ide::makeConformingMethodListCallbacksFactory( ArrayRef expectedTypeNames, ConformingMethodListConsumer &Consumer) { // CC callback factory which produces 'ContextInfoCallbacks'. class ConformingMethodListCallbacksFactoryImpl : public CodeCompletionCallbacksFactory { ArrayRef ExpectedTypeNames; ConformingMethodListConsumer &Consumer; public: ConformingMethodListCallbacksFactoryImpl( ArrayRef ExpectedTypeNames, ConformingMethodListConsumer &Consumer) : ExpectedTypeNames(ExpectedTypeNames), Consumer(Consumer) {} CodeCompletionCallbacks *createCodeCompletionCallbacks(Parser &P) override { return new ConformingMethodListCallbacks(P, ExpectedTypeNames, Consumer); } }; return new ConformingMethodListCallbacksFactoryImpl(expectedTypeNames, Consumer); }