Skip to content

Commit 6bc0de9

Browse files
committed
[SourceKit] Compute code completion type relation when wrapping a ContextFreeCodeCompletionResult in a CodeCompletionResult
[CodeCompletion] Make ExpectedTypeContext a class with explicit getters/setters This simplifies debugging because you can break when the possible types are set and you can also search for references to `setPossibleType` to figure out where the expected types are being set.
1 parent 50eafca commit 6bc0de9

13 files changed

+460
-280
lines changed

Diff for: include/swift/IDE/CodeCompletion.h

+47-37
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#ifndef SWIFT_IDE_CODECOMPLETION_H
1414
#define SWIFT_IDE_CODECOMPLETION_H
1515

16+
#include "CodeCompletionResultType.h"
1617
#include "swift/AST/Identifier.h"
1718
#include "swift/Basic/Debug.h"
1819
#include "swift/Basic/LLVM.h"
@@ -701,31 +702,6 @@ enum class CodeCompletionResultKind : uint8_t {
701702
MAX_VALUE = BuiltinOperator
702703
};
703704

704-
/// Describes the relationship between the type of the completion results and
705-
/// the expected type at the code completion position.
706-
enum class CodeCompletionResultTypeRelation : uint8_t {
707-
/// The result does not have a type (e.g. keyword).
708-
NotApplicable,
709-
710-
/// The type relation have not been calculated.
711-
Unknown,
712-
713-
/// The relationship of the result's type to the expected type is not
714-
/// invalid, not convertible, and not identical.
715-
Unrelated,
716-
717-
/// The result's type is invalid at the expected position.
718-
Invalid,
719-
720-
/// The result's type is convertible to the type of the expected.
721-
Convertible,
722-
723-
/// The result's type is identical to the type of the expected.
724-
Identical,
725-
726-
MAX_VALUE = Identical
727-
};
728-
729705
/// The parts of a \c CodeCompletionResult that are not dependent on the context
730706
/// it appears in and can thus be cached.
731707
class ContextFreeCodeCompletionResult {
@@ -749,6 +725,7 @@ class ContextFreeCodeCompletionResult {
749725
StringRef ModuleName;
750726
StringRef BriefDocComment;
751727
ArrayRef<StringRef> AssociatedUSRs;
728+
CodeCompletionResultType ResultType;
752729

753730
ContextFreeNotRecommendedReason NotRecommended : 3;
754731
static_assert(int(ContextFreeNotRecommendedReason::MAX_VALUE) < 1 << 3, "");
@@ -772,13 +749,15 @@ class ContextFreeCodeCompletionResult {
772749
CodeCompletionOperatorKind KnownOperatorKind, bool IsSystem,
773750
CodeCompletionString *CompletionString, StringRef ModuleName,
774751
StringRef BriefDocComment, ArrayRef<StringRef> AssociatedUSRs,
752+
CodeCompletionResultType ResultType,
775753
ContextFreeNotRecommendedReason NotRecommended,
776754
CodeCompletionDiagnosticSeverity DiagnosticSeverity,
777755
StringRef DiagnosticMessage)
778756
: Kind(Kind), KnownOperatorKind(KnownOperatorKind), IsSystem(IsSystem),
779757
CompletionString(CompletionString), ModuleName(ModuleName),
780758
BriefDocComment(BriefDocComment), AssociatedUSRs(AssociatedUSRs),
781-
NotRecommended(NotRecommended), DiagnosticSeverity(DiagnosticSeverity),
759+
ResultType(ResultType), NotRecommended(NotRecommended),
760+
DiagnosticSeverity(DiagnosticSeverity),
782761
DiagnosticMessage(DiagnosticMessage) {
783762
this->AssociatedKind.Opaque = AssociatedKind;
784763
assert((NotRecommended == ContextFreeNotRecommendedReason::None) ==
@@ -805,13 +784,14 @@ class ContextFreeCodeCompletionResult {
805784
ContextFreeCodeCompletionResult(
806785
CodeCompletionResultKind Kind, CodeCompletionString *CompletionString,
807786
CodeCompletionOperatorKind KnownOperatorKind, StringRef BriefDocComment,
787+
CodeCompletionResultType ResultType,
808788
ContextFreeNotRecommendedReason NotRecommended,
809789
CodeCompletionDiagnosticSeverity DiagnosticSeverity,
810790
StringRef DiagnosticMessage)
811791
: ContextFreeCodeCompletionResult(
812792
Kind, /*AssociatedKind=*/0, KnownOperatorKind,
813793
/*IsSystem=*/false, CompletionString, /*ModuleName=*/"",
814-
BriefDocComment, /*AssociatedUSRs=*/{}, NotRecommended,
794+
BriefDocComment, /*AssociatedUSRs=*/{}, ResultType, NotRecommended,
815795
DiagnosticSeverity, DiagnosticMessage) {}
816796

817797
/// Constructs a \c Keyword result.
@@ -821,12 +801,14 @@ class ContextFreeCodeCompletionResult {
821801
/// same \c CodeCompletionResultSink as the result itself.
822802
ContextFreeCodeCompletionResult(CodeCompletionKeywordKind Kind,
823803
CodeCompletionString *CompletionString,
824-
StringRef BriefDocComment)
804+
StringRef BriefDocComment,
805+
CodeCompletionResultType ResultType)
825806
: ContextFreeCodeCompletionResult(
826807
CodeCompletionResultKind::Keyword, static_cast<uint8_t>(Kind),
827808
CodeCompletionOperatorKind::None, /*IsSystem=*/false,
828809
CompletionString, /*ModuleName=*/"", BriefDocComment,
829-
/*AssociatedUSRs=*/{}, ContextFreeNotRecommendedReason::None,
810+
/*AssociatedUSRs=*/{}, ResultType,
811+
ContextFreeNotRecommendedReason::None,
830812
CodeCompletionDiagnosticSeverity::None, /*DiagnosticMessage=*/"") {}
831813

832814
/// Constructs a \c Literal result.
@@ -835,13 +817,15 @@ class ContextFreeCodeCompletionResult {
835817
/// result, typically by storing them in the same \c CodeCompletionResultSink
836818
/// as the result itself.
837819
ContextFreeCodeCompletionResult(CodeCompletionLiteralKind LiteralKind,
838-
CodeCompletionString *CompletionString)
820+
CodeCompletionString *CompletionString,
821+
CodeCompletionResultType ResultType)
839822
: ContextFreeCodeCompletionResult(
840823
CodeCompletionResultKind::Literal,
841824
static_cast<uint8_t>(LiteralKind), CodeCompletionOperatorKind::None,
842825
/*IsSystem=*/false, CompletionString, /*ModuleName=*/"",
843826
/*BriefDocComment=*/"",
844-
/*AssociatedUSRs=*/{}, ContextFreeNotRecommendedReason::None,
827+
/*AssociatedUSRs=*/{}, ResultType,
828+
ContextFreeNotRecommendedReason::None,
845829
CodeCompletionDiagnosticSeverity::None, /*DiagnosticMessage=*/"") {}
846830

847831
/// Constructs a \c Declaration result.
@@ -852,7 +836,7 @@ class ContextFreeCodeCompletionResult {
852836
ContextFreeCodeCompletionResult(
853837
CodeCompletionString *CompletionString, const Decl *AssociatedDecl,
854838
StringRef ModuleName, StringRef BriefDocComment,
855-
ArrayRef<StringRef> AssociatedUSRs,
839+
ArrayRef<StringRef> AssociatedUSRs, CodeCompletionResultType ResultType,
856840
ContextFreeNotRecommendedReason NotRecommended,
857841
CodeCompletionDiagnosticSeverity DiagnosticSeverity,
858842
StringRef DiagnosticMessage)
@@ -861,7 +845,7 @@ class ContextFreeCodeCompletionResult {
861845
static_cast<uint8_t>(getCodeCompletionDeclKind(AssociatedDecl)),
862846
CodeCompletionOperatorKind::None, getDeclIsSystem(AssociatedDecl),
863847
CompletionString, ModuleName, BriefDocComment, AssociatedUSRs,
864-
NotRecommended, DiagnosticSeverity, DiagnosticMessage) {
848+
ResultType, NotRecommended, DiagnosticSeverity, DiagnosticMessage) {
865849
assert(AssociatedDecl && "should have a decl");
866850
}
867851

@@ -899,6 +883,8 @@ class ContextFreeCodeCompletionResult {
899883

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

886+
const CodeCompletionResultType &getResultType() const { return ResultType; }
887+
902888
ContextFreeNotRecommendedReason getNotRecommendedReason() const {
903889
return NotRecommended;
904890
}
@@ -962,9 +948,7 @@ class CodeCompletionResult {
962948
CodeCompletionResultTypeRelation TypeDistance : 3;
963949
static_assert(int(CodeCompletionResultTypeRelation::MAX_VALUE) < 1 << 3, "");
964950

965-
public:
966-
/// Enrich a \c ContextFreeCodeCompletionResult with the following contextual
967-
/// information.
951+
/// Memberwise initializer
968952
/// The \c ContextFree result must outlive this result. Typically, this is
969953
/// done by allocating the two in the same sink or adopting the context free
970954
/// sink in the sink that allocates this result.
@@ -981,6 +965,23 @@ class CodeCompletionResult {
981965
DiagnosticMessage(DiagnosticMessage), NumBytesToErase(NumBytesToErase),
982966
TypeDistance(TypeDistance) {}
983967

968+
public:
969+
/// Enrich a \c ContextFreeCodeCompletionResult with the following contextual
970+
/// information.
971+
/// This computes the type relation between the completion item and its
972+
/// expected type context.
973+
/// The \c ContextFree result must outlive this result. Typically, this is
974+
/// done by allocating the two in the same sink or adopting the context free
975+
/// sink in the sink that allocates this result.
976+
CodeCompletionResult(const ContextFreeCodeCompletionResult &ContextFree,
977+
SemanticContextKind SemanticContext,
978+
CodeCompletionFlair Flair, uint8_t NumBytesToErase,
979+
const ExpectedTypeContext *TypeContext,
980+
const DeclContext *DC,
981+
ContextualNotRecommendedReason NotRecommended,
982+
CodeCompletionDiagnosticSeverity DiagnosticSeverity,
983+
StringRef DiagnosticMessage);
984+
984985
const ContextFreeCodeCompletionResult &getContextFreeResult() const {
985986
return ContextFree;
986987
}
@@ -997,7 +998,16 @@ class CodeCompletionResult {
997998
/// context free result outlives the result the result returned by this
998999
/// method.
9991000
CodeCompletionResult *withFlair(CodeCompletionFlair newFlair,
1000-
CodeCompletionResultSink &Sink);
1001+
CodeCompletionResultSink &Sink) const;
1002+
1003+
/// Copy this result to \p Sink with \p newFlair . Note that this does NOT
1004+
/// copy the context free result. Thus the caller needs to ensure that the
1005+
/// context free result outlives the result the result returned by this
1006+
/// method.
1007+
CodeCompletionResult *withContextFreeResultSemanticContextAndFlair(
1008+
const ContextFreeCodeCompletionResult &NewContextFree,
1009+
SemanticContextKind NewSemanticContext, CodeCompletionFlair NewFlair,
1010+
CodeCompletionResultSink &Sink) const;
10011011

10021012
CodeCompletionResultKind getKind() const {
10031013
return getContextFreeResult().getKind();

Diff for: include/swift/IDE/CodeCompletionResultType.h

+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
//===--- CodeCompletionResultType.h -----------------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef SWIFT_IDE_CODECOMPLETIONRESULTTYPE_H
14+
#define SWIFT_IDE_CODECOMPLETIONRESULTTYPE_H
15+
16+
#include "swift/AST/Types.h"
17+
#include "swift/Basic/LLVM.h"
18+
19+
namespace swift {
20+
namespace ide {
21+
22+
/// The expected contextual type(s) for code-completion.
23+
class ExpectedTypeContext {
24+
/// Possible types of the code completion expression.
25+
llvm::SmallVector<Type, 4> PossibleTypes;
26+
27+
/// Pre typechecked type of the expression at the completion position.
28+
Type IdealType;
29+
30+
/// Whether the `ExpectedTypes` comes from a single-expression body, e.g.
31+
/// `foo({ here })`.
32+
///
33+
/// Since the input may be incomplete, we take into account that the types are
34+
/// only a hint.
35+
bool IsImplicitSingleExpressionReturn = false;
36+
bool PreferNonVoid = false;
37+
38+
public:
39+
ExpectedTypeContext() = default;
40+
41+
bool empty() const { return PossibleTypes.empty(); }
42+
43+
ArrayRef<Type> getPossibleTypes() const { return PossibleTypes; }
44+
45+
void setPossibleTypes(ArrayRef<Type> Types) {
46+
PossibleTypes.clear();
47+
PossibleTypes.reserve(Types.size());
48+
for (auto T : Types) {
49+
if (T) {
50+
PossibleTypes.push_back(T);
51+
}
52+
}
53+
}
54+
55+
Type getIdealType() const { return IdealType; }
56+
57+
void setIdealType(Type IdealType) { this->IdealType = IdealType; }
58+
59+
bool requiresNonVoid() const {
60+
if (IsImplicitSingleExpressionReturn)
61+
return false;
62+
if (PreferNonVoid)
63+
return true;
64+
if (PossibleTypes.empty())
65+
return false;
66+
return llvm::all_of(PossibleTypes, [](Type Ty) { return !Ty->isVoid(); });
67+
}
68+
69+
bool isImplicitSingleExpressionReturn() const {
70+
return IsImplicitSingleExpressionReturn;
71+
}
72+
73+
void
74+
setIsImplicitSingleExpressionReturn(bool IsImplicitSingleExpressionReturn) {
75+
this->IsImplicitSingleExpressionReturn = IsImplicitSingleExpressionReturn;
76+
}
77+
78+
bool getPreferNonVoid() const { return PreferNonVoid; }
79+
80+
void setPreferNonVoid(bool PreferNonVoid) {
81+
this->PreferNonVoid = PreferNonVoid;
82+
}
83+
};
84+
85+
/// Describes the relationship between the type of the completion results and
86+
/// the expected type at the code completion position.
87+
enum class CodeCompletionResultTypeRelation : uint8_t {
88+
/// The result does not have a type (e.g. keyword).
89+
NotApplicable,
90+
91+
/// The type relation have not been calculated.
92+
Unknown,
93+
94+
/// The relationship of the result's type to the expected type is not
95+
/// invalid, not convertible, and not identical.
96+
Unrelated,
97+
98+
/// The result's type is invalid at the expected position.
99+
Invalid,
100+
101+
/// The result's type is convertible to the type of the expected.
102+
Convertible,
103+
104+
/// The result's type is identical to the type of the expected.
105+
Identical,
106+
107+
MAX_VALUE = Identical
108+
};
109+
110+
/// The type returned by a \c ContextFreeCodeCompletionResult. Can be either of
111+
/// the following:
112+
/// - Have the NotApplicable flag set: The completion result doesn't produce
113+
/// something that's valid inside an expression like a keyword
114+
/// - An null type if the completion result produces something that's
115+
/// valid inside an expression but the result type isn't known
116+
/// - A proper type if the type produced by this completion result is known
117+
class CodeCompletionResultType {
118+
public:
119+
enum class Flags : unsigned {
120+
IsNotApplicable = 1 << 0,
121+
/// If \p AlsoConsiderMetatype is set the code completion item will be
122+
/// considered as producing the declared interface type (which is passed as
123+
/// \p ResultTypes ) as well as the corresponding metatype.
124+
/// This allows us to suggest 'Int' as 'Identical' for both of the following
125+
/// functions
126+
///
127+
/// func receiveInstance(_: Int) {}
128+
/// func receiveMetatype(_: Int.Type) {}
129+
AlsoConsiderMetatype = 1 << 1
130+
};
131+
132+
private:
133+
llvm::PointerIntPair<Type, 2, OptionSet<Flags>> TypeAndFlags;
134+
135+
CodeCompletionResultType(Type Ty, OptionSet<Flags> Flag)
136+
: TypeAndFlags(Ty, Flag) {}
137+
138+
public:
139+
static CodeCompletionResultType notApplicable() {
140+
return CodeCompletionResultType(Type(), Flags::IsNotApplicable);
141+
}
142+
static CodeCompletionResultType unknown() {
143+
return CodeCompletionResultType(Type(), /*Flags=*/0);
144+
}
145+
CodeCompletionResultType(Type Ty, bool AlsoConsiderMetatype = false)
146+
: CodeCompletionResultType(Ty, AlsoConsiderMetatype
147+
? Flags::AlsoConsiderMetatype
148+
: OptionSet<Flags>()) {}
149+
150+
bool isNotApplicable() const {
151+
return TypeAndFlags.getInt().contains(Flags::IsNotApplicable);
152+
}
153+
154+
/// Calculates the type realtion of this type to the given
155+
CodeCompletionResultTypeRelation
156+
calculateTypeRelation(const ExpectedTypeContext *TypeContext,
157+
const DeclContext *DC) const;
158+
};
159+
} // namespace ide
160+
} // namespace swift
161+
162+
#endif // SWIFT_IDE_CODECOMPLETIONRESULTTYPE_H

Diff for: lib/IDE/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ add_swift_host_library(swiftIDE STATIC
44
CodeCompletionCache.cpp
55
CodeCompletionDiagnostics.cpp
66
CodeCompletionResultPrinter.cpp
7+
CodeCompletionResultType.cpp
78
CommentConversion.cpp
89
CompileInstance.cpp
910
CompletionInstance.cpp

0 commit comments

Comments
 (0)