Skip to content

Commit 21b91cd

Browse files
authored
Merge pull request #33433 from rintaro/ide-completion-astscope-rdar58939376-pt2
[CodeCompletion] Enable ASTScope in code completion
2 parents 90f5730 + b070e85 commit 21b91cd

File tree

9 files changed

+118
-38
lines changed

9 files changed

+118
-38
lines changed

include/swift/AST/SourceFile.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ class SourceFile final : public FileUnit {
172172
bool IsPrimary;
173173

174174
/// The scope map that describes this source file.
175-
std::unique_ptr<ASTScope> Scope;
175+
NullablePtr<ASTScope> Scope = nullptr;
176176

177177
/// The set of validated opaque return type decls in the source file.
178178
llvm::SmallVector<OpaqueTypeDecl *, 4> OpaqueReturnTypes;
@@ -468,6 +468,10 @@ class SourceFile final : public FileUnit {
468468
/// Retrieve the scope that describes this source file.
469469
ASTScope &getScope();
470470

471+
void clearScope() {
472+
Scope = nullptr;
473+
}
474+
471475
/// Retrieves the previously set delayed parser state, asserting that it
472476
/// exists.
473477
PersistentParserState *getDelayedParserState() {

include/swift/Basic/SourceManager.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,20 @@ class SourceManager {
3838
/// to speed up stats.
3939
mutable llvm::DenseMap<StringRef, llvm::vfs::Status> StatusCache;
4040

41+
struct ReplacedRangeType {
42+
SourceRange Original;
43+
SourceRange New;
44+
ReplacedRangeType() {}
45+
ReplacedRangeType(NoneType) {}
46+
ReplacedRangeType(SourceRange Original, SourceRange New)
47+
: Original(Original), New(New) {
48+
assert(Original.isValid() && New.isValid());
49+
}
50+
51+
explicit operator bool() const { return Original.isValid(); }
52+
};
53+
ReplacedRangeType ReplacedRange;
54+
4155
// \c #sourceLocation directive handling.
4256
struct VirtualFile {
4357
CharSourceRange Range;
@@ -89,6 +103,9 @@ class SourceManager {
89103

90104
SourceLoc getCodeCompletionLoc() const;
91105

106+
const ReplacedRangeType &getReplacedRange() const { return ReplacedRange; }
107+
void setReplacedRange(const ReplacedRangeType &val) { ReplacedRange = val; }
108+
92109
/// Returns true if \c LHS is before \c RHS in the source buffer.
93110
bool isBeforeInBuffer(SourceLoc LHS, SourceLoc RHS) const {
94111
return LHS.Value.getPointer() < RHS.Value.getPointer();

lib/AST/ASTScopeCreation.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -795,7 +795,7 @@ void ASTSourceFileScope::
795795
void ASTSourceFileScope::expandFunctionBody(AbstractFunctionDecl *AFD) {
796796
if (!AFD)
797797
return;
798-
auto sr = AFD->getBodySourceRange();
798+
auto sr = AFD->getOriginalBodySourceRange();
799799
if (sr.isInvalid())
800800
return;
801801
ASTScopeImpl *bodyScope = findInnermostEnclosingScope(sr.Start, nullptr);
@@ -1596,11 +1596,6 @@ ASTScopeImpl *GenericTypeOrExtensionWholePortion::expandScope(
15961596
// Get now in case recursion emancipates scope
15971597
auto *const ip = scope->getParent().get();
15981598

1599-
// Prevent circular request bugs caused by illegal input and
1600-
// doing lookups that getExtendedNominal in the midst of getExtendedNominal.
1601-
if (scope->shouldHaveABody() && !scope->doesDeclHaveABody())
1602-
return ip;
1603-
16041599
auto *context = scope->getGenericContext();
16051600
auto *genericParams = (isa<TypeAliasDecl>(context)
16061601
? context->getParsedGenericParams()
@@ -1609,6 +1604,12 @@ ASTScopeImpl *GenericTypeOrExtensionWholePortion::expandScope(
16091604
scope->getDecl(), genericParams, scope);
16101605
if (context->getTrailingWhereClause())
16111606
scope->createTrailingWhereClauseScope(deepestScope, scopeCreator);
1607+
1608+
// Prevent circular request bugs caused by illegal input and
1609+
// doing lookups that getExtendedNominal in the midst of getExtendedNominal.
1610+
if (scope->shouldHaveABody() && !scope->doesDeclHaveABody())
1611+
return ip;
1612+
16121613
scope->createBodyScope(deepestScope, scopeCreator);
16131614
return ip;
16141615
}

lib/AST/ASTScopeLookup.cpp

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030
#include "swift/AST/TypeRepr.h"
3131
#include "swift/Basic/STLExtras.h"
3232
#include "llvm/Support/Compiler.h"
33-
#include <algorithm>
3433

3534
using namespace swift;
3635
using namespace namelookup;
@@ -82,6 +81,15 @@ const ASTScopeImpl *ASTScopeImpl::findStartingScopeForLookup(
8281
// Someday, just use the assertion below. For now, print out lots of info for
8382
// debugging.
8483
if (!startingScope) {
84+
85+
// Be lenient in code completion mode. There are cases where the decl
86+
// context doesn't match with the ASTScope. e.g. dangling attributes.
87+
// FIXME: Create ASTScope tree even for invalid code.
88+
if (innermost &&
89+
startingContext->getASTContext().SourceMgr.hasCodeCompletionBuffer()) {
90+
return innermost;
91+
}
92+
8593
llvm::errs() << "ASTScopeImpl: resorting to startingScope hack, file: "
8694
<< sourceFile->getFilename() << "\n";
8795
// The check is costly, and inactive lookups will end up here, so don't
@@ -143,33 +151,40 @@ bool ASTScopeImpl::checkSourceRangeOfThisASTNode() const {
143151
return true;
144152
}
145153

154+
/// If the \p loc is in a new buffer but \p range is not, consider the location
155+
/// is at the start of replaced range. Otherwise, returns \p loc as is.
156+
static SourceLoc translateLocForReplacedRange(SourceManager &sourceMgr,
157+
SourceRange range,
158+
SourceLoc loc) {
159+
if (const auto &replacedRange = sourceMgr.getReplacedRange()) {
160+
if (sourceMgr.rangeContainsTokenLoc(replacedRange.New, loc) &&
161+
!sourceMgr.rangeContains(replacedRange.New, range)) {
162+
return replacedRange.Original.Start;
163+
}
164+
}
165+
return loc;
166+
}
167+
146168
NullablePtr<ASTScopeImpl>
147169
ASTScopeImpl::findChildContaining(SourceLoc loc,
148170
SourceManager &sourceMgr) const {
149171
// Use binary search to find the child that contains this location.
150-
struct CompareLocs {
151-
SourceManager &sourceMgr;
152-
153-
bool operator()(const ASTScopeImpl *scope, SourceLoc loc) {
154-
ASTScopeAssert(scope->checkSourceRangeOfThisASTNode(), "Bad range.");
155-
return -1 == ASTScopeImpl::compare(scope->getSourceRangeOfScope(), loc,
156-
sourceMgr,
157-
/*ensureDisjoint=*/false);
158-
}
159-
bool operator()(SourceLoc loc, const ASTScopeImpl *scope) {
160-
ASTScopeAssert(scope->checkSourceRangeOfThisASTNode(), "Bad range.");
161-
// Alternatively, we could check that loc < start-of-scope
162-
return 0 >= ASTScopeImpl::compare(loc, scope->getSourceRangeOfScope(),
163-
sourceMgr,
164-
/*ensureDisjoint=*/false);
165-
}
166-
};
167-
auto *const *child = std::lower_bound(
168-
getChildren().begin(), getChildren().end(), loc, CompareLocs{sourceMgr});
169-
170-
if (child != getChildren().end() &&
171-
sourceMgr.rangeContainsTokenLoc((*child)->getSourceRangeOfScope(), loc))
172-
return *child;
172+
auto *const *child = llvm::lower_bound(
173+
getChildren(), loc,
174+
[&sourceMgr](const ASTScopeImpl *scope, SourceLoc loc) {
175+
ASTScopeAssert(scope->checkSourceRangeOfThisASTNode(), "Bad range.");
176+
auto rangeOfScope = scope->getSourceRangeOfScope();
177+
loc = translateLocForReplacedRange(sourceMgr, rangeOfScope, loc);
178+
return -1 == ASTScopeImpl::compare(rangeOfScope, loc, sourceMgr,
179+
/*ensureDisjoint=*/false);
180+
});
181+
182+
if (child != getChildren().end()) {
183+
auto rangeOfScope = (*child)->getSourceRangeOfScope();
184+
loc = translateLocForReplacedRange(sourceMgr, rangeOfScope, loc);
185+
if (sourceMgr.rangeContainsTokenLoc(rangeOfScope, loc))
186+
return *child;
187+
}
173188

174189
return nullptr;
175190
}

lib/AST/ASTScopeSourceRange.cpp

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,14 @@ ASTScopeImpl::widenSourceRangeForChildren(const SourceRange range,
6767
if (range.isInvalid())
6868
return childRange;
6969
auto r = range;
70+
71+
// HACK: For code completion. If the range of the child is from another
72+
// source buffer, don't widen using that range.
73+
if (const auto &replacedRange = getSourceManager().getReplacedRange()) {
74+
if (getSourceManager().rangeContains(replacedRange.Original, range) &&
75+
getSourceManager().rangeContains(replacedRange.New, childRange))
76+
return r;
77+
}
7078
r.widen(childRange);
7179
return r;
7280
}
@@ -114,6 +122,14 @@ bool ASTScopeImpl::verifyThatChildrenAreContainedWithin(
114122
getChildren().back()->getSourceRangeOfScope().End);
115123
if (getSourceManager().rangeContains(range, rangeOfChildren))
116124
return true;
125+
126+
// HACK: For code completion. Handle replaced range.
127+
if (const auto &replacedRange = getSourceManager().getReplacedRange()) {
128+
if (getSourceManager().rangeContains(replacedRange.Original, range) &&
129+
getSourceManager().rangeContains(replacedRange.New, rangeOfChildren))
130+
return true;
131+
}
132+
117133
auto &out = verificationError() << "children not contained in its parent\n";
118134
if (getChildren().size() == 1) {
119135
out << "\n***Only Child node***\n";
@@ -200,7 +216,7 @@ SourceRange DifferentiableAttributeScope::getSourceRangeOfThisASTNode(
200216

201217
SourceRange AbstractFunctionBodyScope::getSourceRangeOfThisASTNode(
202218
const bool omitAssertions) const {
203-
return decl->getBodySourceRange();
219+
return decl->getOriginalBodySourceRange();
204220
}
205221

206222
SourceRange TopLevelCodeScope::getSourceRangeOfThisASTNode(
@@ -357,7 +373,7 @@ SourceRange AbstractFunctionDeclScope::getSourceRangeOfThisASTNode(
357373
ASTScopeAssert(r.End.isValid(), "Start valid imples end valid.");
358374
return r;
359375
}
360-
return decl->getBodySourceRange();
376+
return decl->getOriginalBodySourceRange();
361377
}
362378

363379
SourceRange ParameterListScope::getSourceRangeOfThisASTNode(
@@ -609,7 +625,7 @@ SourceRange IterableTypeScope::sourceRangeForDeferredExpansion() const {
609625
return portion->sourceRangeForDeferredExpansion(this);
610626
}
611627
SourceRange AbstractFunctionBodyScope::sourceRangeForDeferredExpansion() const {
612-
const auto bsr = decl->getBodySourceRange();
628+
const auto bsr = decl->getOriginalBodySourceRange();
613629
const SourceLoc endEvenIfNoCloseBraceAndEndsWithInterpolatedStringLiteral =
614630
getLocEncompassingPotentialLookups(getSourceManager(), bsr.End);
615631
return SourceRange(bsr.Start,

lib/AST/Module.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2387,7 +2387,7 @@ StringRef SourceFile::getFilename() const {
23872387

23882388
ASTScope &SourceFile::getScope() {
23892389
if (!Scope)
2390-
Scope = std::unique_ptr<ASTScope>(new (getASTContext()) ASTScope(this));
2390+
Scope = new (getASTContext()) ASTScope(this);
23912391
return *Scope.get();
23922392
}
23932393

lib/IDE/CompletionInstance.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,8 @@ bool CompletionInstance::performCachedOperationIfPossible(
417417

418418
auto *AFD = cast<AbstractFunctionDecl>(DC);
419419
AFD->setBodyToBeReparsed(newBodyRange);
420+
SM.setReplacedRange({AFD->getOriginalBodySourceRange(), newBodyRange});
421+
oldSF->clearScope();
420422

421423
traceDC = AFD;
422424
break;
@@ -602,9 +604,6 @@ bool swift::ide::CompletionInstance::performOperation(
602604
// We don't need token list.
603605
Invocation.getLangOptions().CollectParsedToken = false;
604606

605-
// FIXME: ASTScopeLookup doesn't support code completion yet.
606-
Invocation.disableASTScopeLookup();
607-
608607
if (EnableASTCaching) {
609608
// Compute the signature of the invocation.
610609
llvm::hash_code ArgsHash(0);

test/IDE/complete_attributes.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=TOP_LEVEL_ATTR_1 -code-completion-keywords=false | %FileCheck %s -check-prefix=ERROR_COMMON
22
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=MEMBER_DECL_ATTR_1 -code-completion-keywords=false | %FileCheck %s -check-prefix=ERROR_COMMON
3+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ATTRARG_MEMBER | %FileCheck %s -check-prefix=MEMBER_MyValue
4+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=ATTRARG_MEMBER_IN_CLOSURE | %FileCheck %s -check-prefix=MEMBER_MyValue
35

46
// ERROR_COMMON: found code completion token
57
// ERROR_COMMON-NOT: Keyword/
@@ -9,3 +11,22 @@
911
class MemberDeclAttribute {
1012
@#^MEMBER_DECL_ATTR_1^# func memberDeclAttr1() {}
1113
}
14+
15+
struct MyValue {
16+
init() {}
17+
static var val: Int
18+
}
19+
20+
// MEMBER_MyValue: Begin completions, 4 items
21+
// MEMBER_MyValue-DAG: Keyword[self]/CurrNominal: self[#MyValue.Type#];
22+
// MEMBER_MyValue-DAG: Keyword/CurrNominal: Type[#MyValue.Type#];
23+
// MEMBER_MyValue-DAG: Decl[Constructor]/CurrNominal: init()[#MyValue#];
24+
// MEMBER_MyValue-DAG: Decl[StaticVar]/CurrNominal: val[#Int#];
25+
// MEMBER_MyValue: End completions
26+
27+
class TestUknownDanglingAttr1 {
28+
@UknownAttr(arg: MyValue.#^ATTRARG_MEMBER^#)
29+
}
30+
class TestUknownDanglingAttr2 {
31+
@UknownAttr(arg: { MyValue.#^ATTRARG_MEMBER_IN_CLOSURE^# })
32+
}

test/decl/ext/extensions.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,3 +349,10 @@ struct SR_10466<T> {
349349
extension SR_10466 where T == Never { // expected-note {{requirement specified as 'T' == 'Never' [with T = T]}}
350350
typealias A = Int
351351
}
352+
353+
#if true
354+
protocol Rdar66943328 {
355+
associatedtype Assoc
356+
}
357+
extension Rdar66943328 where Assoc == Int // expected-error {{expected '{' in extension}}
358+
#endif

0 commit comments

Comments
 (0)