Skip to content

Commit fda4dfc

Browse files
authored
Merge pull request #28884 from rintaro/5.2-sourcekit-completion-reuseinstance
[5.2][SourceKit] Fast code completion within function bodies
2 parents e41ae93 + 1115ecc commit fda4dfc

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
@@ -5818,13 +5818,7 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
58185818
/// \sa hasBody()
58195819
BraceStmt *getBody(bool canSynthesize = true) const;
58205820

5821-
void setBody(BraceStmt *S, BodyKind NewBodyKind = BodyKind::Parsed) {
5822-
assert(getBodyKind() != BodyKind::Skipped &&
5823-
"cannot set a body if it was skipped");
5824-
5825-
Body = S;
5826-
setBodyKind(NewBodyKind);
5827-
}
5821+
void setBody(BraceStmt *S, BodyKind NewBodyKind = BodyKind::Parsed);
58285822

58295823
/// Note that the body was skipped for this function. Function body
58305824
/// cannot be attached after this call.
@@ -5843,7 +5837,8 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
58435837

58445838
/// Note that parsing for the body was delayed.
58455839
void setBodyDelayed(SourceRange bodyRange) {
5846-
assert(getBodyKind() == BodyKind::None);
5840+
assert(getBodyKind() == BodyKind::None ||
5841+
getBodyKind() == BodyKind::Skipped);
58475842
assert(bodyRange.isValid());
58485843
BodyRange = bodyRange;
58495844
setBodyKind(BodyKind::Unparsed);
@@ -6634,6 +6629,9 @@ class ConstructorDecl : public AbstractFunctionDecl {
66346629
/// initializer.
66356630
BodyInitKind getDelegatingOrChainedInitKind(DiagnosticEngine *diags,
66366631
ApplyExpr **init = nullptr) const;
6632+
void clearCachedDelegatingOrChainedInitKind() {
6633+
Bits.ConstructorDecl.ComputedBodyInitKind = 0;
6634+
}
66376635

66386636
/// Whether this constructor is required.
66396637
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.
@@ -481,6 +472,10 @@ class CompilerInstance {
481472
Diagnostics.addConsumer(*DC);
482473
}
483474

475+
void removeDiagnosticConsumer(DiagnosticConsumer *DC) {
476+
Diagnostics.removeConsumer(*DC);
477+
}
478+
484479
void createDependencyTracker(bool TrackSystemDeps) {
485480
assert(!Context && "must be called before setup()");
486481
DepTracker = llvm::make_unique<DependencyTracker>(TrackSystemDeps);
@@ -545,6 +540,18 @@ class CompilerInstance {
545540
/// Returns true if there was an error during setup.
546541
bool setup(const CompilerInvocation &Invocation);
547542

543+
const CompilerInvocation &getInvocation() {
544+
return Invocation;
545+
}
546+
547+
bool hasPersistentParserState() const {
548+
return bool(PersistentState);
549+
}
550+
551+
PersistentParserState &getPersistentParserState() {
552+
return *PersistentState.get();
553+
}
554+
548555
private:
549556
/// Set up the file system by loading and validating all VFS overlay YAML
550557
/// 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
@@ -1578,12 +1578,8 @@ class Parser {
15781578
//===--------------------------------------------------------------------===//
15791579
// Code completion second pass.
15801580

1581-
static void
1582-
performCodeCompletionSecondPass(PersistentParserState &ParserState,
1583-
CodeCompletionCallbacksFactory &Factory);
1584-
15851581
void performCodeCompletionSecondPassImpl(
1586-
PersistentParserState::CodeCompletionDelayedDeclState &info);
1582+
CodeCompletionDelayedDeclState &info);
15871583
};
15881584

15891585
/// 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
@@ -6626,6 +6626,20 @@ BraceStmt *AbstractFunctionDecl::getBody(bool canSynthesize) const {
66266626
nullptr);
66276627
}
66286628

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

74037417
// If we already computed the result, return it.
74047418
if (Bits.ConstructorDecl.ComputedBodyInitKind) {
7405-
return static_cast<BodyInitKind>(
7406-
Bits.ConstructorDecl.ComputedBodyInitKind - 1);
7419+
auto Kind = static_cast<BodyInitKind>(
7420+
Bits.ConstructorDecl.ComputedBodyInitKind - 1);
7421+
assert((Kind == BodyInitKind::None || !init) &&
7422+
"can't return cached result with the init expr");
7423+
return Kind;
74077424
}
74087425

74097426

lib/Frontend/Frontend.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -886,12 +886,6 @@ void CompilerInstance::parseAndCheckTypesUpTo(
886886
}
887887
});
888888

889-
if (Invocation.isCodeCompletion()) {
890-
assert(limitStage == SourceFile::NameBound);
891-
performCodeCompletionSecondPass(*PersistentState.get(),
892-
*Invocation.getCodeCompletionFactory());
893-
}
894-
895889
// If the limiting AST stage is name binding, we're done.
896890
if (limitStage <= SourceFile::NameBound) {
897891
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)