Merge pull request #22429 from rintaro/sourcekit-conformingmethods

[IDE/SourceKit] New SourceKit request for filtered method list
This commit is contained in:
Rintaro Ishizaki
2019-02-11 18:13:15 -08:00
committed by GitHub
24 changed files with 1040 additions and 210 deletions

View File

@@ -0,0 +1,66 @@
//===--- ConformingMethodList.h --- -----------------------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_IDE_CONFORMINGMETHODLIST_H
#define SWIFT_IDE_CONFORMINGMETHODLIST_H
#include "swift/AST/Type.h"
#include "swift/Basic/LLVM.h"
namespace swift {
class CodeCompletionCallbacksFactory;
namespace ide {
/// A result item for context info query.
class ConformingMethodListResult {
public:
/// The decl context of the parsed expression.
DeclContext *DC;
/// The resolved type of the expression.
Type ExprType;
/// Methods which satisfy the criteria.
SmallVector<ValueDecl *, 0> Members;
ConformingMethodListResult(DeclContext *DC, Type ExprType)
: DC(DC), ExprType(ExprType) {}
};
/// An abstract base class for consumers of context info results.
class ConformingMethodListConsumer {
public:
virtual ~ConformingMethodListConsumer() {}
virtual void handleResult(const ConformingMethodListResult &result) = 0;
};
/// Printing consumer
class PrintingConformingMethodListConsumer
: public ConformingMethodListConsumer {
llvm::raw_ostream &OS;
public:
PrintingConformingMethodListConsumer(llvm::raw_ostream &OS) : OS(OS) {}
void handleResult(const ConformingMethodListResult &result) override;
};
/// Create a factory for code completion callbacks.
CodeCompletionCallbacksFactory *makeConformingMethodListCallbacksFactory(
ArrayRef<const char *> expectedTypeNames,
ConformingMethodListConsumer &Consumer);
} // namespace ide
} // namespace swift
#endif // SWIFT_IDE_CONFORMINGMETHODLIST_H

View File

@@ -117,35 +117,35 @@ public:
/// Complete the whole expression. This is a fallback that should
/// produce results when more specific completion methods failed.
virtual void completeExpr() = 0;
virtual void completeExpr() {};
/// Complete expr-dot after we have consumed the dot.
virtual void completeDotExpr(Expr *E, SourceLoc DotLoc) = 0;
virtual void completeDotExpr(Expr *E, SourceLoc DotLoc) {};
/// Complete the beginning of a statement or expression.
virtual void completeStmtOrExpr() = 0;
virtual void completeStmtOrExpr() {};
/// Complete the beginning of expr-postfix -- no tokens provided
/// by user.
virtual void completePostfixExprBeginning(CodeCompletionExpr *E) = 0;
virtual void completePostfixExprBeginning(CodeCompletionExpr *E) {};
/// Complete the beginning of expr-postfix in a for-each loop sequqence
/// -- no tokens provided by user.
virtual void completeForEachSequenceBeginning(CodeCompletionExpr *E) = 0;
virtual void completeForEachSequenceBeginning(CodeCompletionExpr *E) {};
/// Complete a given expr-postfix.
virtual void completePostfixExpr(Expr *E, bool hasSpace) = 0;
virtual void completePostfixExpr(Expr *E, bool hasSpace) {};
/// Complete a given expr-postfix, given that there is a following
/// left parenthesis.
virtual void completePostfixExprParen(Expr *E, Expr *CodeCompletionE) = 0;
virtual void completePostfixExprParen(Expr *E, Expr *CodeCompletionE) {};
/// Complete expr-super after we have consumed the 'super' keyword.
virtual void completeExprSuper(SuperRefExpr *SRE) = 0;
virtual void completeExprSuper(SuperRefExpr *SRE) {};
/// Complete expr-super after we have consumed the 'super' keyword and
/// a dot.
virtual void completeExprSuperDot(SuperRefExpr *SRE) = 0;
virtual void completeExprSuperDot(SuperRefExpr *SRE) {};
/// Complete the argument to an Objective-C #keyPath
/// expression.
@@ -153,59 +153,59 @@ public:
/// \param KPE A partial #keyPath expression that can be used to
/// provide context. This will be \c NULL if no components of the
/// #keyPath argument have been parsed yet.
virtual void completeExprKeyPath(KeyPathExpr *KPE, SourceLoc DotLoc) = 0;
virtual void completeExprKeyPath(KeyPathExpr *KPE, SourceLoc DotLoc) {};
/// Complete the beginning of type-simple -- no tokens provided
/// by user.
virtual void completeTypeSimpleBeginning() = 0;
virtual void completeTypeSimpleBeginning() {};
/// Complete a given type-identifier after we have consumed the dot.
virtual void completeTypeIdentifierWithDot(IdentTypeRepr *ITR) = 0;
virtual void completeTypeIdentifierWithDot(IdentTypeRepr *ITR) {};
/// Complete a given type-identifier when there is no trailing dot.
virtual void completeTypeIdentifierWithoutDot(IdentTypeRepr *ITR) = 0;
virtual void completeTypeIdentifierWithoutDot(IdentTypeRepr *ITR) {};
/// Complete at the beginning of a case stmt pattern.
virtual void completeCaseStmtBeginning() = 0;
virtual void completeCaseStmtBeginning() {};
/// Complete a case stmt pattern that starts with a dot.
virtual void completeCaseStmtDotPrefix() = 0;
virtual void completeCaseStmtDotPrefix() {};
/// Complete at the beginning of member of a nominal decl member -- no tokens
/// provided by user.
virtual void completeNominalMemberBeginning(
SmallVectorImpl<StringRef> &Keywords) = 0;
SmallVectorImpl<StringRef> &Keywords) {};
/// Complete at the beginning of accessor in a accessor block.
virtual void completeAccessorBeginning() = 0;
virtual void completeAccessorBeginning() {};
/// Complete the keyword in attribute, for instance, @available.
virtual void completeDeclAttrKeyword(Decl *D, bool Sil, bool Param) = 0;
virtual void completeDeclAttrKeyword(Decl *D, bool Sil, bool Param) {};
/// Complete the parameters in attribute, for instance, version specifier for
/// @available.
virtual void completeDeclAttrParam(DeclAttrKind DK, int Index) = 0;
virtual void completeDeclAttrParam(DeclAttrKind DK, int Index) {};
/// Complete within a precedence group decl or after a colon in an
/// operator decl.
virtual void completeInPrecedenceGroup(SyntaxKind SK) = 0;
virtual void completeInPrecedenceGroup(SyntaxKind SK) {};
/// Complete the platform names inside #available statements.
virtual void completePoundAvailablePlatform() = 0;
virtual void completePoundAvailablePlatform() {};
/// Complete the import decl with importable modules.
virtual void
completeImportDecl(std::vector<std::pair<Identifier, SourceLoc>> &Path) = 0;
completeImportDecl(std::vector<std::pair<Identifier, SourceLoc>> &Path) {};
/// Complete unresolved members after dot.
virtual void completeUnresolvedMember(CodeCompletionExpr *E,
SourceLoc DotLoc) = 0;
SourceLoc DotLoc) {};
virtual void completeAssignmentRHS(AssignExpr *E) = 0;
virtual void completeAssignmentRHS(AssignExpr *E) {};
virtual void completeCallArg(CodeCompletionExpr *E) = 0;
virtual void completeCallArg(CodeCompletionExpr *E) {};
virtual void completeReturnStmt(CodeCompletionExpr *E) = 0;
virtual void completeReturnStmt(CodeCompletionExpr *E) {};
/// Complete a yield statement. A missing yield index means that the
/// completion immediately follows the 'yield' keyword; it may be either
@@ -213,18 +213,18 @@ public:
/// index means that the completion is within the parentheses and is
/// for a specific yield value.
virtual void completeYieldStmt(CodeCompletionExpr *E,
Optional<unsigned> yieldIndex) = 0;
Optional<unsigned> yieldIndex) {};
virtual void completeAfterPoundExpr(CodeCompletionExpr *E,
Optional<StmtKind> ParentKind) = 0;
Optional<StmtKind> ParentKind) {};
virtual void completeAfterPoundDirective() = 0;
virtual void completeAfterPoundDirective() {};
virtual void completePlatformCondition() = 0;
virtual void completePlatformCondition() {};
virtual void completeAfterIfStmt(bool hasElse) = 0;
virtual void completeAfterIfStmt(bool hasElse) {};
virtual void completeGenericParams(TypeLoc TL) = 0;
virtual void completeGenericParams(TypeLoc TL) {};
/// Signals that the AST for the all the delayed-parsed code was
/// constructed. No \c complete*() callbacks will be done after this.

