Skip to content

[IDE] Signature Help PoC #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions include/swift/IDE/ArgumentCompletion.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
#include "swift/IDE/CodeCompletionConsumer.h"
#include "swift/IDE/CodeCompletionContext.h"
#include "swift/IDE/PossibleParamInfo.h"
#include "swift/IDE/SignatureHelp.h"
#include "swift/IDE/TypeCheckCompletionCallback.h"
#include "swift/Sema/ConstraintSystem.h"

namespace swift {
namespace ide {
Expand Down Expand Up @@ -87,6 +89,8 @@ class ArgumentTypeCheckCompletionCallback : public TypeCheckCompletionCallback {
/// this result. This in particular includes parameters of closures that
/// were type-checked with the code completion expression.
llvm::SmallDenseMap<const VarDecl *, Type> SolutionSpecificVarTypes;

constraints::Score FixedScore;
};

CodeCompletionExpr *CompletionExpr;
Expand Down Expand Up @@ -120,6 +124,9 @@ class ArgumentTypeCheckCompletionCallback : public TypeCheckCompletionCallback {
void collectResults(bool IsLabeledTrailingClosure,
SourceLoc Loc, DeclContext *DC,
CodeCompletionContext &CompletionCtx);

// TODO(a7medev): add doc comment.
SignatureHelpResult getSignatures(SourceLoc Loc, DeclContext *DC);
};

} // end namespace ide
Expand Down
4 changes: 3 additions & 1 deletion include/swift/IDE/CommentConversion.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,11 @@ namespace ide {
/// If the declaration has a documentation comment, prints the comment to \p OS
/// in Clang-like XML format.
///
/// \param IncludeParameters if true, includes the parameters part of the documentation comment and ignores it otherwise.
///
/// \returns true if the declaration has a documentation comment.
bool getDocumentationCommentAsXML(
const Decl *D, raw_ostream &OS,
const Decl *D, raw_ostream &OS, bool IncludeParameters,
TypeOrExtensionDecl SynthesizedTarget = TypeOrExtensionDecl());

/// If the declaration has a documentation comment, prints the comment to \p OS
Expand Down
78 changes: 78 additions & 0 deletions include/swift/IDE/SignatureHelp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//===--- SignatureHelp.h --- ------------------------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2025 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_SIGNATURE_HELP_H
#define SWIFT_IDE_SIGNATURE_HELP_H

#include "swift/AST/Type.h"
#include "swift/Basic/LLVM.h"
#include "swift/IDE/TypeCheckCompletionCallback.h"

namespace swift {
class IDEInspectionCallbacksFactory;

namespace ide {

struct SignatureHelpResult {
struct Signature {
/// True if this is a subscript rather than a function call.
bool IsSubscript;

/// The FuncDecl or SubscriptDecl associated with the call.
ValueDecl *FuncD;

/// The type of the function being called.
AnyFunctionType *FuncTy;

/// The index of the argument containing the completion location
unsigned ArgIdx;

/// The index of the parameter corresponding to the completion argument.
std::optional<unsigned> ParamIdx;

/// True if the completion is a noninitial term in a variadic argument.
bool IsNoninitialVariadic;

/// The base type of the call/subscript (null for free functions).
Type BaseType;

/// The resolved type of the expression.
Type ExprType;
};

/// The decl context of the parsed expression.
DeclContext *DC;

/// The active signature.
std::optional<unsigned> ActiveSignature;

/// Suggested signatures.
SmallVector<Signature, 0> Signatures;

SignatureHelpResult(DeclContext *DC) : DC(DC) {}
};

/// An abstract base class for consumers of signatures results.
class SignatureHelpConsumer {
public:
virtual ~SignatureHelpConsumer() {}
virtual void handleResult(const SignatureHelpResult &result) = 0;
};

/// Create a factory for code completion callbacks.
IDEInspectionCallbacksFactory *makeSignatureHelpCallbacksFactory(
SignatureHelpConsumer &Consumer);

} // namespace ide
} // namespace swift

#endif // SWIFT_IDE_SIGNATURE_HELP_H
18 changes: 18 additions & 0 deletions include/swift/IDETool/IDEInspectionInstance.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "swift/IDE/CodeCompletionResult.h"
#include "swift/IDE/CodeCompletionResultSink.h"
#include "swift/IDE/ConformingMethodList.h"
#include "swift/IDE/SignatureHelp.h"
#include "swift/IDE/CursorInfo.h"
#include "swift/IDE/ImportDepth.h"
#include "swift/IDE/SwiftCompletionInfo.h"
Expand Down Expand Up @@ -80,6 +81,14 @@ struct ConformingMethodListResults {
bool DidReuseAST;
};

/// The results returned from \c IDEInspectionInstance::signatures.
struct SignatureHelpResults {
/// The actual results. If \c nullptr, no results were found.
const SignatureHelpResult *Result;
/// Whether an AST was reused to produce the results.
bool DidReuseAST;
};

/// The results returned from \c IDEInspectionInstance::cursorInfo.
struct CursorInfoResults {
/// The actual results.
Expand Down Expand Up @@ -205,6 +214,15 @@ class IDEInspectionInstance {
std::shared_ptr<std::atomic<bool>> CancellationFlag,
llvm::function_ref<void(CancellableResult<ConformingMethodListResults>)>
Callback);

void signatureHelp(
swift::CompilerInvocation &Invocation, llvm::ArrayRef<const char *> Args,
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
llvm::MemoryBuffer *ideInspectionTargetBuffer, unsigned int Offset,
DiagnosticConsumer *DiagC,
std::shared_ptr<std::atomic<bool>> CancellationFlag,
llvm::function_ref<void(CancellableResult<SignatureHelpResults>)>
Callback);

void cursorInfo(
swift::CompilerInvocation &Invocation, llvm::ArrayRef<const char *> Args,
Expand Down
45 changes: 44 additions & 1 deletion lib/IDE/ArgumentCompletion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,12 +282,16 @@ void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {
IncludeSignature = true;
}
}

SmallString<512> ToPrint;
llvm::raw_svector_ostream OS(ToPrint);
S.getFixedScore().print(OS);

Results.push_back(
{ExpectedTy, ExpectedCallType, isa<SubscriptExpr>(ParentCall),
Info.getValue(), FuncTy, ArgIdx, ParamIdx, std::move(ClaimedParams),
IsNoninitialVariadic, IncludeSignature, Info.BaseTy, HasLabel, FirstTrailingClosureIndex,
IsAsync, DeclParamIsOptional, SolutionSpecificVarTypes});
IsAsync, DeclParamIsOptional, SolutionSpecificVarTypes, S.getFixedScore()});
}

