Skip to content

Commit 86a1bfd

Browse files
authored
Merge pull request swiftlang#39631 from ahoppen/pr/cancel-completion-infrastructure
[CodeCompletion] Refactor how code completion results are returned to support cancellation
2 parents 858ed27 + c9f5331 commit 86a1bfd

19 files changed

+1222
-708
lines changed

include/swift/IDE/CancellableResult.h

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
//===--- CancellableResult.h ------------------------------------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2021 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_CANCELLABLE_RESULT_H
14+
#define SWIFT_IDE_CANCELLABLE_RESULT_H
15+
16+
#include <string>
17+
18+
namespace swift {
19+
20+
namespace ide {
21+
22+
enum class CancellableResultKind { Success, Failure, Cancelled };
23+
24+
/// A result type that can carry one be in one of the following states:
25+
/// - Success and carry a value of \c ResultType
26+
/// - Failure and carry an error description
27+
/// - Cancelled in case the operation that produced the result was cancelled
28+
///
29+
/// Essentially this emulates an enum with associated values as follows
30+
/// \code
31+
/// enum CancellableResult<ResultType> {
32+
/// case success(ResultType)
33+
/// case failure(String)
34+
/// case cancelled
35+
/// }
36+
/// \endcode
37+
///
38+
/// The implementation is inspired by llvm::optional_detail::OptionalStorage
39+
template <typename ResultType>
40+
class CancellableResult {
41+
CancellableResultKind Kind;
42+
union {
43+
/// If \c Kind == Success, carries the result.
44+
ResultType Result;
45+
/// If \c Kind == Error, carries the error description.
46+
std::string Error;
47+
/// If \c Kind == Cancelled, this union is not initialized.
48+
char Empty;
49+
};
50+
51+
CancellableResult(ResultType Result)
52+
: Kind(CancellableResultKind::Success), Result(Result) {}
53+
54+
CancellableResult(std::string Error)
55+
: Kind(CancellableResultKind::Failure), Error(Error) {}
56+
57+
explicit CancellableResult()
58+
: Kind(CancellableResultKind::Cancelled), Empty() {}
59+
60+
public:
61+
CancellableResult(const CancellableResult &Other) : Kind(Other.Kind), Empty() {
62+
switch (Kind) {
63+
case CancellableResultKind::Success:
64+
::new ((void *)std::addressof(Result)) ResultType(Other.Result);
65+
break;
66+
case CancellableResultKind::Failure:
67+
::new ((void *)std::addressof(Error)) std::string(Other.Error);
68+
break;
69+
case CancellableResultKind::Cancelled:
70+
break;
71+
}
72+
}
73+
74+
CancellableResult(CancellableResult &&Other) : Kind(Other.Kind), Empty() {
75+
switch (Kind) {
76+
case CancellableResultKind::Success:
77+
::new ((void *)std::addressof(Result))
78+
ResultType(std::move(Other.Result));
79+
break;
80+
case CancellableResultKind::Failure:
81+
::new ((void *)std::addressof(Error)) std::string(std::move(Other.Error));
82+
break;
83+
case CancellableResultKind::Cancelled:
84+
break;
85+
}
86+
}
87+
88+
~CancellableResult() {
89+
using std::string;
90+
switch (Kind) {
91+
case CancellableResultKind::Success:
92+
Result.~ResultType();
93+
break;
94+
case CancellableResultKind::Failure:
95+
Error.~string();
96+
break;
97+
case CancellableResultKind::Cancelled:
98+
break;
99+
}
100+
}
101+
102+
/// Construct a \c CancellableResult that carries a successful result.
103+
static CancellableResult success(ResultType Result) {
104+
return std::move(CancellableResult(ResultType(Result)));
105+
}
106+
107+
/// Construct a \c CancellableResult that carries the error message of a
108+
/// failure.
109+
static CancellableResult failure(std::string Error) {
110+
return std::move(CancellableResult(Error));
111+
}
112+
113+
/// Construct a \c CancellableResult representing that the producing operation
114+
/// was cancelled.
115+
static CancellableResult cancelled() {
116+
return std::move(CancellableResult());
117+
}
118+
119+
/// Return the result kind this \c CancellableResult represents: success,
120+
/// failure or cancelled.
121+
CancellableResultKind getKind() { return Kind; }
122+
123+
/// Assuming that the result represents success, return the underlying result
124+
/// value.
125+
ResultType &getResult() {
126+
assert(getKind() == CancellableResultKind::Success);
127+
return Result;
128+
}
129+
130+
/// Assuming that the result represents success, retrieve members of the
131+
/// underlying result value.
132+
ResultType *operator->() { return &getResult(); }
133+
134+
/// Assuming that the result represents success, return the underlying result
135+
/// value.
136+
ResultType &operator*() { return getResult(); }
137+
138+
/// Assuming that the result represents a failure, return the error message.
139+
std::string getError() {
140+
assert(getKind() == CancellableResultKind::Failure);
141+
return Error;
142+
}
143+
144+
/// If the result represents success, invoke \p Transform to asynchronously
145+
/// transform the wrapped result type and produce a new result type that is
146+
/// provided by the callback function passed to \p Transform. Afterwards call
147+
/// \p Handle with either the transformed value or the failure or cancelled
148+
/// result.
149+
/// The \c async part of the map means that the transform might happen
150+
/// asyncronously. This function does not introduce asynchronicity by itself.
151+
/// \p Transform might also invoke the callback synchronously.
152+
template <typename NewResultType>
153+
void
154+
mapAsync(llvm::function_ref<
155+
void(ResultType &,
156+
llvm::function_ref<void(CancellableResult<NewResultType>)>)>
157+
Transform,
158+
llvm::function_ref<void(CancellableResult<NewResultType>)> Handle) {
159+
switch (getKind()) {
160+
case CancellableResultKind::Success:
161+
Transform(getResult(), [&](CancellableResult<NewResultType> NewResult) {
162+
Handle(NewResult);
163+
});
164+
break;
165+
case CancellableResultKind::Failure:
166+
Handle(CancellableResult<NewResultType>::failure(getError()));
167+
break;
168+
case CancellableResultKind::Cancelled:
169+
Handle(CancellableResult<NewResultType>::cancelled());
170+
break;
171+
}
172+
}
173+
};
174+
175+
} // namespace ide
176+
} // namespace swift
177+
178+
#endif // SWIFT_IDE_CANCELLABLE_RESULT_H