View File

@@ -2,6 +2,7 @@ add_swift_host_library(swiftIDE STATIC
CodeCompletion.cpp
CodeCompletionCache.cpp
CommentConversion.cpp
ConformingMethodList.cpp
ExprContextAnalysis.cpp
Formatting.cpp
Refactoring.cpp

View File

@@ -374,48 +374,6 @@ static Stmt *findNearestStmt(const DeclContext *DC, SourceLoc Loc,
const_cast<DeclContext *>(DC)->walkContext(Finder);
return Finder.getFoundStmt();
}
/// Prepare the given expression for type-checking again, prinicipally by
/// erasing any ErrorType types on the given expression, allowing later
/// type-checking to make progress.
///
/// FIXME: this is fundamentally a workaround for the fact that we may end up
/// typechecking parts of an expression more than once - first for checking
/// the context, and later for checking more-specific things like unresolved
/// members. We should restructure code-completion type-checking so that we
/// never typecheck more than once (or find a more principled way to do it).
static void prepareForRetypechecking(Expr *E) {
assert(E);
struct Eraser : public ASTWalker {
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
if (expr && expr->getType() && (expr->getType()->hasError() ||
expr->getType()->hasUnresolvedType()))
expr->setType(Type());
if (auto *ACE = dyn_cast_or_null<AutoClosureExpr>(expr)) {
return { true, ACE->getSingleExpressionBody() };
}
return { true, expr };
}
bool walkToTypeLocPre(TypeLoc &TL) override {
if (TL.getType() && (TL.getType()->hasError() ||
TL.getType()->hasUnresolvedType()))
TL.setType(Type());
return true;
}
std::pair<bool, Pattern*> walkToPatternPre(Pattern *P) override {
if (P && P->hasType() && (P->getType()->hasError() ||
P->getType()->hasUnresolvedType())) {
P->setType(Type());
}
return { true, P };
}
std::pair<bool, Stmt *> walkToStmtPre(Stmt *S) override {
return { false, S };
}
};
E->walk(Eraser());
}
CodeCompletionString::CodeCompletionString(ArrayRef<Chunk> Chunks) {
std::uninitialized_copy(Chunks.begin(), Chunks.end(),

View File

@@ -0,0 +1,241 @@
//===--- 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 resolveExpectedTypes(ArrayRef<const char *> names, SourceLoc loc,
SmallVectorImpl<ProtocolDecl *> &result);
void getMatchingMethods(Type T, ArrayRef<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>()) {
prepareForRetypechecking(ParsedExpr);
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;
SmallVector<ProtocolDecl *, 4> expectedProtocols;
resolveExpectedTypes(ExpectedTypeNames, ParsedExpr->getLoc(),
expectedProtocols);
// Collect the matching methods.
ConformingMethodListResult result(CurDeclContext, T);
getMatchingMethods(T, expectedProtocols, result.Members);
Consumer.handleResult(result);
}
void ConformingMethodListCallbacks::resolveExpectedTypes(
ArrayRef<const char *> names, SourceLoc loc,
SmallVectorImpl<ProtocolDecl *> &result) {
auto &ctx = CurDeclContext->getASTContext();
for (auto name : names) {
if (auto ty = Demangle::getTypeForMangling(ctx, name)) {
if (auto Proto = dyn_cast_or_null<ProtocolDecl>(ty->getAnyGeneric()))
result.push_back(Proto);
}
}
}
void ConformingMethodListCallbacks::getMatchingMethods(
Type T, ArrayRef<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.
ArrayRef<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,
ArrayRef<ProtocolDecl *> expectedTypes,
SmallVectorImpl<ValueDecl *> &result)
: CurModule(DC->getParentModule()), T(T), ExpectedTypes(expectedTypes),
Result(result) {}
void foundDecl(ValueDecl *VD, DeclVisibilityKind reason) {
if (isMatchingMethod(VD) && !VD->shouldHideFromEditor())
Result.push_back(VD);
}
} LocalConsumer(CurDeclContext, T, expectedTypes, result);
lookupVisibleMemberDecls(LocalConsumer, MetatypeType::get(T), CurDeclContext,
CurDeclContext->getASTContext().getLazyResolver(),
/*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);
}

View File

@@ -30,6 +30,53 @@
using namespace swift;
using namespace ide;
//===----------------------------------------------------------------------===//
// prepareForRetypechecking(Expr *)
//===----------------------------------------------------------------------===//
/// Prepare the given expression for type-checking again, prinicipally by
/// erasing any ErrorType types on the given expression, allowing later
/// type-checking to make progress.
///
/// FIXME: this is fundamentally a workaround for the fact that we may end up
/// typechecking parts of an expression more than once - first for checking
/// the context, and later for checking more-specific things like unresolved
/// members. We should restructure code-completion type-checking so that we
/// never typecheck more than once (or find a more principled way to do it).
void swift::ide::prepareForRetypechecking(Expr *E) {
assert(E);
struct Eraser : public ASTWalker {
std::pair<bool, Expr *> walkToExprPre(Expr *expr) override {
if (expr && expr->getType() && (expr->getType()->hasError() ||
expr->getType()->hasUnresolvedType()))
expr->setType(Type());
if (auto *ACE = dyn_cast_or_null<AutoClosureExpr>(expr)) {
return { true, ACE->getSingleExpressionBody() };
}
return { true, expr };
}
bool walkToTypeLocPre(TypeLoc &TL) override {
if (TL.getType() && (TL.getType()->hasError() ||
TL.getType()->hasUnresolvedType()))
TL.setType(Type());
return true;
}
std::pair<bool, Pattern*> walkToPatternPre(Pattern *P) override {
if (P && P->hasType() && (P->getType()->hasError() ||
P->getType()->hasUnresolvedType())) {
P->setType(Type());
}
return { true, P };
}
std::pair<bool, Stmt *> walkToStmtPre(Stmt *S) override {
return { false, S };
}
};
E->walk(Eraser());
}
//===----------------------------------------------------------------------===//
// typeCheckContextUntil(DeclContext, SourceLoc)
//===----------------------------------------------------------------------===//

View File

@@ -25,6 +25,11 @@ class AnyFunctionType;
namespace ide {
/// Prepare the given expression for type-checking again, prinicipally by
/// erasing any ErrorType types on the given expression, allowing later
/// type-checking to make progress.
void prepareForRetypechecking(Expr *E);
/// Type check parent contexts of the given decl context, and the body of the
/// given context until \c Loc if the context is a function body.
void typeCheckContextUntil(DeclContext *DC, SourceLoc Loc);

View File

@@ -35,45 +35,10 @@ public:
ContextInfoCallbacks(Parser &P, TypeContextInfoConsumer &Consumer)
: CodeCompletionCallbacks(P), Consumer(Consumer) {}
void completeExpr() override{};
// Ignore callbacks for suffix completions
// {
void completeDotExpr(Expr *E, SourceLoc DotLoc) override {};
void completePostfixExpr(Expr *E, bool hasSpace) override {};
void completeExprSuper(SuperRefExpr *SRE) override {};
void completeExprSuperDot(SuperRefExpr *SRE) override {};
// }
// Ignore non-expression callbacks.
// {
void completeInPrecedenceGroup(SyntaxKind SK) override {};
void completePoundAvailablePlatform() override {};
void completeExprKeyPath(KeyPathExpr *KPE, SourceLoc DotLoc) override {}
void completeTypeSimpleBeginning() override {}
void completeTypeIdentifierWithDot(IdentTypeRepr *ITR) override {}
void completeTypeIdentifierWithoutDot(IdentTypeRepr *ITR) override {}
void completeDeclAttrKeyword(Decl *D, bool Sil, bool Param) override {}
void completeDeclAttrParam(DeclAttrKind DK, int Index) override {}
void completeNominalMemberBeginning(
SmallVectorImpl<StringRef> &Keywords) override {}
void completeImportDecl(
std::vector<std::pair<Identifier, SourceLoc>> &Path) override {}
void completeAfterPoundExpr(CodeCompletionExpr *E,
Optional<StmtKind> ParentKind) override {}
void completeAfterPoundDirective() override {}
void completePlatformCondition() override {}
void completeGenericParams(TypeLoc TL) override {}
void completeAfterIfStmt(bool hasElse) override {}
void completeAccessorBeginning() override {};
// }
void completeStmtOrExpr() override {};
void completePostfixExprBeginning(CodeCompletionExpr *E) override;
void completeForEachSequenceBeginning(CodeCompletionExpr *E) override;
void completeCaseStmtBeginning() override;
void completeAssignmentRHS(AssignExpr *E) override {};
void completeCallArg(CodeCompletionExpr *E) override;
void completeReturnStmt(CodeCompletionExpr *E) override;
void completeYieldStmt(CodeCompletionExpr *E,
@@ -83,8 +48,6 @@ public:
SourceLoc DotLoc) override;
void completeCaseStmtDotPrefix() override;
void completePostfixExprParen(Expr *E, Expr *CodeCompletionE) override{};
void doneParsing() override;
};

View File

@@ -0,0 +1,49 @@
// RUN: %target-swift-ide-test -conforming-methods -source-filename %s -code-completion-token=TEST_1 -module-name MyModule -conforming-methods-expected-types '$s8MyModule7Target1PD' -conforming-methods-expected-types '$s8MyModule7Target2PD' | %FileCheck %s -check-prefix=CHECK_1
// RUN: %target-swift-ide-test -conforming-methods -source-filename %s -code-completion-token=TEST_2 -module-name MyModule -conforming-methods-expected-types '$s8MyModule7Target1PD' -conforming-methods-expected-types '$s8MyModule7Target2PD' | %FileCheck %s -check-prefix=CHECK_1
// RUN: %target-swift-ide-test -conforming-methods -source-filename %s -code-completion-token=TEST_3 -module-name MyModule -conforming-methods-expected-types '$s8MyModule7Target1PD' -conforming-methods-expected-types '$s8MyModule7Target2PD' | %FileCheck %s -check-prefix=CHECK_1
// RUN: %target-swift-ide-test -conforming-methods -source-filename %s -code-completion-token=TEST_4 -module-name MyModule -conforming-methods-expected-types '$s8MyModule7Target1PD' -conforming-methods-expected-types '$s8MyModule7Target2PD' | %FileCheck %s -check-prefix=CHECK_1
protocol Target1 {
associatedType Assoc
}
protocol Target2 {}
protocol Target3 {}
struct ConcreteTarget1 : Target1 {}
struct ConcreteTarget2 : Target2 {}
struct ConcreteTarget3 : Target3 {}
struct C {
func returnsConcreteTarget1() -> ConcreteTarget1 { fatalError() }
func returnsExistentialTarget1() -> Target1 { fatalError() }
}
protocol P {
func returnsConcreteTarget2() -> ConcreteTarget2
func returnsConcreteTarget3() -> ConcreteTarget3
}
extension P {
func returnSelf() -> Self { return self }
func returnsConcreteTarget2() -> ConcreteTarget2 { fatalError() }
func returnsConcreteTarget3() -> ConcreteTarget3 { fatalError() }
}
extension C : P {}
func testing(obj: C) {
let _ = obj #^TEST_1^#
let _ = obj .#^TEST_2^#
let _ = obj.returnSelf()#^TEST_3^#
let _ = obj.returnSelf().#^TEST_4^#
}
// CHECK_1: -----BEGIN CONFORMING METHOD LIST-----
// CHECK_1_NEXT: - TypeName: C
// CHECK_1_NEXT: - Members:
// CHECK_1_NEXT: - Name: returnsConcreteTarget1()
// CHECK_1_NEXT: TypeName: ConcreteTarget1
// CHECK_1_NEXT: - Name: returnsConcreteTarget1()
// CHECK_1_NEXT: TypeName: ConcreteTarget1
// CHECK_1_NEXT: -----END CONFORMING METHOD LIST-----

View File

@@ -0,0 +1,30 @@
protocol Target1 {}
protocol Target2 {}
protocol Target3 {}
struct ConcreteTarget1 : Target1 {}
struct ConcreteTarget2 : Target2 {}
struct ConcreteTarget3 : Target3 {}
protocol P {
associatedtype Assoc
func protocolMethod(asc: Assoc) -> Self
}
extension P {
func protocolMethod(asc: Assoc) -> Self { return self }
}
class C : P {
typealias Assoc = String
static func staticMethod() -> Self {}
func instanceMethod(x: Int) -> C {}
func methodForTarget1() -> ConcreteTarget1 {}
func methodForTarget2() -> ConcreteTarget2 {}
}
func testing(obj: C) {
let _ = obj.
}
// RUN: %sourcekitd-test -req=conformingmethods -pos=26:14 %s -req-opts=expectedtypes='$s8MyModule7Target2PD;$s8MyModule7Target1PD' -- -module-name MyModule %s > %t.response
// RUN: diff -u %s.response %t.response

View File

@@ -0,0 +1,20 @@
{
key.typename: "C",
key.typeusr: "$s8MyModule1CCD",
key.members: [
{
key.name: "methodForTarget1()",
key.sourcetext: "methodForTarget1()",
key.description: "methodForTarget1()",
key.typename: "ConcreteTarget1",
key.typeusr: "$s8MyModule15ConcreteTarget1VD"
},
{
key.name: "methodForTarget2()",
key.sourcetext: "methodForTarget2()",
key.description: "methodForTarget2()",
key.typename: "ConcreteTarget2",
key.typeusr: "$s8MyModule15ConcreteTarget2VD"
}
]
}

View File

@@ -533,6 +533,31 @@ public:
virtual void failed(StringRef ErrDescription) = 0;
};
struct ConformingMethodListResult {
StringRef TypeName;
StringRef TypeUSR;
struct Member {
StringRef Name;
StringRef TypeName;
StringRef TypeUSR;
StringRef Description;
StringRef SourceText;
StringRef DocBrief;
};
ArrayRef<Member> Members;
};
class ConformingMethodListConsumer {
virtual void anchor();
public:
virtual ~ConformingMethodListConsumer() {}
virtual void handleResult(const ConformingMethodListResult &Result) = 0;
virtual void failed(StringRef ErrDescription) = 0;
};
class LangSupport {
virtual void anchor();
@@ -693,6 +718,12 @@ public:
ArrayRef<const char *> Args,
TypeContextInfoConsumer &Consumer) = 0;
virtual void getConformingMethodList(llvm::MemoryBuffer *inputBuf,
unsigned Offset,
ArrayRef<const char *> Args,
ArrayRef<const char *> ExpectedTypes,
ConformingMethodListConsumer &Consumer) = 0;
virtual void getStatistics(StatisticsReceiver) = 0;
};

View File

@@ -20,4 +20,5 @@ void EditorConsumer::anchor() { }
void OptionsDictionary::anchor() {}
void DocInfoConsumer::anchor() { }
void TypeContextInfoConsumer::anchor() { }
void ConformingMethodListConsumer::anchor() { }
void LangSupport::anchor() { }

View File

@@ -2,6 +2,7 @@ add_sourcekit_library(SourceKitSwiftLang
CodeCompletionOrganizer.cpp
SwiftASTManager.cpp
SwiftCompletion.cpp
SwiftConformingMethodList.cpp
SwiftDocSupport.cpp
SwiftEditor.cpp
SwiftEditorInterfaceGen.cpp

View File

@@ -0,0 +1,213 @@
//===--- SwiftConformingMethodList.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 "SwiftASTManager.h"
#include "SwiftEditorDiagConsumer.h"
#include "SwiftLangSupport.h"
#include "swift/Frontend/Frontend.h"
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
#include "swift/IDE/ConformingMethodList.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Comment.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Type.h"
using namespace SourceKit;
using namespace swift;
using namespace ide;
static bool swiftConformingMethodListImpl(
SwiftLangSupport &Lang, llvm::MemoryBuffer *UnresolvedInputFile,
unsigned Offset, ArrayRef<const char *> Args,
ArrayRef<const char *> ExpectedTypeNames,
ide::ConformingMethodListConsumer &Consumer, std::string &Error) {
auto bufferIdentifier =
Lang.resolvePathSymlinks(UnresolvedInputFile->getBufferIdentifier());
auto origOffset = Offset;
auto newBuffer = SwiftLangSupport::makeCodeCompletionMemoryBuffer(
UnresolvedInputFile, Offset, bufferIdentifier);
CompilerInstance CI;
PrintingDiagnosticConsumer PrintDiags;
CI.addDiagnosticConsumer(&PrintDiags);
EditorDiagConsumer TraceDiags;
trace::TracedOperation TracedOp(trace::OperationKind::CodeCompletion);
if (TracedOp.enabled()) {
CI.addDiagnosticConsumer(&TraceDiags);
trace::SwiftInvocation SwiftArgs;
trace::initTraceInfo(SwiftArgs, bufferIdentifier, Args);
TracedOp.setDiagnosticProvider(
[&TraceDiags](SmallVectorImpl<DiagnosticEntryInfo> &diags) {
TraceDiags.getAllDiagnostics(diags);
});
TracedOp.start(
SwiftArgs,
{std::make_pair("OriginalOffset", std::to_string(origOffset)),
std::make_pair("Offset", std::to_string(Offset))});
}
CompilerInvocation Invocation;
bool Failed = Lang.getASTManager()->initCompilerInvocation(
Invocation, Args, CI.getDiags(), bufferIdentifier, Error);
if (Failed)
return false;
if (!Invocation.getFrontendOptions().InputsAndOutputs.hasInputs()) {
Error = "no input filenames specified";
return false;
}
Invocation.setCodeCompletionPoint(newBuffer.get(), Offset);
// Create a factory for code completion callbacks that will feed the
// Consumer.
std::unique_ptr<CodeCompletionCallbacksFactory> callbacksFactory(
ide::makeConformingMethodListCallbacksFactory(ExpectedTypeNames,
Consumer));
Invocation.setCodeCompletionFactory(callbacksFactory.get());
if (CI.setup(Invocation)) {
// FIXME: error?
return true;
}
CI.performSema();
return true;
}
void SwiftLangSupport::getConformingMethodList(
llvm::MemoryBuffer *UnresolvedInputFile, unsigned Offset,
ArrayRef<const char *> Args, ArrayRef<const char *> ExpectedTypeNames,
SourceKit::ConformingMethodListConsumer &SKConsumer) {
class Consumer : public ide::ConformingMethodListConsumer {
SourceKit::ConformingMethodListConsumer &SKConsumer;
public:
Consumer(SourceKit::ConformingMethodListConsumer &SKConsumer)
: SKConsumer(SKConsumer) {}
/// Convert an IDE result to a SK result and send it to \c SKConsumer .
void handleResult(const ide::ConformingMethodListResult &Result) {
SmallString<512> SS;
llvm::raw_svector_ostream OS(SS);
unsigned TypeNameBegin = SS.size();
Result.ExprType.print(OS);
unsigned TypeNameLength = SS.size() - TypeNameBegin;
unsigned TypeUSRBegin = SS.size();
SwiftLangSupport::printTypeUSR(Result.ExprType, OS);
unsigned TypeUSRLength = SS.size() - TypeUSRBegin;
struct MemberInfo {
size_t DeclNameBegin = 0;
size_t DeclNameLength = 0;
size_t TypeNameBegin = 0;
size_t TypeNameLength = 0;
size_t TypeUSRBegin = 0;
size_t TypeUSRLength = 0;
size_t DescriptionBegin = 0;
size_t DescriptionLength = 0;
size_t SourceTextBegin = 0;
size_t SourceTextLength = 0;
StringRef BriefComment;
MemberInfo() {}
};
SmallVector<MemberInfo, 8> Members;
for (auto member : Result.Members) {
Members.emplace_back();
auto &memberElem = Members.back();
auto funcTy = cast<FuncDecl>(member)->getMethodInterfaceType();
funcTy = Result.ExprType->getTypeOfMember(Result.DC->getParentModule(),
member, funcTy);
auto resultTy = funcTy->castTo<FunctionType>()->getResult();
// Name.
memberElem.DeclNameBegin = SS.size();
member->getFullName().print(OS);
memberElem.DeclNameLength = SS.size() - memberElem.DeclNameBegin;
// Type name.
memberElem.TypeNameBegin = SS.size();
resultTy.print(OS);
memberElem.TypeNameLength = SS.size() - memberElem.TypeNameBegin;
// Type USR.
memberElem.TypeUSRBegin = SS.size();
SwiftLangSupport::printTypeUSR(resultTy, OS);
memberElem.TypeUSRLength = SS.size() - memberElem.TypeUSRBegin;
// Description.
memberElem.DescriptionBegin = SS.size();
SwiftLangSupport::printMemberDeclDescription(
member, Result.ExprType, /*usePlaceholder=*/false, OS);
memberElem.DescriptionLength =
SS.size() - memberElem.DescriptionBegin;
// Sourcetext.
memberElem.SourceTextBegin = SS.size();
SwiftLangSupport::printMemberDeclDescription(
member, Result.ExprType, /*usePlaceholder=*/true, OS);
memberElem.SourceTextLength =
SS.size() - memberElem.SourceTextBegin;
// DocBrief.
auto MaybeClangNode = member->getClangNode();
if (MaybeClangNode) {
if (auto *D = MaybeClangNode.getAsDecl()) {
const auto &ClangContext = D->getASTContext();
if (const clang::RawComment *RC =
ClangContext.getRawCommentForAnyRedecl(D))
memberElem.BriefComment = RC->getBriefText(ClangContext);
}
} else {
memberElem.BriefComment = member->getBriefComment();
}
}
SourceKit::ConformingMethodListResult SKResult;
SmallVector<SourceKit::ConformingMethodListResult::Member, 8>
SKMembers;
for (auto info : Members) {
StringRef Name(SS.begin() + info.DeclNameBegin, info.DeclNameLength);
StringRef TypeName(SS.begin() + info.TypeNameBegin,
info.TypeNameLength);
StringRef TypeUSR(SS.begin() + info.TypeUSRBegin, info.TypeUSRLength);
StringRef Description(SS.begin() + info.DescriptionBegin,
info.DescriptionLength);
StringRef SourceText(SS.begin() + info.SourceTextBegin,
info.SourceTextLength);
SKMembers.push_back({Name, TypeName, TypeUSR, Description, SourceText,
info.BriefComment});
}
SKResult.TypeName = StringRef(SS.begin() + TypeNameBegin, TypeNameLength);
SKResult.TypeUSR = StringRef(SS.begin() + TypeUSRBegin, TypeUSRLength);
SKResult.Members = SKMembers;
SKConsumer.handleResult(SKResult);
}
} Consumer(SKConsumer);
std::string Error;
if (!swiftConformingMethodListImpl(*this, UnresolvedInputFile, Offset, Args,
ExpectedTypeNames, Consumer, Error)) {
SKConsumer.failed(Error);
}
}

