mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Merge pull request #22429 from rintaro/sourcekit-conformingmethods
[IDE/SourceKit] New SourceKit request for filtered method list
This commit is contained in:
66
include/swift/IDE/ConformingMethodList.h
Normal file
66
include/swift/IDE/ConformingMethodList.h
Normal 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
|
||||
@@ -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.
|
||||
|
||||
@@ -2,6 +2,7 @@ add_swift_host_library(swiftIDE STATIC
|
||||
CodeCompletion.cpp
|
||||
CodeCompletionCache.cpp
|
||||
CommentConversion.cpp
|
||||
ConformingMethodList.cpp
|
||||
ExprContextAnalysis.cpp
|
||||
Formatting.cpp
|
||||
Refactoring.cpp
|
||||
|
||||
@@ -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(),
|
||||
|
||||
241
lib/IDE/ConformingMethodList.cpp
Normal file
241
lib/IDE/ConformingMethodList.cpp
Normal 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);
|
||||
}
|
||||
@@ -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)
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
49
test/IDE/conforming-methods-basic.swift
Normal file
49
test/IDE/conforming-methods-basic.swift
Normal 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-----
|
||||
30
test/SourceKit/ConformingMethods/basic.swift
Normal file
30
test/SourceKit/ConformingMethods/basic.swift
Normal 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
|
||||
20
test/SourceKit/ConformingMethods/basic.swift.response
Normal file
20
test/SourceKit/ConformingMethods/basic.swift.response
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -20,4 +20,5 @@ void EditorConsumer::anchor() { }
|
||||
void OptionsDictionary::anchor() {}
|
||||
void DocInfoConsumer::anchor() { }
|
||||
void TypeContextInfoConsumer::anchor() { }
|
||||
void ConformingMethodListConsumer::anchor() { }
|
||||
void LangSupport::anchor() { }
|
||||
|
||||
@@ -2,6 +2,7 @@ add_sourcekit_library(SourceKitSwiftLang
|
||||
CodeCompletionOrganizer.cpp
|
||||
SwiftASTManager.cpp
|
||||
SwiftCompletion.cpp
|
||||
SwiftConformingMethodList.cpp
|
||||
SwiftDocSupport.cpp
|
||||
SwiftEditor.cpp
|
||||
SwiftEditorInterfaceGen.cpp
|
||||
|
||||
213
tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp
Normal file
213
tools/SourceKit/lib/SwiftLang/SwiftConformingMethodList.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -33,6 +33,7 @@ enum class SourceKitRequest {
|
||||
CodeCompleteCacheOnDisk,
|
||||
CodeCompleteSetPopularAPI,
|
||||
TypeContextInfo,
|
||||
ConformingMethodList,
|
||||
CursorInfo,
|
||||
RangeInfo,
|
||||
RelatedIdents,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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'),
|
||||
|
||||
Reference in New Issue
Block a user