Skip to content

[SourceKit] Compute code completion type relation when wrapping a ContextFreeCodeCompletionResult in a CodeCompletionResult #40999

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

Merged
Merged
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
97 changes: 54 additions & 43 deletions include/swift/IDE/CodeCompletion.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#ifndef SWIFT_IDE_CODECOMPLETION_H
#define SWIFT_IDE_CODECOMPLETION_H

#include "CodeCompletionResultType.h"
#include "swift/AST/Identifier.h"
#include "swift/Basic/Debug.h"
#include "swift/Basic/LLVM.h"
Expand Down Expand Up @@ -724,6 +725,7 @@ class ContextFreeCodeCompletionResult {
StringRef ModuleName;
StringRef BriefDocComment;
ArrayRef<StringRef> AssociatedUSRs;
CodeCompletionResultType ResultType;

ContextFreeNotRecommendedReason NotRecommended : 3;
static_assert(int(ContextFreeNotRecommendedReason::MAX_VALUE) < 1 << 3, "");
Expand All @@ -747,13 +749,15 @@ class ContextFreeCodeCompletionResult {
CodeCompletionOperatorKind KnownOperatorKind, bool IsSystem,
CodeCompletionString *CompletionString, StringRef ModuleName,
StringRef BriefDocComment, ArrayRef<StringRef> AssociatedUSRs,
CodeCompletionResultType ResultType,
ContextFreeNotRecommendedReason NotRecommended,
CodeCompletionDiagnosticSeverity DiagnosticSeverity,
StringRef DiagnosticMessage)
: Kind(Kind), KnownOperatorKind(KnownOperatorKind), IsSystem(IsSystem),
CompletionString(CompletionString), ModuleName(ModuleName),
BriefDocComment(BriefDocComment), AssociatedUSRs(AssociatedUSRs),
NotRecommended(NotRecommended), DiagnosticSeverity(DiagnosticSeverity),
ResultType(ResultType), NotRecommended(NotRecommended),
DiagnosticSeverity(DiagnosticSeverity),
DiagnosticMessage(DiagnosticMessage) {
this->AssociatedKind.Opaque = AssociatedKind;
assert((NotRecommended == ContextFreeNotRecommendedReason::None) ==
Expand All @@ -780,13 +784,14 @@ class ContextFreeCodeCompletionResult {
ContextFreeCodeCompletionResult(
CodeCompletionResultKind Kind, CodeCompletionString *CompletionString,
CodeCompletionOperatorKind KnownOperatorKind, StringRef BriefDocComment,
CodeCompletionResultType ResultType,
ContextFreeNotRecommendedReason NotRecommended,
CodeCompletionDiagnosticSeverity DiagnosticSeverity,
StringRef DiagnosticMessage)
: ContextFreeCodeCompletionResult(
Kind, /*AssociatedKind=*/0, KnownOperatorKind,
/*IsSystem=*/false, CompletionString, /*ModuleName=*/"",
BriefDocComment, /*AssociatedUSRs=*/{}, NotRecommended,
BriefDocComment, /*AssociatedUSRs=*/{}, ResultType, NotRecommended,
DiagnosticSeverity, DiagnosticMessage) {}

/// Constructs a \c Keyword result.
Expand All @@ -796,12 +801,14 @@ class ContextFreeCodeCompletionResult {
/// same \c CodeCompletionResultSink as the result itself.
ContextFreeCodeCompletionResult(CodeCompletionKeywordKind Kind,
CodeCompletionString *CompletionString,
StringRef BriefDocComment)
StringRef BriefDocComment,
CodeCompletionResultType ResultType)
: ContextFreeCodeCompletionResult(
CodeCompletionResultKind::Keyword, static_cast<uint8_t>(Kind),
CodeCompletionOperatorKind::None, /*IsSystem=*/false,
CompletionString, /*ModuleName=*/"", BriefDocComment,
/*AssociatedUSRs=*/{}, ContextFreeNotRecommendedReason::None,
/*AssociatedUSRs=*/{}, ResultType,
ContextFreeNotRecommendedReason::None,
CodeCompletionDiagnosticSeverity::None, /*DiagnosticMessage=*/"") {}

/// Constructs a \c Literal result.
Expand All @@ -810,13 +817,15 @@ class ContextFreeCodeCompletionResult {
/// result, typically by storing them in the same \c CodeCompletionResultSink
/// as the result itself.
ContextFreeCodeCompletionResult(CodeCompletionLiteralKind LiteralKind,
CodeCompletionString *CompletionString)
CodeCompletionString *CompletionString,
CodeCompletionResultType ResultType)
: ContextFreeCodeCompletionResult(
CodeCompletionResultKind::Literal,
static_cast<uint8_t>(LiteralKind), CodeCompletionOperatorKind::None,
/*IsSystem=*/false, CompletionString, /*ModuleName=*/"",
/*BriefDocComment=*/"",
/*AssociatedUSRs=*/{}, ContextFreeNotRecommendedReason::None,
/*AssociatedUSRs=*/{}, ResultType,
ContextFreeNotRecommendedReason::None,
CodeCompletionDiagnosticSeverity::None, /*DiagnosticMessage=*/"") {}

/// Constructs a \c Declaration result.
Expand All @@ -827,7 +836,7 @@ class ContextFreeCodeCompletionResult {
ContextFreeCodeCompletionResult(
CodeCompletionString *CompletionString, const Decl *AssociatedDecl,
StringRef ModuleName, StringRef BriefDocComment,
ArrayRef<StringRef> AssociatedUSRs,
ArrayRef<StringRef> AssociatedUSRs, CodeCompletionResultType ResultType,
ContextFreeNotRecommendedReason NotRecommended,
CodeCompletionDiagnosticSeverity DiagnosticSeverity,
StringRef DiagnosticMessage)
Expand All @@ -836,7 +845,7 @@ class ContextFreeCodeCompletionResult {
static_cast<uint8_t>(getCodeCompletionDeclKind(AssociatedDecl)),
CodeCompletionOperatorKind::None, getDeclIsSystem(AssociatedDecl),
CompletionString, ModuleName, BriefDocComment, AssociatedUSRs,
NotRecommended, DiagnosticSeverity, DiagnosticMessage) {
ResultType, NotRecommended, DiagnosticSeverity, DiagnosticMessage) {
assert(AssociatedDecl && "should have a decl");
}

Expand Down Expand Up @@ -874,6 +883,8 @@ class ContextFreeCodeCompletionResult {

ArrayRef<StringRef> getAssociatedUSRs() const { return AssociatedUSRs; }

const CodeCompletionResultType &getResultType() const { return ResultType; }

ContextFreeNotRecommendedReason getNotRecommendedReason() const {
return NotRecommended;
}
Expand Down Expand Up @@ -909,34 +920,6 @@ class ContextFreeCodeCompletionResult {
/// A single code completion result enriched with information that depend on
/// the completion's usage context.
class CodeCompletionResult {
public:
/// Describes the relationship between the type of the completion results and
/// the expected type at the code completion position.
enum class ExpectedTypeRelation : uint8_t {
/// The result does not have a type (e.g. keyword).
NotApplicable,

/// The type relation have not been calculated.
Unknown,

/// The relationship of the result's type to the expected type is not
/// invalid, not convertible, and not identical.
Unrelated,

/// The result's type is invalid at the expected position.
Invalid,

/// The result's type is convertible to the type of the expected.
Convertible,

/// The result's type is identical to the type of the expected.
Identical,

MAX_VALUE = Identical
};


private:
const ContextFreeCodeCompletionResult &ContextFree;
SemanticContextKind SemanticContext : 3;
static_assert(int(SemanticContextKind::MAX_VALUE) < 1 << 3, "");
Expand All @@ -962,17 +945,17 @@ class CodeCompletionResult {
static const unsigned MaxNumBytesToErase = 127;

private:
ExpectedTypeRelation TypeDistance : 3;
public:
/// Enrich a \c ContextFreeCodeCompletionResult with the following contextual
/// information.
CodeCompletionResultTypeRelation TypeDistance : 3;
static_assert(int(CodeCompletionResultTypeRelation::MAX_VALUE) < 1 << 3, "");

/// Memberwise initializer
/// The \c ContextFree result must outlive this result. Typically, this is
/// done by allocating the two in the same sink or adopting the context free
/// sink in the sink that allocates this result.
CodeCompletionResult(const ContextFreeCodeCompletionResult &ContextFree,
SemanticContextKind SemanticContext,
CodeCompletionFlair Flair, uint8_t NumBytesToErase,
ExpectedTypeRelation TypeDistance,
CodeCompletionResultTypeRelation TypeDistance,
ContextualNotRecommendedReason NotRecommended,
CodeCompletionDiagnosticSeverity DiagnosticSeverity,
StringRef DiagnosticMessage)
Expand All @@ -982,6 +965,23 @@ class CodeCompletionResult {
DiagnosticMessage(DiagnosticMessage), NumBytesToErase(NumBytesToErase),
TypeDistance(TypeDistance) {}

public:
/// Enrich a \c ContextFreeCodeCompletionResult with the following contextual
/// information.
/// This computes the type relation between the completion item and its
/// expected type context.
/// The \c ContextFree result must outlive this result. Typically, this is
/// done by allocating the two in the same sink or adopting the context free
/// sink in the sink that allocates this result.
CodeCompletionResult(const ContextFreeCodeCompletionResult &ContextFree,
SemanticContextKind SemanticContext,
CodeCompletionFlair Flair, uint8_t NumBytesToErase,
const ExpectedTypeContext *TypeContext,
const DeclContext *DC,
ContextualNotRecommendedReason NotRecommended,
CodeCompletionDiagnosticSeverity DiagnosticSeverity,
StringRef DiagnosticMessage);

const ContextFreeCodeCompletionResult &getContextFreeResult() const {
return ContextFree;
}
Expand All @@ -998,7 +998,16 @@ class CodeCompletionResult {
/// context free result outlives the result the result returned by this
/// method.
CodeCompletionResult *withFlair(CodeCompletionFlair newFlair,
CodeCompletionResultSink &Sink);
CodeCompletionResultSink &Sink) const;

/// Copy this result to \p Sink with \p newFlair . Note that this does NOT
/// copy the context free result. Thus the caller needs to ensure that the
/// context free result outlives the result the result returned by this
/// method.
CodeCompletionResult *withContextFreeResultSemanticContextAndFlair(
const ContextFreeCodeCompletionResult &NewContextFree,
SemanticContextKind NewSemanticContext, CodeCompletionFlair NewFlair,
CodeCompletionResultSink &Sink) const;

CodeCompletionResultKind getKind() const {
return getContextFreeResult().getKind();
Expand All @@ -1024,7 +1033,9 @@ class CodeCompletionResult {

bool isSystem() const { return getContextFreeResult().isSystem(); }

ExpectedTypeRelation getExpectedTypeRelation() const { return TypeDistance; }
CodeCompletionResultTypeRelation getExpectedTypeRelation() const {
return TypeDistance;
}

/// Get the contextual not-recommended reason. This disregards context-free
/// not recommended reasons.
Expand Down
162 changes: 162 additions & 0 deletions include/swift/IDE/CodeCompletionResultType.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
//===--- CodeCompletionResultType.h -----------------------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2022 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_CODECOMPLETIONRESULTTYPE_H
#define SWIFT_IDE_CODECOMPLETIONRESULTTYPE_H

#include "swift/AST/Types.h"
#include "swift/Basic/LLVM.h"

namespace swift {
namespace ide {

/// The expected contextual type(s) for code-completion.
class ExpectedTypeContext {
/// Possible types of the code completion expression.
llvm::SmallVector<Type, 4> PossibleTypes;

/// Pre typechecked type of the expression at the completion position.
Type IdealType;

/// Whether the `ExpectedTypes` comes from a single-expression body, e.g.
/// `foo({ here })`.
///
/// Since the input may be incomplete, we take into account that the types are
/// only a hint.
bool IsImplicitSingleExpressionReturn = false;
bool PreferNonVoid = false;

public:
ExpectedTypeContext() = default;

bool empty() const { return PossibleTypes.empty(); }

ArrayRef<Type> getPossibleTypes() const { return PossibleTypes; }

void setPossibleTypes(ArrayRef<Type> Types) {
PossibleTypes.clear();
PossibleTypes.reserve(Types.size());
for (auto T : Types) {
if (T) {
PossibleTypes.push_back(T);
}
}
}

Type getIdealType() const { return IdealType; }

void setIdealType(Type IdealType) { this->IdealType = IdealType; }

bool requiresNonVoid() const {
if (IsImplicitSingleExpressionReturn)
return false;
if (PreferNonVoid)
return true;
if (PossibleTypes.empty())
return false;
return llvm::all_of(PossibleTypes, [](Type Ty) { return !Ty->isVoid(); });
}

bool isImplicitSingleExpressionReturn() const {
return IsImplicitSingleExpressionReturn;
}

void
setIsImplicitSingleExpressionReturn(bool IsImplicitSingleExpressionReturn) {
this->IsImplicitSingleExpressionReturn = IsImplicitSingleExpressionReturn;
}

bool getPreferNonVoid() const { return PreferNonVoid; }

void setPreferNonVoid(bool PreferNonVoid) {
this->PreferNonVoid = PreferNonVoid;
}
};

/// Describes the relationship between the type of the completion results and
/// the expected type at the code completion position.
enum class CodeCompletionResultTypeRelation : uint8_t {
/// The result does not have a type (e.g. keyword).
NotApplicable,

/// The type relation have not been calculated.
Unknown,

/// The relationship of the result's type to the expected type is not
/// invalid, not convertible, and not identical.
Unrelated,

/// The result's type is invalid at the expected position.
Invalid,

/// The result's type is convertible to the type of the expected.
Convertible,

/// The result's type is identical to the type of the expected.
Identical,

MAX_VALUE = Identical
};

/// The type returned by a \c ContextFreeCodeCompletionResult. Can be either of
/// the following:
/// - Have the NotApplicable flag set: The completion result doesn't produce
/// something that's valid inside an expression like a keyword
/// - An null type if the completion result produces something that's
/// valid inside an expression but the result type isn't known
/// - A proper type if the type produced by this completion result is known
class CodeCompletionResultType {
public:
enum class Flags : unsigned {
IsNotApplicable = 1 << 0,
/// If \p AlsoConsiderMetatype is set the code completion item will be
/// considered as producing the declared interface type (which is passed as
/// \p ResultTypes ) as well as the corresponding metatype.
/// This allows us to suggest 'Int' as 'Identical' for both of the following
/// functions
///
/// func receiveInstance(_: Int) {}
/// func receiveMetatype(_: Int.Type) {}
AlsoConsiderMetatype = 1 << 1
};

private:
llvm::PointerIntPair<Type, 2, OptionSet<Flags>> TypeAndFlags;

CodeCompletionResultType(Type Ty, OptionSet<Flags> Flag)
: TypeAndFlags(Ty, Flag) {}

public:
static CodeCompletionResultType notApplicable() {
return CodeCompletionResultType(Type(), Flags::IsNotApplicable);
}
static CodeCompletionResultType unknown() {
return CodeCompletionResultType(Type(), /*Flags=*/0);
}
CodeCompletionResultType(Type Ty, bool AlsoConsiderMetatype = false)
: CodeCompletionResultType(Ty, AlsoConsiderMetatype
? Flags::AlsoConsiderMetatype
: OptionSet<Flags>()) {}

bool isNotApplicable() const {
return TypeAndFlags.getInt().contains(Flags::IsNotApplicable);
}

/// Calculates the type realtion of this type to the given
CodeCompletionResultTypeRelation
calculateTypeRelation(const ExpectedTypeContext *TypeContext,
const DeclContext *DC) const;
};
} // namespace ide
} // namespace swift

#endif // SWIFT_IDE_CODECOMPLETIONRESULTTYPE_H
1 change: 1 addition & 0 deletions lib/IDE/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ add_swift_host_library(swiftIDE STATIC
CodeCompletionCache.cpp
CodeCompletionDiagnostics.cpp
CodeCompletionResultPrinter.cpp
CodeCompletionResultType.cpp
CommentConversion.cpp
CompileInstance.cpp
CompletionInstance.cpp
Expand Down
Loading