include/swift/IDE/CodeCompletion.h

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "swift/Basic/Debug.h"
1818
#include "swift/Basic/LLVM.h"
1919
#include "swift/Basic/OptionSet.h"
20+
#include "swift/Frontend/Frontend.h"
2021
#include "llvm/ADT/ArrayRef.h"
2122
#include "llvm/ADT/StringMap.h"
2223
#include "llvm/ADT/StringRef.h"
@@ -1062,6 +1063,12 @@ class CodeCompletionContext {
10621063
}
10631064
};
10641065

1066+
struct SwiftCompletionInfo {
1067+
swift::ASTContext *swiftASTContext = nullptr;
1068+
const swift::CompilerInvocation *invocation = nullptr;
1069+
CodeCompletionContext *completionContext = nullptr;
1070+
};
1071+
10651072
/// An abstract base class for consumers of code completion results.
10661073
/// \see \c SimpleCachingCodeCompletionConsumer.
10671074
class CodeCompletionConsumer {
@@ -1087,32 +1094,6 @@ struct SimpleCachingCodeCompletionConsumer : public CodeCompletionConsumer {
10871094
virtual void handleResults(CodeCompletionContext &context) = 0;
10881095
};
10891096

1090-
/// A code completion result consumer that prints the results to a
1091-
/// \c raw_ostream.
1092-
class PrintingCodeCompletionConsumer
1093-
: public SimpleCachingCodeCompletionConsumer {
1094-
llvm::raw_ostream &OS;
1095-
bool IncludeKeywords;
1096-
bool IncludeComments;
1097-
bool IncludeSourceText;
1098-
bool PrintAnnotatedDescription;
1099-
1100-
public:
1101-
PrintingCodeCompletionConsumer(llvm::raw_ostream &OS,
1102-
bool IncludeKeywords = true,
1103-
bool IncludeComments = true,
1104-
bool IncludeSourceText = false,
1105-
bool PrintAnnotatedDescription = false)
1106-
: OS(OS),
1107-
IncludeKeywords(IncludeKeywords),
1108-
IncludeComments(IncludeComments),
1109-
IncludeSourceText(IncludeSourceText),
1110-
PrintAnnotatedDescription(PrintAnnotatedDescription) {}
1111-
1112-
void handleResults(CodeCompletionContext &context) override;
1113-
void handleResults(MutableArrayRef<CodeCompletionResult *> Results);
1114-
};
1115-
11161097
/// Create a factory for code completion callbacks.
11171098
CodeCompletionCallbacksFactory *
11181099
makeCodeCompletionCallbacksFactory(CodeCompletionContext &CompletionContext,

include/swift/IDE/CompletionInstance.h

Lines changed: 86 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@
1414
#define SWIFT_IDE_COMPLETIONINSTANCE_H
1515

1616
#include "swift/Frontend/Frontend.h"
17+
#include "swift/IDE/CancellableResult.h"
18+
#include "swift/IDE/CodeCompletion.h"
19+
#include "swift/IDE/ConformingMethodList.h"
20+
#include "swift/IDE/TypeContextInfo.h"
1721
#include "llvm/ADT/Hashing.h"
1822
#include "llvm/ADT/IntrusiveRefCntPtr.h"
1923
#include "llvm/ADT/StringRef.h"
@@ -35,6 +39,40 @@ makeCodeCompletionMemoryBuffer(const llvm::MemoryBuffer *origBuf,
3539
unsigned &Offset,
3640
llvm::StringRef bufferIdentifier);
3741

42+
/// The result returned via the callback from the perform*Operation methods.
43+
struct CompletionInstanceResult {
44+
/// The compiler instance that is prepared for the second pass.
45+
CompilerInstance &CI;
46+
/// Whether an AST was reused.
47+
bool DidReuseAST;
48+
/// Whether the CompletionInstance found a code completion token in the source
49+
/// file. If this is \c false, the user will most likely want to return empty
50+
/// results.
51+
bool DidFindCodeCompletionToken;
52+
};
53+
54+
/// The results returned from \c CompletionInstance::codeComplete.
55+
struct CodeCompleteResult {
56+
MutableArrayRef<CodeCompletionResult *> Results;
57+
SwiftCompletionInfo &Info;
58+
};
59+
60+
/// The results returned from \c CompletionInstance::typeContextInfo.
61+
struct TypeContextInfoResult {
62+
/// The actual results. If empty, no results were found.
63+
ArrayRef<TypeContextInfoItem> Results;
64+
/// Whether an AST was reused to produce the results.
65+
bool DidReuseAST;
66+
};
67+
68+
/// The results returned from \c CompletionInstance::conformingMethodList.
69+
struct ConformingMethodListResults {
70+
/// The actual results. If \c nullptr, no results were found.
71+
const ConformingMethodListResult *Result;
72+
/// Whether an AST was reused for the completion.
73+
bool DidReuseAST;
74+
};
75+
3876
/// Manages \c CompilerInstance for completion like operations.
3977
class CompletionInstance {
4078
struct Options {
@@ -58,27 +96,49 @@ class CompletionInstance {
5896

5997
/// Calls \p Callback with cached \c CompilerInstance if it's usable for the
6098
/// specified completion request.
61-
/// Returns \c if the callback was called. Returns \c false if the compiler
62-
/// argument has changed, primary file is not the same, the \c Offset is not
63-
/// in function bodies, or the interface hash of the file has changed.
99+
/// Returns \c true if performing the cached operation was possible. Returns
100+
/// \c false if the compiler argument has changed, primary file is not the
101+
/// same, the \c Offset is not in function bodies, or the interface hash of
102+
/// the file has changed.
103+
/// \p Callback will be called if and only if this function returns \c true.
64104
bool performCachedOperationIfPossible(
65105
llvm::hash_code ArgsHash,
66106
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
67107
llvm::MemoryBuffer *completionBuffer, unsigned int Offset,
68108
DiagnosticConsumer *DiagC,
69-
llvm::function_ref<void(CompilerInstance &, bool)> Callback);
109+
llvm::function_ref<void(CancellableResult<CompletionInstanceResult>)>
110+
Callback);
70111

71112
/// Calls \p Callback with new \c CompilerInstance for the completion
72113
/// request. The \c CompilerInstace passed to the callback already performed
73114
/// the first pass.
74115
/// Returns \c false if it fails to setup the \c CompilerInstance.
75-
bool performNewOperation(
116+
void performNewOperation(
76117
llvm::Optional<llvm::hash_code> ArgsHash,
77118
swift::CompilerInvocation &Invocation,
78119
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
79120
llvm::MemoryBuffer *completionBuffer, unsigned int Offset,
80-
std::string &Error, DiagnosticConsumer *DiagC,
81-
llvm::function_ref<void(CompilerInstance &, bool)> Callback);
121+
DiagnosticConsumer *DiagC,
122+
llvm::function_ref<void(CancellableResult<CompletionInstanceResult>)>
123+
Callback);
124+
125+
/// Calls \p Callback with a \c CompilerInstance which is prepared for the
126+
/// second pass. \p Callback is resposible to perform the second pass on it.
127+
/// The \c CompilerInstance may be reused from the previous completions,
128+
/// and may be cached for the next completion.
129+
/// In case of failure or cancellation, the callback receives the
130+
/// corresponding failed or cancelled result.
131+
///
132+
/// NOTE: \p Args is only used for checking the equaity of the invocation.
133+
/// Since this function assumes that it is already normalized, exact the same
134+
/// arguments including their order is considered as the same invocation.
135+
void performOperation(
136+
swift::CompilerInvocation &Invocation, llvm::ArrayRef<const char *> Args,
137+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
138+
llvm::MemoryBuffer *completionBuffer, unsigned int Offset,
139+
DiagnosticConsumer *DiagC,
140+
llvm::function_ref<void(CancellableResult<CompletionInstanceResult>)>
141+
Callback);
82142

83143
public:
84144
CompletionInstance() : CachedCIShouldBeInvalidated(false) {}
@@ -90,22 +150,28 @@ class CompletionInstance {
90150
// Update options with \c NewOpts. (Thread safe.)
91151
void setOptions(Options NewOpts);
92152

93-
/// Calls \p Callback with a \c CompilerInstance which is prepared for the
94-
/// second pass. \p Callback is resposible to perform the second pass on it.
95-
/// The \c CompilerInstance may be reused from the previous completions,
96-
/// and may be cached for the next completion.
97-
/// Return \c true if \p is successfully called, \c it fails. In failure
98-
/// cases \p Error is populated with an error message.
99-
///
100-
/// NOTE: \p Args is only used for checking the equaity of the invocation.
101-
/// Since this function assumes that it is already normalized, exact the same
102-
/// arguments including their order is considered as the same invocation.
103-
bool performOperation(
153+
void codeComplete(
154+
swift::CompilerInvocation &Invocation, llvm::ArrayRef<const char *> Args,
155+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
156+
llvm::MemoryBuffer *completionBuffer, unsigned int Offset,
157+
DiagnosticConsumer *DiagC, ide::CodeCompletionContext &CompletionContext,
158+
llvm::function_ref<void(CancellableResult<CodeCompleteResult>)> Callback);
159+
160+
void typeContextInfo(
161+
swift::CompilerInvocation &Invocation, llvm::ArrayRef<const char *> Args,
162+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
163+
llvm::MemoryBuffer *completionBuffer, unsigned int Offset,
164+
DiagnosticConsumer *DiagC,
165+
llvm::function_ref<void(CancellableResult<TypeContextInfoResult>)>
166+
Callback);
167+
168+
void conformingMethodList(
104169
swift::CompilerInvocation &Invocation, llvm::ArrayRef<const char *> Args,
105170
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
106171
llvm::MemoryBuffer *completionBuffer, unsigned int Offset,
107-
std::string &Error, DiagnosticConsumer *DiagC,
108-
llvm::function_ref<void(CompilerInstance &, bool)> Callback);
172+
DiagnosticConsumer *DiagC, ArrayRef<const char *> ExpectedTypeNames,
173+
llvm::function_ref<void(CancellableResult<ConformingMethodListResults>)>
174+
Callback);
109175
};
110176

111177
} // namespace ide

0 commit comments

Comments
 (0)