Skip to content

Commit 10936f6

Browse files
authored
Merge pull request swiftlang#28727 from rintaro/sourcekit-completion-reuseinstance
[SourceKit] ⚡️Fast code completion within function bodies
2 parents b03b356 + eebcbf6 commit 10936f6

33 files changed

+1115
-524
lines changed

include/swift/AST/Decl.h

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5841,13 +5841,7 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
58415841
/// \sa hasBody()
58425842
BraceStmt *getBody(bool canSynthesize = true) const;
58435843

5844-
void setBody(BraceStmt *S, BodyKind NewBodyKind = BodyKind::Parsed) {
5845-
assert(getBodyKind() != BodyKind::Skipped &&
5846-
"cannot set a body if it was skipped");
5847-
5848-
Body = S;
5849-
setBodyKind(NewBodyKind);
5850-
}
5844+
void setBody(BraceStmt *S, BodyKind NewBodyKind = BodyKind::Parsed);
58515845

58525846
/// Note that the body was skipped for this function. Function body
58535847
/// cannot be attached after this call.
@@ -5866,7 +5860,8 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
58665860

58675861
/// Note that parsing for the body was delayed.
58685862
void setBodyDelayed(SourceRange bodyRange) {
5869-
assert(getBodyKind() == BodyKind::None);
5863+
assert(getBodyKind() == BodyKind::None ||
5864+
getBodyKind() == BodyKind::Skipped);
58705865
assert(bodyRange.isValid());
58715866
BodyRange = bodyRange;
58725867
setBodyKind(BodyKind::Unparsed);
@@ -6657,6 +6652,9 @@ class ConstructorDecl : public AbstractFunctionDecl {
66576652
/// initializer.
66586653
BodyInitKind getDelegatingOrChainedInitKind(DiagnosticEngine *diags,
66596654
ApplyExpr **init = nullptr) const;
6655+
void clearCachedDelegatingOrChainedInitKind() {
6656+
Bits.ConstructorDecl.ComputedBodyInitKind = 0;
6657+
}
66606658

66616659
/// Whether this constructor is required.
66626660
bool isRequired() const {

include/swift/AST/DiagnosticsFrontend.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,9 @@ ERROR(repl_must_be_initialized,none,
214214
ERROR(error_doing_code_completion,none,
215215
"compiler is in code completion mode (benign diagnostic)", ())
216216

217+
WARNING(completion_reusing_astcontext,none,
218+
"completion reusing previous ASTContext (benign diagnostic)", ())
219+
217220
ERROR(verify_encountered_fatal,none,
218221
"fatal error encountered while in -verify mode", ())
219222

include/swift/AST/SourceFile.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,9 +404,11 @@ class SourceFile final : public FileUnit {
404404
InterfaceHash->update(a);
405405
}
406406

407-
void getInterfaceHash(llvm::SmallString<32> &str) {
407+
void getInterfaceHash(llvm::SmallString<32> &str) const {
408+
// Copy to preserve idempotence.
409+
llvm::MD5 md5 = *InterfaceHash;
408410
llvm::MD5::MD5Result result;
409-
InterfaceHash->final(result);
411+
md5.final(result);
410412
llvm::MD5::stringifyResult(result, str);
411413
}
412414

include/swift/Frontend/Frontend.h

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -318,20 +318,11 @@ class CompilerInvocation {
318318
return CodeCompletionOffset != ~0U;
319319
}
320320

321-
void setCodeCompletionFactory(CodeCompletionCallbacksFactory *Factory) {
322-
CodeCompletionFactory = Factory;
323-
disableASTScopeLookup();
324-
}
325-
326321
/// Called from lldb, see rdar://53971116
327322
void disableASTScopeLookup() {
328323
LangOpts.EnableASTScopeLookup = false;
329324
}
330325

331-
CodeCompletionCallbacksFactory *getCodeCompletionFactory() const {
332-
return CodeCompletionFactory;
333-
}
334-
335326
/// Retrieve a module hash string that is suitable for uniquely
336327
/// identifying the conditions under which the module was built, for use
337328
/// in generating a cached PCH file for the bridging header.
@@ -483,6 +474,10 @@ class CompilerInstance {
483474
Diagnostics.addConsumer(*DC);
484475
}
485476

477+
void removeDiagnosticConsumer(DiagnosticConsumer *DC) {
478+
Diagnostics.removeConsumer(*DC);
479+
}
480+
486481
void createDependencyTracker(bool TrackSystemDeps) {
487482
assert(!Context && "must be called before setup()");
488483
DepTracker = llvm::make_unique<DependencyTracker>(TrackSystemDeps);
@@ -547,6 +542,18 @@ class CompilerInstance {
547542
/// Returns true if there was an error during setup.
548543
bool setup(const CompilerInvocation &Invocation);
549544

545+
const CompilerInvocation &getInvocation() {
546+
return Invocation;
547+
}
548+
549+
bool hasPersistentParserState() const {
550+
return bool(PersistentState);
551+
}
552+
553+
PersistentParserState &getPersistentParserState() {
554+
return *PersistentState.get();
555+
}
556+
550557
private:
551558
/// Set up the file system by loading and validating all VFS overlay YAML
552559
/// files. If the process of validating VFS files failed, or the overlay
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//===--- CompletionInstance.h ---------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2019 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_COMPLETIONINSTANCE_H
14+
#define SWIFT_IDE_COMPLETIONINSTANCE_H
15+
16+
#include "swift/Frontend/Frontend.h"
17+
#include "llvm/ADT/Hashing.h"
18+
#include "llvm/ADT/IntrusiveRefCntPtr.h"
19+
#include "llvm/ADT/SmallString.h"
20+
#include "llvm/ADT/StringRef.h"
21+
#include "llvm/Support/MemoryBuffer.h"
22+
#include "llvm/Support/VirtualFileSystem.h"
23+
24+
namespace swift {
25+
26+
class CompilerInstance;
27+
class CompilerInvocation;
28+
class DiagnosticConsumer;
29+
30+
namespace ide {
31+
32+
/// Copy a memory buffer inserting '\0' at the position of \c origBuf.
33+
std::unique_ptr<llvm::MemoryBuffer>
34+
makeCodeCompletionMemoryBuffer(const llvm::MemoryBuffer *origBuf,
35+
unsigned &Offset,
36+
llvm::StringRef bufferIdentifier);
37+
38+
/// Manages \c CompilerInstance for completion like operations.
39+
class CompletionInstance {
40+
unsigned MaxASTReuseCount = 100;
41+
42+
std::mutex mtx;
43+
44+
std::unique_ptr<CompilerInstance> CachedCI;
45+
llvm::hash_code CachedArgHash;
46+
unsigned CachedReuseCount = 0;
47+
48+
/// Calls \p Callback with cached \c CompilerInstance if it's usable for the
49+
/// specified completion request.
50+
/// Returns \c if the callback was called. Returns \c false if the compiler
51+
/// argument has changed, primary file is not the same, the \c Offset is not
52+
/// in function bodies, or the interface hash of the file has changed.
53+
bool performCachedOperaitonIfPossible(
54+
const swift::CompilerInvocation &Invocation, llvm::hash_code ArgsHash,
55+
llvm::MemoryBuffer *completionBuffer, unsigned int Offset,
56+
DiagnosticConsumer *DiagC,
57+
llvm::function_ref<void(CompilerInstance &)> Callback);
58+
59+
/// Calls \p Callback with new \c CompilerInstance for the completion
60+
/// request. The \c CompilerInstace passed to the callback already performed
61+
/// the first pass.
62+
/// Returns \c false if it fails to setup the \c CompilerInstance.
63+
bool performNewOperation(
64+
llvm::Optional<llvm::hash_code> ArgsHash,
65+
swift::CompilerInvocation &Invocation,
66+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
67+
llvm::MemoryBuffer *completionBuffer, unsigned int Offset,
68+
std::string &Error, DiagnosticConsumer *DiagC,
69+
llvm::function_ref<void(CompilerInstance &)> Callback);
70+
71+
public:
72+
/// Calls \p Callback with a \c CompilerInstance which is prepared for the
73+
/// second pass. \p Callback is resposible to perform the second pass on it.
74+
/// The \c CompilerInstance may be reused from the previous completions,
75+
/// and may be cached for the next completion.
76+
/// Return \c true if \p is successfully called, \c it fails. In failure
77+
/// cases \p Error is populated with an error message.
78+
///
79+
/// NOTE: \p Args is only used for checking the equaity of the invocation.
80+
/// Since this function assumes that it is already normalized, exact the same
81+
/// arguments including their order is considered as the same invocation.
82+
bool performOperation(
83+
swift::CompilerInvocation &Invocation, llvm::ArrayRef<const char *> Args,
84+
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FileSystem,
85+
llvm::MemoryBuffer *completionBuffer, unsigned int Offset,
86+
bool EnableASTCaching, std::string &Error, DiagnosticConsumer *DiagC,
87+
llvm::function_ref<void(CompilerInstance &)> Callback);
88+
};
89+
90+
} // namespace ide
91+
} // namespace swift
92+
93+
#endif // SWIFT_IDE_COMPLETIONINSTANCE_H

include/swift/Parse/LocalContext.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#define SWIFT_PARSE_LOCALCONTEXT_H
2020

2121
#include "llvm/ADT/DenseMap.h"
22+
#include "swift/AST/Identifier.h"
2223
#include <cassert>
2324

2425
namespace swift {

include/swift/Parse/Parser.h

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1642,12 +1642,8 @@ class Parser {
16421642
//===--------------------------------------------------------------------===//
16431643
// Code completion second pass.
16441644

1645-
static void
1646-
performCodeCompletionSecondPass(PersistentParserState &ParserState,
1647-
CodeCompletionCallbacksFactory &Factory);
1648-
16491645
void performCodeCompletionSecondPassImpl(
1650-
PersistentParserState::CodeCompletionDelayedDeclState &info);
1646+
CodeCompletionDelayedDeclState &info);
16511647
};
16521648

16531649
/// Describes a parsed declaration name.

include/swift/Parse/PersistentParserState.h

Lines changed: 35 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,33 @@ class SourceFile;
2929
class DeclContext;
3030
class IterableDeclContext;
3131

32+
enum class CodeCompletionDelayedDeclKind {
33+
TopLevelCodeDecl,
34+
Decl,
35+
FunctionBody,
36+
};
37+
38+
class CodeCompletionDelayedDeclState {
39+
public:
40+
CodeCompletionDelayedDeclKind Kind;
41+
unsigned Flags;
42+
DeclContext *ParentContext;
43+
SavedScope Scope;
44+
unsigned StartOffset;
45+
unsigned EndOffset;
46+
unsigned PrevOffset;
47+
48+
SavedScope takeScope() { return std::move(Scope); }
49+
50+
CodeCompletionDelayedDeclState(CodeCompletionDelayedDeclKind Kind,
51+
unsigned Flags, DeclContext *ParentContext,
52+
SavedScope &&Scope, unsigned StartOffset,
53+
unsigned EndOffset, unsigned PrevOffset)
54+
: Kind(Kind), Flags(Flags), ParentContext(ParentContext),
55+
Scope(std::move(Scope)), StartOffset(StartOffset), EndOffset(EndOffset),
56+
PrevOffset(PrevOffset) {}
57+
};
58+
3259
/// Parser state persistent across multiple parses.
3360
class PersistentParserState {
3461
public:
@@ -39,36 +66,6 @@ class PersistentParserState {
3966
bool isValid() const { return Loc.isValid(); }
4067
};
4168

42-
enum class CodeCompletionDelayedDeclKind {
43-
TopLevelCodeDecl,
44-
Decl,
45-
FunctionBody,
46-
};
47-
48-
class CodeCompletionDelayedDeclState {
49-
friend class PersistentParserState;
50-
friend class Parser;
51-
CodeCompletionDelayedDeclKind Kind;
52-
unsigned Flags;
53-
DeclContext *ParentContext;
54-
ParserPos BodyPos;
55-
SourceLoc BodyEnd;
56-
SavedScope Scope;
57-
58-
SavedScope takeScope() {
59-
return std::move(Scope);
60-
}
61-
62-
public:
63-
CodeCompletionDelayedDeclState(CodeCompletionDelayedDeclKind Kind,
64-
unsigned Flags, DeclContext *ParentContext,
65-
SourceRange BodyRange, SourceLoc PreviousLoc,
66-
SavedScope &&Scope)
67-
: Kind(Kind), Flags(Flags),
68-
ParentContext(ParentContext), BodyPos{BodyRange.Start, PreviousLoc},
69-
BodyEnd(BodyRange.End), Scope(std::move(Scope)) {}
70-
};
71-
7269
bool InPoundLineEnvironment = false;
7370
// FIXME: When condition evaluation moves to a later phase, remove this bit
7471
// and adjust the client call 'performParseOnly'.
@@ -92,16 +89,23 @@ class PersistentParserState {
9289
PersistentParserState(ASTContext &ctx) : PersistentParserState() { }
9390
~PersistentParserState();
9491

95-
void setCodeCompletionDelayedDeclState(CodeCompletionDelayedDeclKind Kind,
92+
void setCodeCompletionDelayedDeclState(SourceManager &SM, unsigned BufferID,
93+
CodeCompletionDelayedDeclKind Kind,
9694
unsigned Flags,
9795
DeclContext *ParentContext,
9896
SourceRange BodyRange,
9997
SourceLoc PreviousLoc);
98+
void restoreCodeCompletionDelayedDeclState(
99+
const CodeCompletionDelayedDeclState &other);
100100

101101
bool hasCodeCompletionDelayedDeclState() {
102102
return CodeCompletionDelayedDeclStat.get() != nullptr;
103103
}
104104

105+
CodeCompletionDelayedDeclState &getCodeCompletionDelayedDeclState() {
106+
return *CodeCompletionDelayedDeclStat.get();
107+
}
108+
105109
std::unique_ptr<CodeCompletionDelayedDeclState>
106110
takeCodeCompletionDelayedDeclState() {
107111
assert(hasCodeCompletionDelayedDeclState());

include/swift/Parse/Scope.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ class Scope {
141141
bool isResolvable() const;
142142

143143
public:
144+
Scope(ScopeInfo &SI, ScopeKind SC, bool isInactiveConfigBlock = false);
145+
144146
/// Create a lexical scope of the specified kind.
145147
Scope(Parser *P, ScopeKind SC, bool isInactiveConfigBlock = false);
146148

lib/AST/Decl.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6628,6 +6628,20 @@ BraceStmt *AbstractFunctionDecl::getBody(bool canSynthesize) const {
66286628
nullptr);
66296629
}
66306630

6631+
void AbstractFunctionDecl::setBody(BraceStmt *S, BodyKind NewBodyKind) {
6632+
assert(getBodyKind() != BodyKind::Skipped &&
6633+
"cannot set a body if it was skipped");
6634+
6635+
Body = S;
6636+
setBodyKind(NewBodyKind);
6637+
6638+
// Need to recompute init body kind.
6639+
if (NewBodyKind < BodyKind::TypeChecked) {
6640+
if (auto *ctor = dyn_cast<ConstructorDecl>(this))
6641+
ctor->clearCachedDelegatingOrChainedInitKind();
6642+
}
6643+
}
6644+
66316645
SourceRange AbstractFunctionDecl::getBodySourceRange() const {
66326646
switch (getBodyKind()) {
66336647
case BodyKind::None:
@@ -7404,8 +7418,11 @@ ConstructorDecl::getDelegatingOrChainedInitKind(DiagnosticEngine *diags,
74047418

74057419
// If we already computed the result, return it.
74067420
if (Bits.ConstructorDecl.ComputedBodyInitKind) {
7407-
return static_cast<BodyInitKind>(
7408-
Bits.ConstructorDecl.ComputedBodyInitKind - 1);
7421+
auto Kind = static_cast<BodyInitKind>(
7422+
Bits.ConstructorDecl.ComputedBodyInitKind - 1);
7423+
assert((Kind == BodyInitKind::None || !init) &&
7424+
"can't return cached result with the init expr");
7425+
return Kind;
74097426
}
74107427

74117428

lib/Frontend/Frontend.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -894,12 +894,6 @@ void CompilerInstance::parseAndCheckTypesUpTo(
894894
}
895895
});
896896

897-
if (Invocation.isCodeCompletion()) {
898-
assert(limitStage == SourceFile::NameBound);
899-
performCodeCompletionSecondPass(*PersistentState.get(),
900-
*Invocation.getCodeCompletionFactory());
901-
}
902-
903897
// If the limiting AST stage is name binding, we're done.
904898
if (limitStage <= SourceFile::NameBound) {
905899
return;

lib/IDE/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ add_swift_host_library(swiftIDE STATIC
22
CodeCompletion.cpp
33
CodeCompletionCache.cpp
44
CommentConversion.cpp
5+
CompletionInstance.cpp
56
ConformingMethodList.cpp
67
ExprContextAnalysis.cpp
78
Formatting.cpp

0 commit comments

Comments
 (0)