Skip to content

Commit ee0d008

Browse files
committed
Parse: Preserve source order when code completion adds delayed declarations
1 parent 0c11fad commit ee0d008

File tree

3 files changed

+110
-25
lines changed

3 files changed

+110
-25
lines changed

include/swift/AST/DeclContext.h

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -782,9 +782,20 @@ class IterableDeclContext {
782782
/// abstractions on top of member loading, such as a name lookup table.
783783
DeclRange getCurrentMembersWithoutLoading() const;
784784

785-
/// Add a member to this context. If the hint decl is specified, the new decl
786-
/// is inserted immediately after the hint.
787-
void addMember(Decl *member, Decl *hint = nullptr);
785+
/// Add a member to this context.
786+
///
787+
/// If the hint decl is specified, the new decl is inserted immediately
788+
/// after the hint.
789+
///
790+
/// If insertAtHead is set, the new decl is inserted at the beginning of
791+
/// the list.
792+
///
793+
/// Otherwise, it is inserted at the end.
794+
void addMember(Decl *member, Decl *hint = nullptr, bool insertAtHead = false);
795+
796+
/// Add a member in the right place to preserve source order. This should
797+
/// only be called from the code completion delayed parsing path.
798+
void addMemberPreservingSourceOrder(Decl *member);
788799

789800
/// Check whether there are lazily-loaded members.
790801
bool hasLazyMembers() const {
@@ -862,10 +873,7 @@ class IterableDeclContext {
862873
private:
863874
/// Add a member to the list for iteration purposes, but do not notify the
864875
/// subclass that we have done so.
865-
///
866-
/// This is used internally when loading members, because loading a
867-
/// member is an invisible addition.
868-
void addMemberSilently(Decl *member, Decl *hint = nullptr) const;
876+
void addMemberSilently(Decl *member, Decl *hint, bool insertAtHead) const;
869877
};
870878

871879
/// Define simple_display for DeclContexts but not for subclasses in order to

lib/AST/DeclContext.cpp

Lines changed: 93 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -764,10 +764,33 @@ ArrayRef<Decl *> IterableDeclContext::getSemanticMembers() const {
764764
ArrayRef<Decl *>());
765765
}
766766

767+
void IterableDeclContext::addMemberPreservingSourceOrder(Decl *member) {
768+
auto &SM = getASTContext().SourceMgr;
769+
770+
SourceLoc start = member->getStartLoc();
771+
Decl *hint = nullptr;
772+
773+
for (auto *existingMember : getMembers()) {
774+
if (existingMember->isImplicit())
775+
continue;
776+
777+
if (isa<EnumCaseDecl>(existingMember) ||
778+
isa<IfConfigDecl>(existingMember))
779+
continue;
780+
781+
if (!SM.isBeforeInBuffer(existingMember->getEndLoc(), start))
782+
break;
783+
784+
hint = existingMember;
785+
}
786+
787+
addMember(member, hint, /*insertAtHead=*/hint == nullptr);
788+
}
789+
767790
/// Add a member to this context.
768-
void IterableDeclContext::addMember(Decl *member, Decl *Hint) {
791+
void IterableDeclContext::addMember(Decl *member, Decl *hint, bool insertAtHead) {
769792
// Add the member to the list of declarations without notification.
770-
addMemberSilently(member, Hint);
793+
addMemberSilently(member, hint, insertAtHead);
771794

772795
// Notify our parent declaration that we have added the member, which can
773796
// be used to update the lookup tables.
@@ -790,29 +813,83 @@ void IterableDeclContext::addMember(Decl *member, Decl *Hint) {
790813
}
791814
}
792815

793-
void IterableDeclContext::addMemberSilently(Decl *member, Decl *hint) const {
816+
void IterableDeclContext::addMemberSilently(Decl *member, Decl *hint,
817+
bool insertAtHead) const {
794818
assert(!isa<AccessorDecl>(member) && "Accessors should not be added here");
795819
assert(!member->NextDecl && "Already added to a container");
796820

797-
// If there is a hint decl that specifies where to add this, just
798-
// link into the chain immediately following it.
799-
if (hint) {
821+
#ifndef NDEBUG
822+
auto checkSourceRange = [&](Decl *prev, Decl *next) {
823+
if (!member->getDeclContext()->getParentSourceFile())
824+
return;
825+
826+
auto shouldSkip = [](Decl *d) {
827+
if (isa<VarDecl>(d) || isa<EnumElementDecl>(d) || isa<IfConfigDecl>(d))
828+
return true;
829+
830+
if (d->isImplicit())
831+
return true;
832+
833+
return false;
834+
};
835+
836+
if (shouldSkip(prev) || shouldSkip(next))
837+
return;
838+
839+
SourceLoc prevEnd = prev->getEndLoc();
840+
SourceLoc nextStart = next->getStartLoc();
841+
842+
if (!prevEnd.isValid() || !nextStart.isValid())
843+
return;
844+
845+
if (getASTContext().SourceMgr.isBeforeInBuffer(prevEnd, nextStart))
846+
return;
847+
848+
llvm::errs() << "Source ranges out of order in addMember():\n";
849+
prev->dump(llvm::errs());
850+
next->dump(llvm::errs());
851+
abort();
852+
};
853+
#endif
854+
855+
// Empty list.
856+
if (!FirstDeclAndLazyMembers.getPointer()) {
857+
assert(hint == nullptr);
858+
859+
FirstDeclAndLazyMembers.setPointer(member);
860+
LastDeclAndKind.setPointer(member);
861+
862+
// Insertion at the head.
863+
} else if (insertAtHead) {
864+
assert(hint == nullptr);
865+
866+
member->NextDecl = FirstDeclAndLazyMembers.getPointer();
867+
FirstDeclAndLazyMembers.setPointer(member);
868+
869+
// Insertion at the tail.
870+
} else if (hint == nullptr) {
871+
auto *last = LastDeclAndKind.getPointer();
872+
873+
#ifndef NDEBUG
874+
checkSourceRange(last, member);
875+
#endif
876+
877+
last->NextDecl = member;
878+
LastDeclAndKind.setPointer(member);
879+
880+
// Insertion after 'hint' (which may be the tail).
881+
} else {
882+
#ifndef NDEBUG
883+
checkSourceRange(hint, member);
884+
#endif
885+
800886
member->NextDecl = hint->NextDecl;
801887
hint->NextDecl = member;
802888

803-
// If the hint was the last in the parent context's chain, update it.
889+
// Handle case where the 'hint' is the tail.
804890
if (LastDeclAndKind.getPointer() == hint)
805891
LastDeclAndKind.setPointer(member);
806-
return;
807-
}
808-
809-
if (auto last = LastDeclAndKind.getPointer()) {
810-
last->NextDecl = member;
811-
assert(last != member && "Simple cycle in decl list");
812-
} else {
813-
FirstDeclAndLazyMembers.setPointer(member);
814892
}
815-
LastDeclAndKind.setPointer(member);
816893
}
817894

818895
void IterableDeclContext::setMemberLoader(LazyMemberLoader *loader,

lib/Parse/Parser.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,9 +189,9 @@ void Parser::performCodeCompletionSecondPassImpl(
189189
parseDecl(ParseDeclOptions(info.Flags),
190190
/*IsAtStartOfLineOrPreviousHadSemi=*/true, [&](Decl *D) {
191191
if (auto *NTD = dyn_cast<NominalTypeDecl>(DC)) {
192-
NTD->addMember(D);
192+
NTD->addMemberPreservingSourceOrder(D);
193193
} else if (auto *ED = dyn_cast<ExtensionDecl>(DC)) {
194-
ED->addMember(D);
194+
ED->addMemberPreservingSourceOrder(D);
195195
} else if (auto *SF = dyn_cast<SourceFile>(DC)) {
196196
SF->addTopLevelDecl(D);
197197
} else {

0 commit comments

Comments
 (0)