Skip to content

Commit 1ab4a47

Browse files
committed
[AST] Formalize getBodySourceRange() for fast completion
Fast completion replaces the body ('BraceStmt') of function decls with other bodies parsed from different source buffers from the original source buffer. That means the source range of the body and the location of the function declaration itself might be in different buffers. Previously, FuncDecl::getSourceRange() used to use the 'func' keyword decl as the start loc and 'getBodySourceRange().End' as the end loc. This breaks a SourceRange invariant where the start and end loc must be in the same buffer. This patch add a new function 'getOriginalBodySourceRange()' which always return the source range of the original body of the function. And use that from 'getSourceRange()' functions. The orignal body source range is stored in a side table in ASTContext so that normal compilation doesn't consume space for that extra info.
1 parent 0d25abc commit 1ab4a47

File tree

4 files changed

+72
-5
lines changed

4 files changed

+72
-5
lines changed

include/swift/AST/Decl.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5942,6 +5942,8 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
59425942
setBodyKind(BodyKind::Unparsed);
59435943
}
59445944

5945+
void setBodyToBeReparsed(SourceRange bodyRange);
5946+
59455947
/// Provide the parsed body for the function.
59465948
void setBodyParsed(BraceStmt *S) {
59475949
setBody(S, BodyKind::Parsed);
@@ -6007,6 +6009,19 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
60076009
/// Retrieve the source range of the function body.
60086010
SourceRange getBodySourceRange() const;
60096011

6012+
/// Keep current \c getBodySourceRange() as the "original" body source range
6013+
/// iff the this method hasn't been called on this object. The current body
6014+
/// source range must be in the same buffer as the location of the declaration
6015+
/// itself.
6016+
void keepOriginalBodySourceRange();
6017+
6018+
/// Retrieve the source range of the *original* function body.
6019+
///
6020+
/// This may be different from \c getBodySourceRange() that returns the source
6021+
/// range of the *current* body. It happens when the body is parsed from other
6022+
/// source buffers for e.g. code-completion.
6023+
SourceRange getOriginalBodySourceRange() const;
6024+
60106025
/// Retrieve the source range of the function declaration name + patterns.
60116026
SourceRange getSignatureSourceRange() const;
60126027

lib/AST/ASTContext.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,10 @@ struct ASTContext::Implementation {
339339
/// LiteralExprs in fully-checked AST.
340340
llvm::DenseMap<const NominalTypeDecl *, ConcreteDeclRef> BuiltinInitWitness;
341341

342+
/// Mapping from the function decl to its original body's source range. This
343+
/// is populated if the body is reparsed from other source buffers.
344+
llvm::DenseMap<const AbstractFunctionDecl *, SourceRange> OriginalBodySourceRanges;
345+
342346
/// Structure that captures data that is segregated into different
343347
/// arenas.
344348
struct Arena {
@@ -4778,6 +4782,36 @@ void VarDecl::setOriginalWrappedProperty(VarDecl *originalProperty) {
47784782
ctx.getImpl().OriginalWrappedProperties[this] = originalProperty;
47794783
}
47804784

4785+
#ifndef NDEBUG
4786+
static bool isSourceLocInOrignalBuffer(const Decl *D, SourceLoc Loc) {
4787+
assert(Loc.isValid());
4788+
auto bufferID = D->getDeclContext()->getParentSourceFile()->getBufferID();
4789+
assert(bufferID.hasValue() && "Source buffer ID must be set");
4790+
auto &SM = D->getASTContext().SourceMgr;
4791+
return SM.getRangeForBuffer(*bufferID).contains(Loc);
4792+
}
4793+
#endif
4794+
4795+
void AbstractFunctionDecl::keepOriginalBodySourceRange() {
4796+
auto &impl = getASTContext().getImpl();
4797+
auto result =
4798+
impl.OriginalBodySourceRanges.insert({this, getBodySourceRange()});
4799+
assert((!result.second ||
4800+
isSourceLocInOrignalBuffer(this, result.first->getSecond().Start)) &&
4801+
"This function must be called before setting new body range");
4802+
(void)result;
4803+
}
4804+
4805+
SourceRange AbstractFunctionDecl::getOriginalBodySourceRange() const {
4806+
auto &impl = getASTContext().getImpl();
4807+
auto found = impl.OriginalBodySourceRanges.find(this);
4808+
if (found != impl.OriginalBodySourceRanges.end()) {
4809+
return found->getSecond();
4810+
} else {
4811+
return getBodySourceRange();
4812+
}
4813+
}
4814+
47814815
IndexSubset *
47824816
IndexSubset::get(ASTContext &ctx, const SmallBitVector &indices) {
47834817
auto &foldingSet = ctx.getImpl().IndexSubsets;

lib/AST/Decl.cpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6726,6 +6726,21 @@ void AbstractFunctionDecl::setBody(BraceStmt *S, BodyKind NewBodyKind) {
67266726
}
67276727
}
67286728

6729+
void AbstractFunctionDecl::setBodyToBeReparsed(SourceRange bodyRange) {
6730+
assert(bodyRange.isValid());
6731+
assert(getBodyKind() == BodyKind::None ||
6732+
getBodyKind() == BodyKind::Skipped ||
6733+
getBodyKind() == BodyKind::Parsed ||
6734+
getBodyKind() == BodyKind::TypeChecked);
6735+
assert(getASTContext().SourceMgr.rangeContainsTokenLoc(
6736+
bodyRange, getASTContext().SourceMgr.getCodeCompletionLoc()) &&
6737+
"This function is only intended to be used for code completion");
6738+
6739+
keepOriginalBodySourceRange();
6740+
BodyRange = bodyRange;
6741+
setBodyKind(BodyKind::Unparsed);
6742+
}
6743+
67296744
SourceRange AbstractFunctionDecl::getBodySourceRange() const {
67306745
switch (getBodyKind()) {
67316746
case BodyKind::None:
@@ -7386,7 +7401,7 @@ SourceRange FuncDecl::getSourceRange() const {
73867401
getBodyKind() == BodyKind::Skipped)
73877402
return { StartLoc, BodyRange.End };
73887403

7389-
SourceLoc RBraceLoc = getBodySourceRange().End;
7404+
SourceLoc RBraceLoc = getOriginalBodySourceRange().End;
73907405
if (RBraceLoc.isValid()) {
73917406
return { StartLoc, RBraceLoc };
73927407
}
@@ -7507,7 +7522,7 @@ SourceRange ConstructorDecl::getSourceRange() const {
75077522
if (isImplicit())
75087523
return getConstructorLoc();
75097524

7510-
SourceLoc End = getBodySourceRange().End;
7525+
SourceLoc End = getOriginalBodySourceRange().End;
75117526
if (End.isInvalid())
75127527
End = getGenericTrailingWhereClauseSourceRange().End;
75137528
if (End.isInvalid())
@@ -7733,7 +7748,7 @@ ConstructorDecl::getDelegatingOrChainedInitKind(DiagnosticEngine *diags,
77337748
}
77347749

77357750
SourceRange DestructorDecl::getSourceRange() const {
7736-
SourceLoc End = getBodySourceRange().End;
7751+
SourceLoc End = getOriginalBodySourceRange().End;
77377752
if (End.isInvalid()) {
77387753
End = getDestructorLoc();
77397754
}

lib/IDE/CompletionInstance.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -411,9 +411,12 @@ bool CompletionInstance::performCachedOperationIfPossible(
411411
oldInfo.PrevOffset = newInfo.PrevOffset;
412412
oldState->restoreCodeCompletionDelayedDeclState(oldInfo);
413413

414+
auto newBufferStart = SM.getRangeForBuffer(newBufferID).getStart();
415+
SourceRange newBodyRange(newBufferStart.getAdvancedLoc(newInfo.StartOffset),
416+
newBufferStart.getAdvancedLoc(newInfo.EndOffset));
417+
414418
auto *AFD = cast<AbstractFunctionDecl>(DC);
415-
if (AFD->isBodySkipped())
416-
AFD->setBodyDelayed(AFD->getBodySourceRange());
419+
AFD->setBodyToBeReparsed(newBodyRange);
417420

418421
traceDC = AFD;
419422
break;

0 commit comments

Comments
 (0)