void ArgumentTypeCheckCompletionCallback::computeShadowedDecls(
Expand Down Expand Up @@ -430,3 +434,42 @@ void ArgumentTypeCheckCompletionCallback::collectResults(
*Lookup.getExpectedTypeContext(),
Lookup.canCurrDeclContextHandleAsync());
}


SignatureHelpResult ArgumentTypeCheckCompletionCallback::getSignatures(
SourceLoc Loc, DeclContext *DC) {
SmallPtrSet<ValueDecl *, 4> ShadowedDecls;
computeShadowedDecls(ShadowedDecls);

SignatureHelpResult result(DC);

if (Results.empty())
return result;

// The active signature is the signature with the lowest solution score
// TODO(a7medev): Is that the most suitable active signature?
std::optional<Score> MinScore;

for (size_t i = 0; i < Results.size(); ++i) {
auto &Result = Results[i];

// TODO(a7medev): Use the same result output mechanism in code completion
// Only show call pattern completions if the function isn't
// overridden.
if (Result.FuncD && ShadowedDecls.count(Result.FuncD) == 0) {
// TODO(a7medev): Probably avoid using a new type altogether.
result.Signatures.push_back({
Result.IsSubscript, Result.FuncD, Result.FuncTy, Result.ArgIdx,
Result.ParamIdx, Result.IsNoninitialVariadic, Result.BaseType,
Result.ExpectedType
});

if (!MinScore || Result.FixedScore < MinScore) {
result.ActiveSignature = i;
MinScore = Result.FixedScore;
}
}
}

return result;
}
1 change: 1 addition & 0 deletions lib/IDE/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ add_swift_host_library(swiftIDE STATIC
CompletionLookup.cpp
CompletionOverrideLookup.cpp
ConformingMethodList.cpp
SignatureHelp.cpp
CursorInfo.cpp
ExprCompletion.cpp
ExprContextAnalysis.cpp
Expand Down
12 changes: 9 additions & 3 deletions lib/IDE/CommentConversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ using namespace swift;
namespace {
struct CommentToXMLConverter {
raw_ostream &OS;
bool IncludeParameters;

CommentToXMLConverter(raw_ostream &OS) : OS(OS) {}
CommentToXMLConverter(raw_ostream &OS, bool IncludeParameters = true)
: OS(OS), IncludeParameters(IncludeParameters) {}

void printRawHTML(StringRef Tag) {
OS << "<rawHTML>";
Expand Down Expand Up @@ -224,6 +226,8 @@ struct CommentToXMLConverter {
}

void printParamField(const ParamField *PF) {
assert(IncludeParameters);

OS << "<Parameter>";
OS << "<Name>";
OS << PF->getName();
Expand Down Expand Up @@ -283,7 +287,7 @@ void CommentToXMLConverter::visitCommentParts(const swift::markup::CommentParts
OS << "</Abstract>";
}

if (!Parts.ParamFields.empty()) {
if (!Parts.ParamFields.empty() && IncludeParameters) {
OS << "<Parameters>";
for (const auto *PF : Parts.ParamFields)
printParamField(PF);
Expand Down Expand Up @@ -496,12 +500,14 @@ static DocComment *getCascadingDocComment(swift::markup::MarkupContext &MC,
}

bool ide::getDocumentationCommentAsXML(const Decl *D, raw_ostream &OS,
bool IncludeParameters,
TypeOrExtensionDecl SynthesizedTarget) {
auto MaybeClangNode = D->getClangNode();
if (MaybeClangNode) {
if (auto *CD = MaybeClangNode.getAsDecl()) {
std::string S;
llvm::raw_string_ostream SS(S);
// TODO(refaey): respect IncludeParameters for Clang.
if (getClangDocumentationCommentAsXML(CD, SS)) {
replaceObjcDeclarationsWithSwiftOnes(D, SS.str(), OS,
SynthesizedTarget);
Expand All @@ -516,7 +522,7 @@ bool ide::getDocumentationCommentAsXML(const Decl *D, raw_ostream &OS,
if (!DC)
return false;

CommentToXMLConverter Converter(OS);
CommentToXMLConverter Converter(OS, IncludeParameters);
Converter.visitDocComment(DC, SynthesizedTarget);

OS.flush();
Expand Down
Loading