View File

@@ -18,6 +18,7 @@
#include "swift/AST/ASTVisitor.h"
#include "swift/AST/ClangModuleLoader.h"
#include "swift/AST/ParameterList.h"
#include "swift/AST/SILOptions.h"
#include "swift/AST/USRGeneration.h"
#include "swift/Config.h"
@@ -190,6 +191,26 @@ SwiftLangSupport::SwiftLangSupport(SourceKit::Context &SKCtx)
SwiftLangSupport::~SwiftLangSupport() {
}
std::unique_ptr<llvm::MemoryBuffer>
SwiftLangSupport::makeCodeCompletionMemoryBuffer(
const llvm::MemoryBuffer *origBuf, unsigned &Offset,
const std::string bufferIdentifier) {
auto origBuffSize = origBuf->getBufferSize();
if (Offset > origBuffSize)
Offset = origBuffSize;
auto newBuffer = llvm::WritableMemoryBuffer::getNewUninitMemBuffer(
origBuffSize + 1, bufferIdentifier);
auto *pos = origBuf->getBufferStart() + Offset;
auto *newPos =
std::copy(origBuf->getBufferStart(), pos, newBuffer->getBufferStart());
*newPos = '\0';
std::copy(pos, origBuf->getBufferEnd(), newPos + 1);
return std::unique_ptr<llvm::MemoryBuffer>(newBuffer.release());
}
UIdent SwiftLangSupport::getUIDForDecl(const Decl *D, bool IsRef) {
return UIdentVisitor(IsRef).visit(const_cast<Decl*>(D));
}
@@ -750,6 +771,71 @@ bool SwiftLangSupport::printAccessorUSR(const AbstractStorageDecl *D,
return ide::printAccessorUSR(D, AccKind, OS);
}
void SwiftLangSupport::printMemberDeclDescription(const swift::ValueDecl *VD,
swift::Type baseTy,
bool usePlaceholder,
llvm::raw_ostream &OS) {
// Base name.
OS << VD->getBaseName().userFacingName();
// Parameters.
auto *M = VD->getModuleContext();
auto substMap = baseTy->getMemberSubstitutionMap(M, VD);
auto printSingleParam = [&](ParamDecl *param) {
auto paramTy = param->getInterfaceType();
// Label.
if (!param->getArgumentName().empty())
OS << param->getArgumentName() << ": ";
// InOut.
if (param->isInOut()) {
OS << "&";
paramTy = paramTy->getInOutObjectType();
}
// Type.
if (usePlaceholder)
OS << "<#T##";
if (auto substitutedTy = paramTy.subst(substMap))
paramTy = substitutedTy;
if (paramTy->hasError() && param->getTypeLoc().hasLocation()) {
// Fallback to 'TypeRepr' printing.
param->getTypeLoc().getTypeRepr()->print(OS);
} else {
paramTy.print(OS);
}
if (usePlaceholder)
OS << "#>";
};
auto printParams = [&](const ParameterList *params) {
OS << '(';
bool isFirst = true;
for (auto param : params->getArray()) {
if (isFirst)
isFirst = false;
else
OS << ", ";
printSingleParam(param);
}
OS << ')';
};
if (auto EED = dyn_cast<EnumElementDecl>(VD)) {
if (auto params = EED->getParameterList())
printParams(params);
} else if (auto *FD = dyn_cast<FuncDecl>(VD)) {
if (auto params = FD->getParameters())
printParams(params);
} else if (isa<VarDecl>(VD)) {
// Var decl doesn't have parameters.
} else {
llvm_unreachable("Unsupported Decl kind for printMemberDeclDescription()");
}
}
std::string SwiftLangSupport::resolvePathSymlinks(StringRef FilePath) {
std::string InputPath = FilePath;
#if !defined(_WIN32)

View File

@@ -306,6 +306,13 @@ public:
return CCCache;
}
/// Copy a memory buffer inserting '0' at the position of \c origBuf.
// TODO: Share with code completion.
static std::unique_ptr<llvm::MemoryBuffer>
makeCodeCompletionMemoryBuffer(const llvm::MemoryBuffer *origBuf,
unsigned &Offset,
const std::string bufferIdentifier);
static SourceKit::UIdent getUIDForDecl(const swift::Decl *D,
bool IsRef = false);
static SourceKit::UIdent getUIDForExtensionOfDecl(const swift::Decl *D);
@@ -381,6 +388,14 @@ public:
static void
printFullyAnnotatedGenericReq(const swift::GenericSignature *Sig,
llvm::raw_ostream &OS);
/// Print 'description' or 'sourcetext' the given \p VD to \p OS. If
/// \p usePlaceholder is \c true, call argument positions are substituted with
/// a typed editor placeholders which is suitable for 'sourcetext'.
static void
printMemberDeclDescription(const swift::ValueDecl *VD, swift::Type baseTy,
bool usePlaceholder, llvm::raw_ostream &OS);
/// Tries to resolve the path to the real file-system path. If it fails it
/// returns the original path;
static std::string resolvePathSymlinks(StringRef FilePath);
@@ -528,6 +543,11 @@ public:
ArrayRef<const char *> Args,
TypeContextInfoConsumer &Consumer) override;
void getConformingMethodList(llvm::MemoryBuffer *inputBuf, unsigned Offset,
ArrayRef<const char *> Args,
ArrayRef<const char *> ExpectedTypes,
ConformingMethodListConsumer &Consumer) override;
void getStatistics(StatisticsReceiver) override;
private:

