mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Note that in all cases it was either nullptr or ctx.getLazyResolver().
While passing in nullptr might appear at first glance to mean something
("don't type check anything"), in practice we would check for a nullptr
value and pull out ctx.getLazyResolver() instead. Furthermore, with
the lazy resolver going away (at least for resolveDeclSignature() calls),
it won't make sense to do that anymore anyway.
234 lines
7.2 KiB
C++
234 lines
7.2 KiB
C++
//===--- 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<const char *> ExpectedTypeNames;
|
|
ConformingMethodListConsumer &Consumer;
|
|
SourceLoc Loc;
|
|
Expr *ParsedExpr = nullptr;
|
|
DeclContext *CurDeclContext = nullptr;
|
|
|
|
void getMatchingMethods(Type T,
|
|
llvm::SmallPtrSetImpl<ProtocolDecl*> &expectedTypes,
|
|
SmallVectorImpl<ValueDecl *> &result);
|
|
|
|
public:
|
|
ConformingMethodListCallbacks(Parser &P,
|
|
ArrayRef<const char *> 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<ErrorType>()) {
|
|
ConcreteDeclRef ReferencedDecl = nullptr;
|
|
auto optT = getTypeOfCompletionContextExpr(P.Context, CurDeclContext,
|
|
CompletionTypeCheckKind::Normal,
|
|
ParsedExpr, ReferencedDecl);
|
|
if (!optT)
|
|
return;
|
|
T = *optT;
|
|
}
|
|
|
|
if (!T || T->is<ErrorType>() || T->is<UnresolvedType>())
|
|
return;
|
|
|
|
T = T->getRValueType();
|
|
if (T->hasArchetype())
|
|
T = T->mapTypeOutOfContext();
|
|
|
|
llvm::SmallPtrSet<ProtocolDecl*, 8> 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<ProtocolDecl*> &expectedTypes,
|
|
SmallVectorImpl<ValueDecl *> &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<ProtocolDecl*> &ExpectedTypes;
|
|
|
|
/// Result sink to populate.
|
|
SmallVectorImpl<ValueDecl *> &Result;
|
|
|
|
/// Returns true if \p VD is a instance method whose return type conforms
|
|
/// to the requested protocols.
|
|
bool isMatchingMethod(ValueDecl *VD) {
|
|
if (!isa<FuncDecl>(VD))
|
|
return false;
|
|
if (VD->isStatic() || VD->isOperator())
|
|
return false;
|
|
|
|
auto declTy = T->getTypeOfMember(CurModule, VD);
|
|
if (declTy->is<ErrorType>())
|
|
return false;
|
|
|
|
// Strip '(Self.Type) ->' and parameters.
|
|
declTy = declTy->castTo<AnyFunctionType>()->getResult();
|
|
declTy = declTy->castTo<AnyFunctionType>()->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<ProtocolDecl*> &expectedTypes,
|
|
SmallVectorImpl<ValueDecl *> &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<FuncDecl>(VD)->getMethodInterfaceType();
|
|
funcTy = result.ExprType->getTypeOfMember(result.DC->getParentModule(), VD,
|
|
funcTy);
|
|
auto resultTy = funcTy->castTo<FunctionType>()->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<const char *> expectedTypeNames,
|
|
ConformingMethodListConsumer &Consumer) {
|
|
|
|
// CC callback factory which produces 'ContextInfoCallbacks'.
|
|
class ConformingMethodListCallbacksFactoryImpl
|
|
: public CodeCompletionCallbacksFactory {
|
|
ArrayRef<const char *> ExpectedTypeNames;
|
|
ConformingMethodListConsumer &Consumer;
|
|
|
|
public:
|
|
ConformingMethodListCallbacksFactoryImpl(
|
|
ArrayRef<const char *> ExpectedTypeNames,
|
|
ConformingMethodListConsumer &Consumer)
|
|
: ExpectedTypeNames(ExpectedTypeNames), Consumer(Consumer) {}
|
|
|
|
CodeCompletionCallbacks *createCodeCompletionCallbacks(Parser &P) override {
|
|
return new ConformingMethodListCallbacks(P, ExpectedTypeNames, Consumer);
|
|
}
|
|
};
|
|
|
|
return new ConformingMethodListCallbacksFactoryImpl(expectedTypeNames,
|
|
Consumer);
|
|
}
|