Skip to content

[AST] Formalize getBodySourceRange() for fast completion #33421

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Aug 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -5935,13 +5935,14 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {

/// Note that parsing for the body was delayed.
void setBodyDelayed(SourceRange bodyRange) {
assert(getBodyKind() == BodyKind::None ||
getBodyKind() == BodyKind::Skipped);
assert(getBodyKind() == BodyKind::None);
assert(bodyRange.isValid());
BodyRange = bodyRange;
setBodyKind(BodyKind::Unparsed);
}

void setBodyToBeReparsed(SourceRange bodyRange);

/// Provide the parsed body for the function.
void setBodyParsed(BraceStmt *S) {
setBody(S, BodyKind::Parsed);
Expand Down Expand Up @@ -6007,6 +6008,19 @@ class AbstractFunctionDecl : public GenericContext, public ValueDecl {
/// Retrieve the source range of the function body.
SourceRange getBodySourceRange() const;

/// Keep current \c getBodySourceRange() as the "original" body source range
/// iff the this method hasn't been called on this object. The current body
/// source range must be in the same buffer as the location of the declaration
/// itself.
void keepOriginalBodySourceRange();

/// Retrieve the source range of the *original* function body.
///
/// This may be different from \c getBodySourceRange() that returns the source
/// range of the *current* body. It happens when the body is parsed from other
/// source buffers for e.g. code-completion.
SourceRange getOriginalBodySourceRange() const;

/// Retrieve the source range of the function declaration name + patterns.
SourceRange getSignatureSourceRange() const;

Expand Down
34 changes: 34 additions & 0 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,10 @@ struct ASTContext::Implementation {
/// LiteralExprs in fully-checked AST.
llvm::DenseMap<const NominalTypeDecl *, ConcreteDeclRef> BuiltinInitWitness;

/// Mapping from the function decl to its original body's source range. This
/// is populated if the body is reparsed from other source buffers.
llvm::DenseMap<const AbstractFunctionDecl *, SourceRange> OriginalBodySourceRanges;

/// Structure that captures data that is segregated into different
/// arenas.
struct Arena {
Expand Down Expand Up @@ -4778,6 +4782,36 @@ void VarDecl::setOriginalWrappedProperty(VarDecl *originalProperty) {
ctx.getImpl().OriginalWrappedProperties[this] = originalProperty;
}

#ifndef NDEBUG
static bool isSourceLocInOrignalBuffer(const Decl *D, SourceLoc Loc) {
assert(Loc.isValid());
auto bufferID = D->getDeclContext()->getParentSourceFile()->getBufferID();
assert(bufferID.hasValue() && "Source buffer ID must be set");
auto &SM = D->getASTContext().SourceMgr;
return SM.getRangeForBuffer(*bufferID).contains(Loc);
}
#endif

void AbstractFunctionDecl::keepOriginalBodySourceRange() {
auto &impl = getASTContext().getImpl();
auto result =
impl.OriginalBodySourceRanges.insert({this, getBodySourceRange()});
assert((!result.second ||
isSourceLocInOrignalBuffer(this, result.first->getSecond().Start)) &&
"This function must be called before setting new body range");
(void)result;
}

SourceRange AbstractFunctionDecl::getOriginalBodySourceRange() const {
auto &impl = getASTContext().getImpl();
auto found = impl.OriginalBodySourceRanges.find(this);
if (found != impl.OriginalBodySourceRanges.end()) {
return found->getSecond();
} else {
return getBodySourceRange();
}
}

IndexSubset *
IndexSubset::get(ASTContext &ctx, const SmallBitVector &indices) {
auto &foldingSet = ctx.getImpl().IndexSubsets;
Expand Down
20 changes: 17 additions & 3 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6726,6 +6726,20 @@ void AbstractFunctionDecl::setBody(BraceStmt *S, BodyKind NewBodyKind) {
}
}

void AbstractFunctionDecl::setBodyToBeReparsed(SourceRange bodyRange) {
assert(bodyRange.isValid());
assert(getBodyKind() == BodyKind::Unparsed ||
getBodyKind() == BodyKind::Parsed ||
getBodyKind() == BodyKind::TypeChecked);
assert(getASTContext().SourceMgr.rangeContainsTokenLoc(
bodyRange, getASTContext().SourceMgr.getCodeCompletionLoc()) &&
"This function is only intended to be used for code completion");

keepOriginalBodySourceRange();
BodyRange = bodyRange;
setBodyKind(BodyKind::Unparsed);
}

SourceRange AbstractFunctionDecl::getBodySourceRange() const {
switch (getBodyKind()) {
case BodyKind::None:
Expand Down Expand Up @@ -7386,7 +7400,7 @@ SourceRange FuncDecl::getSourceRange() const {
getBodyKind() == BodyKind::Skipped)
return { StartLoc, BodyRange.End };

SourceLoc RBraceLoc = getBodySourceRange().End;
SourceLoc RBraceLoc = getOriginalBodySourceRange().End;
if (RBraceLoc.isValid()) {
return { StartLoc, RBraceLoc };
}
Expand Down Expand Up @@ -7507,7 +7521,7 @@ SourceRange ConstructorDecl::getSourceRange() const {
if (isImplicit())
return getConstructorLoc();

SourceLoc End = getBodySourceRange().End;
SourceLoc End = getOriginalBodySourceRange().End;
if (End.isInvalid())
End = getGenericTrailingWhereClauseSourceRange().End;
if (End.isInvalid())
Expand Down Expand Up @@ -7733,7 +7747,7 @@ ConstructorDecl::getDelegatingOrChainedInitKind(DiagnosticEngine *diags,
}

SourceRange DestructorDecl::getSourceRange() const {
SourceLoc End = getBodySourceRange().End;
SourceLoc End = getOriginalBodySourceRange().End;
if (End.isInvalid()) {
End = getDestructorLoc();
}
Expand Down
7 changes: 5 additions & 2 deletions lib/IDE/CompletionInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -411,9 +411,12 @@ bool CompletionInstance::performCachedOperationIfPossible(
oldInfo.PrevOffset = newInfo.PrevOffset;
oldState->restoreCodeCompletionDelayedDeclState(oldInfo);

auto newBufferStart = SM.getRangeForBuffer(newBufferID).getStart();
SourceRange newBodyRange(newBufferStart.getAdvancedLoc(newInfo.StartOffset),
newBufferStart.getAdvancedLoc(newInfo.EndOffset));

auto *AFD = cast<AbstractFunctionDecl>(DC);
if (AFD->isBodySkipped())
AFD->setBodyDelayed(AFD->getBodySourceRange());
AFD->setBodyToBeReparsed(newBodyRange);

traceDC = AFD;
break;
Expand Down
15 changes: 6 additions & 9 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6248,15 +6248,12 @@ void Parser::consumeAbstractFunctionBody(AbstractFunctionDecl *AFD,

AFD->setBodyDelayed(BodyRange);

if (isCodeCompletionFirstPass()) {
if (SourceMgr.rangeContainsCodeCompletionLoc(BodyRange)) {
State->setCodeCompletionDelayedDeclState(
SourceMgr, L->getBufferID(),
CodeCompletionDelayedDeclKind::FunctionBody,
PD_Default, AFD, BodyRange, BeginParserPosition.PreviousLoc);
} else {
AFD->setBodySkipped(BodyRange);
}
if (isCodeCompletionFirstPass() &&
SourceMgr.rangeContainsCodeCompletionLoc(BodyRange)) {
State->setCodeCompletionDelayedDeclState(
SourceMgr, L->getBufferID(),
CodeCompletionDelayedDeclKind::FunctionBody,
PD_Default, AFD, BodyRange, BeginParserPosition.PreviousLoc);
}
}

Expand Down