View File

@@ -24,28 +24,6 @@ using namespace SourceKit;
using namespace swift;
using namespace ide;
/// Copy a memory buffer inserting '0' at the position of \c origBuf.
// TODO: Share with code completion.
static std::unique_ptr<llvm::MemoryBuffer>
makeCodeCompletionMemoryBuffer(const llvm::MemoryBuffer *origBuf,
unsigned &Offset,
const std::string bufferIdentifier) {
auto origBuffSize = origBuf->getBufferSize();
if (Offset > origBuffSize)
Offset = origBuffSize;
auto newBuffer = llvm::WritableMemoryBuffer::getNewUninitMemBuffer(
origBuffSize + 1, bufferIdentifier);
auto *pos = origBuf->getBufferStart() + Offset;
auto *newPos =
std::copy(origBuf->getBufferStart(), pos, newBuffer->getBufferStart());
*newPos = '\0';
std::copy(pos, origBuf->getBufferEnd(), newPos + 1);
return std::unique_ptr<llvm::MemoryBuffer>(newBuffer.release());
}
static bool swiftTypeContextInfoImpl(SwiftLangSupport &Lang,
llvm::MemoryBuffer *UnresolvedInputFile,
unsigned Offset,
@@ -56,8 +34,8 @@ static bool swiftTypeContextInfoImpl(SwiftLangSupport &Lang,
Lang.resolvePathSymlinks(UnresolvedInputFile->getBufferIdentifier());
auto origOffset = Offset;
auto newBuffer = makeCodeCompletionMemoryBuffer(UnresolvedInputFile, Offset,
bufferIdentifier);
auto newBuffer = SwiftLangSupport::makeCodeCompletionMemoryBuffer(
UnresolvedInputFile, Offset, bufferIdentifier);
CompilerInstance CI;
PrintingDiagnosticConsumer PrintDiags;
@@ -107,12 +85,6 @@ static bool swiftTypeContextInfoImpl(SwiftLangSupport &Lang,
return true;
}
/// Print 'description' or 'sourcetext' the given \c VD to \c OS. If
/// \c usePlaceholder is true, call argument positions are substituted with
/// type editor placeholders which is suitable for 'sourcetext'.
static void printDeclDescription(llvm::raw_ostream &OS, Type baseTy,
ValueDecl *VD, bool usePlaceholder);
void SwiftLangSupport::getExpressionContextInfo(
llvm::MemoryBuffer *UnresolvedInputFile, unsigned Offset,
ArrayRef<const char *> Args,
@@ -144,15 +116,15 @@ void SwiftLangSupport::getExpressionContextInfo(
// Description.
unsigned DescriptionBegin = SS.size();
printDeclDescription(OS, Item.ExpectedTy, member,
/*usePlaceholder=*/false);
SwiftLangSupport::printMemberDeclDescription(
member, Item.ExpectedTy, /*usePlaceholder=*/false, OS);
unsigned DescriptionLength = SS.size() - DescriptionBegin;
StringRef Description(SS.begin() + DescriptionBegin, DescriptionLength);
// Sourcetext.
unsigned SourceTextBegin = SS.size();
printDeclDescription(OS, Item.ExpectedTy, member,
/*usePlaceholder=*/true);
SwiftLangSupport::printMemberDeclDescription(
member, Item.ExpectedTy, /*usePlaceholder=*/true, OS);
unsigned SourceTextLength = SS.size() - SourceTextBegin;
StringRef SourceText(SS.begin() + SourceTextBegin, SourceTextLength);
@@ -198,67 +170,3 @@ void SwiftLangSupport::getExpressionContextInfo(
SKConsumer.failed(Error);
}
}
static void printDeclDescription(llvm::raw_ostream &OS, Type baseTy,
ValueDecl *VD, bool usePlaceholder) {
// Base name.
OS << VD->getBaseName().userFacingName();
// Parameters.
auto *M = VD->getModuleContext();
auto substMap = baseTy->getMemberSubstitutionMap(M, VD);
auto printSingleParam = [&](ParamDecl *param) {
auto paramTy = param->getInterfaceType();
// Label.
if (!param->getArgumentName().empty())
OS << param->getArgumentName() << ": ";
// InOut.
if (param->isInOut()) {
OS << "&";
paramTy = paramTy->getInOutObjectType();
}
// Type.
if (usePlaceholder)
OS << "<#T##";
if (auto substitutedTy = paramTy.subst(substMap))
paramTy = substitutedTy;
if (paramTy->hasError() && param->getTypeLoc().hasLocation()) {
// Fallback to 'TypeRepr' printing.
param->getTypeLoc().getTypeRepr()->print(OS);
} else {
paramTy.print(OS);
}
if (usePlaceholder)
OS << "#>";
};
auto printParams = [&](ParameterList *params) {
OS << '(';
bool isFirst = true;
for (auto param : params->getArray()) {
if (isFirst)
isFirst = false;
else
OS << ", ";
printSingleParam(param);
}
OS << ')';
};
if (auto EED = dyn_cast<EnumElementDecl>(VD)) {
if (auto params = EED->getParameterList())
printParams(params);
} else if (auto *FD = dyn_cast<FuncDecl>(VD)) {
if (auto params = FD->getParameters())
printParams(params);
} else if (isa<VarDecl>(VD)) {
// Var decl doesn't have parameters.
} else {
llvm_unreachable("Invalid Decl type for context info implicit member");
}
}

View File

@@ -116,6 +116,7 @@ bool TestOptions::parseArgs(llvm::ArrayRef<const char *> Args) {
.Case("complete.cache.ondisk", SourceKitRequest::CodeCompleteCacheOnDisk)
.Case("complete.setpopularapi", SourceKitRequest::CodeCompleteSetPopularAPI)
.Case("typecontextinfo", SourceKitRequest::TypeContextInfo)
.Case("conformingmethods", SourceKitRequest::ConformingMethodList)
.Case("cursor", SourceKitRequest::CursorInfo)
.Case("related-idents", SourceKitRequest::RelatedIdents)
.Case("syntax-map", SourceKitRequest::SyntaxMap)

View File

@@ -33,6 +33,7 @@ enum class SourceKitRequest {
CodeCompleteCacheOnDisk,
CodeCompleteSetPopularAPI,
TypeContextInfo,
ConformingMethodList,
CursorInfo,
RangeInfo,
RelatedIdents,

View File

@@ -564,6 +564,30 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) {
sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset);
break;
case SourceKitRequest::ConformingMethodList:
sourcekitd_request_dictionary_set_uid(Req, KeyRequest,
RequestConformingMethodList);
sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset);
for (auto &Opt : Opts.RequestOptions) {
auto KeyValue = StringRef(Opt).split('=');
if (KeyValue.first == "expectedtypes") {
SmallVector<StringRef, 4> expectedTypeNames;
KeyValue.second.split(expectedTypeNames, ';');
auto typenames = sourcekitd_request_array_create(nullptr, 0);
for (auto &name : expectedTypeNames) {
std::string n = name;
sourcekitd_request_array_set_string(typenames, SOURCEKITD_ARRAY_APPEND, n.c_str());
}
sourcekitd_request_dictionary_set_value(Req, KeyExpectedTypes, typenames);
} else {
llvm::errs() << "invalid key '" << KeyValue.first << "' in -req-opts\n";
return 1;
}
}
break;
case SourceKitRequest::CursorInfo:
sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestCursorInfo);
if (Opts.CollectActionables) {
@@ -1013,6 +1037,7 @@ static bool handleResponse(sourcekitd_response_t Resp, const TestOptions &Opts,
case SourceKitRequest::CodeCompleteCacheOnDisk:
case SourceKitRequest::CodeCompleteSetPopularAPI:
case SourceKitRequest::TypeContextInfo:
case SourceKitRequest::ConformingMethodList:
sourcekitd_response_description_dump_filedesc(Resp, STDOUT_FILENO);
break;

View File

@@ -171,6 +171,11 @@ static sourcekitd_response_t typeContextInfo(llvm::MemoryBuffer *InputBuf,
int64_t Offset,
ArrayRef<const char *> Args);
static sourcekitd_response_t
conformingMethodList(llvm::MemoryBuffer *InputBuf, int64_t Offset,
ArrayRef<const char *> Args,
ArrayRef<const char *> ExpectedTypes);
static sourcekitd_response_t
editorOpen(StringRef Name, llvm::MemoryBuffer *Buf,
SKEditorConsumerOptions Opts, ArrayRef<const char *> Args);
@@ -886,6 +891,21 @@ handleSemanticRequest(RequestDict Req,
return Rec(typeContextInfo(InputBuf.get(), Offset, Args));
}
if (ReqUID == RequestConformingMethodList) {
std::unique_ptr<llvm::MemoryBuffer> InputBuf =
getInputBufForRequest(SourceFile, SourceText, ErrBuf);
if (!InputBuf)
return Rec(createErrorRequestFailed(ErrBuf.c_str()));
int64_t Offset;
if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false))
return Rec(createErrorRequestInvalid("missing 'key.offset'"));
SmallVector<const char *, 8> ExpectedTypeNames;
if (Req.getStringArray(KeyExpectedTypes, ExpectedTypeNames, true))
return Rec(createErrorRequestInvalid("invalid 'key.expectedtypes'"));
return Rec(
conformingMethodList(InputBuf.get(), Offset, Args, ExpectedTypeNames));
}
if (!SourceFile.hasValue())
return Rec(createErrorRequestInvalid("missing 'key.sourcefile'"));
@@ -2110,6 +2130,57 @@ static sourcekitd_response_t typeContextInfo(llvm::MemoryBuffer *InputBuf,
return RespBuilder.createResponse();
}
//===----------------------------------------------------------------------===//
// Conforming Method List
//===----------------------------------------------------------------------===//
static sourcekitd_response_t
conformingMethodList(llvm::MemoryBuffer *InputBuf, int64_t Offset,
ArrayRef<const char *> Args,
ArrayRef<const char *> ExpectedTypes) {
ResponseBuilder RespBuilder;
class Consumer : public ConformingMethodListConsumer {
ResponseBuilder::Dictionary SKResult;
Optional<std::string> ErrorDescription;
public:
Consumer(ResponseBuilder Builder) : SKResult(Builder.getDictionary()) {}
void handleResult(const ConformingMethodListResult &Result) override {
SKResult.set(KeyTypeName, Result.TypeName);
SKResult.set(KeyTypeUsr, Result.TypeUSR);
auto members = SKResult.setArray(KeyMembers);
for (auto member : Result.Members) {
auto memberElem = members.appendDictionary();
memberElem.set(KeyName, member.Name);
memberElem.set(KeyTypeName, member.TypeName);
memberElem.set(KeyTypeUsr, member.TypeUSR);
memberElem.set(KeyDescription, member.Description);
memberElem.set(KeySourceText, member.SourceText);
if (!member.DocBrief.empty())
memberElem.set(KeyDocBrief, member.DocBrief);
}
}
void failed(StringRef ErrDescription) override {
ErrorDescription = ErrDescription;
}
bool isError() const { return ErrorDescription.hasValue(); }
const char *getErrorDescription() const {
return ErrorDescription->c_str();
}
} Consumer(RespBuilder);
LangSupport &Lang = getGlobalContext().getSwiftLangSupport();
Lang.getConformingMethodList(InputBuf, Offset, Args, ExpectedTypes, Consumer);
if (Consumer.isError())
return createErrorRequestFailed(Consumer.getErrorDescription());
return RespBuilder.createResponse();
}
//===----------------------------------------------------------------------===//
// Editor
//===----------------------------------------------------------------------===//

View File

@@ -33,6 +33,7 @@
#include "swift/Frontend/PrintingDiagnosticConsumer.h"
#include "swift/IDE/CodeCompletion.h"
#include "swift/IDE/CommentConversion.h"
#include "swift/IDE/ConformingMethodList.h"
#include "swift/IDE/ModuleInterfacePrinting.h"
#include "swift/IDE/REPLCodeCompletion.h"
#include "swift/IDE/SourceEntityWalker.h"
@@ -101,6 +102,7 @@ enum class ActionType {
ReconstructType,
Range,
TypeContextInfo,
ConformingMethodList,
};
class NullDebuggerClient : public DebuggerClient {
@@ -225,7 +227,10 @@ Action(llvm::cl::desc("Mode:"), llvm::cl::init(ActionType::None),
"Print indexed symbol information"),
clEnumValN(ActionType::TypeContextInfo,
"type-context-info",
"Perform expression context info analysis")));
"Perform expression context info analysis"),
clEnumValN(ActionType::ConformingMethodList,
"conforming-methods",
"Perform conforming method analysis for expression")));
static llvm::cl::opt<std::string>
SourceFilename("source-filename", llvm::cl::desc("Name of the source file"),
@@ -419,6 +424,13 @@ DebugClientDiscriminator("debug-client-discriminator",
llvm::cl::desc("A discriminator to prefer in lookups"),
llvm::cl::cat(Category));
// '-conforming-methods' options.
static llvm::cl::list<std::string>
ConformingMethodListExpectedTypes("conforming-methods-expected-types",
llvm::cl::desc("Set expected types for comforming method list"),
llvm::cl::cat(Category));
// '-syntax-coloring' options.
static llvm::cl::opt<bool>
@@ -748,6 +760,70 @@ static int doTypeContextInfo(const CompilerInvocation &InitInvok,
return 0;
}
static int
doConformingMethodList(const CompilerInvocation &InitInvok,
StringRef SourceFilename, StringRef SecondSourceFileName,
StringRef CodeCompletionToken,
bool CodeCompletionDiagnostics,
const std::vector<std::string> expectedTypeNames) {
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileBufOrErr =
llvm::MemoryBuffer::getFile(SourceFilename);
if (!FileBufOrErr) {
llvm::errs() << "error opening input file: "
<< FileBufOrErr.getError().message() << '\n';
return 1;
}
unsigned Offset;
std::unique_ptr<llvm::MemoryBuffer> CleanFile(removeCodeCompletionTokens(
FileBufOrErr.get().get(), CodeCompletionToken, &Offset));
if (Offset == ~0U) {
llvm::errs() << "could not find code completion token \""
<< CodeCompletionToken << "\"\n";
return 1;
}
llvm::outs() << "found code completion token " << CodeCompletionToken
<< " at offset " << Offset << "\n";
llvm::errs() << "found code completion token " << CodeCompletionToken
<< " at offset " << Offset << "\n";
CompilerInvocation Invocation(InitInvok);
Invocation.setCodeCompletionPoint(CleanFile.get(), Offset);
SmallVector<const char *, 4> typeNames;
for (auto &name : expectedTypeNames)
typeNames.push_back(name.c_str());
// Create a CodeCompletionConsumer.
std::unique_ptr<ide::ConformingMethodListConsumer> Consumer(
new ide::PrintingConformingMethodListConsumer(llvm::outs()));
// Create a factory for code completion callbacks that will feed the
// Consumer.
std::unique_ptr<CodeCompletionCallbacksFactory> callbacksFactory(
ide::makeConformingMethodListCallbacksFactory(typeNames, *Consumer));
Invocation.setCodeCompletionFactory(callbacksFactory.get());
if (!SecondSourceFileName.empty()) {
Invocation.getFrontendOptions().InputsAndOutputs.addInputFile(
SecondSourceFileName);
}
CompilerInstance CI;
PrintingDiagnosticConsumer PrintDiags;
if (CodeCompletionDiagnostics) {
// Display diagnostics to stderr.
CI.addDiagnosticConsumer(&PrintDiags);
}
if (CI.setup(Invocation))
return 1;
CI.performSema();
return 0;
}
static int doCodeCompletion(const CompilerInvocation &InitInvok,
StringRef SourceFilename,
StringRef SecondSourceFileName,
@@ -3277,6 +3353,19 @@ int main(int argc, char *argv[]) {
options::CodeCompletionDiagnostics);
break;
case ActionType::ConformingMethodList:
if (options::CodeCompletionToken.empty()) {
llvm::errs() << "token name required\n";
return 1;
}
ExitCode = doConformingMethodList(InitInvok,
options::SourceFilename,
options::SecondSourceFilename,
options::CodeCompletionToken,
options::CodeCompletionDiagnostics,
options::ConformingMethodListExpectedTypes);
break;
case ActionType::SyntaxColoring:
ExitCode = doSyntaxColoring(InitInvok,
options::SourceFilename,

View File

@@ -161,6 +161,8 @@ UID_KEYS = [
KEY('CompileID', 'key.compileid'),
KEY('CompilerArgsString', 'key.compilerargs-string'),
KEY('ImplicitMembers', 'key.implicitmembers'),
KEY('ExpectedTypes', 'key.expectedtypes'),
KEY('Members', 'key.members'),
]
@@ -182,6 +184,7 @@ UID_REQUESTS = [
'source.request.codecomplete.setpopularapi'),
REQUEST('CodeCompleteSetCustom', 'source.request.codecomplete.setcustom'),
REQUEST('TypeContextInfo', 'source.request.typecontextinfo'),
REQUEST('ConformingMethodList', 'source.request.conformingmethods'),
REQUEST('CursorInfo', 'source.request.cursorinfo'),
REQUEST('RangeInfo', 'source.request.rangeinfo'),
REQUEST('RelatedIdents', 'source.request.relatedidents'),