Skip to content

Commit 589b919

Browse files
authored
Merge pull request #33063 from benlangmuir/racing-edit
[sourcekitd] Fix range shifting vs semantic update timing issues
2 parents 771083a + 8e352b8 commit 589b919

File tree

3 files changed

+59
-13
lines changed

3 files changed

+59
-13
lines changed

tools/SourceKit/lib/Support/ImmutableTextBuffer.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,19 @@ bool ImmutableTextSnapshot::foreachReplaceUntil(
107107

108108
assert(EndSnapshot);
109109
ImmutableTextUpdateRef Upd = DiffEnd;
110+
111+
// Since `getBufferForSnapshot` may produce a snapshot with edits consolidated
112+
// into a buffer, we need to check the stamp explicitly for "equal" snapshots.
113+
// Otherwise, the following code, which should be a no-op, will behave
114+
// incorrectly:
115+
//
116+
// auto snap1 = editableBuffer->getSnapshot();
117+
// auto buffer = editableBuffer->getBuffer();
118+
// auto snap2 = editableBuffer->getSnapshot();
119+
// snap2->forEachReplaceUntil(snap1, ...); // should be no-op
120+
if (getStamp() == EndSnapshot->getStamp())
121+
return true;
122+
110123
while (Upd != EndSnapshot->DiffEnd) {
111124
Upd = Upd->getNext();
112125
if (!Upd) {

tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1350,9 +1350,24 @@ static void getSemanticInfoImpl(sourcekitd_variant_t Info) {
13501350
sourcekitd_variant_dictionary_get_value(Info, KeyDiagnostics);
13511351
}
13521352

1353-
static void getSemanticInfo(sourcekitd_variant_t Info, StringRef Filename) {
1354-
getSemanticInfoImpl(Info);
1353+
static void getSemanticInfoImplAfterDocUpdate(sourcekitd_variant_t EditOrOpen,
1354+
sourcekitd_variant_t DocUpdate) {
1355+
if (sourcekitd_variant_dictionary_get_uid(EditOrOpen, KeyDiagnosticStage) ==
1356+
SemaDiagnosticStage) {
1357+
// FIXME: currently we only return annotations once, so if the original edit
1358+
// or open request was slow enough, it may "take" the annotations. If that
1359+
// is fixed, we can skip checking the diagnostic stage and always use the
1360+
// DocUpdate variant.
1361+
assert(sourcekitd_variant_get_type(sourcekitd_variant_dictionary_get_value(
1362+
DocUpdate, KeyAnnotations)) == SOURCEKITD_VARIANT_TYPE_NULL);
1363+
1364+
getSemanticInfoImpl(EditOrOpen);
1365+
} else {
1366+
getSemanticInfoImpl(DocUpdate);
1367+
}
1368+
}
13551369

1370+
static void getSemanticInfo(sourcekitd_variant_t Info, StringRef Filename) {
13561371
// Wait for the notification that semantic info is available.
13571372
// But only for 1 min.
13581373
bool expired = semaSemaphore.wait(60 * 1000);
@@ -1364,7 +1379,9 @@ static void getSemanticInfo(sourcekitd_variant_t Info, StringRef Filename) {
13641379
llvm::report_fatal_error(
13651380
llvm::Twine("Got notification for different doc name: ") + semaName);
13661381
}
1367-
getSemanticInfoImpl(sourcekitd_response_get_value(semaResponse));
1382+
1383+
getSemanticInfoImplAfterDocUpdate(
1384+
Info, sourcekitd_response_get_value(semaResponse));
13681385
}
13691386

13701387
static int printAnnotations() {

unittests/SourceKit/SwiftLang/EditingTest.cpp

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -195,11 +195,14 @@ class EditTest : public ::testing::Test {
195195
void doubleOpenWithDelay(std::chrono::microseconds delay, bool close);
196196

197197
void setupThreeAnnotations(const char *DocName, TestConsumer &Consumer) {
198+
// The following is engineered so that the references to `mem` are at
199+
// offsets 60, 70, and 80 for convenience. They're on the same line so
200+
// that tests do not accidentally depend on line separation.
198201
const char *Contents =
199202
"struct S {\n"
200203
" var mem: Int = 0\n"
201204
" func test() {\n"
202-
" _ = (self.mem, self.mem, self.mem)\n"
205+
" _ = mem; _ = mem; _ = mem\n"
203206
" }\n"
204207
"}\n";
205208
const char *Args[] = { "-parse-as-library" };
@@ -385,8 +388,13 @@ TEST_F(EditTest, AnnotationsAfterOpen) {
385388
TestConsumer Consumer;
386389
open(DocName, Contents, Args, Consumer);
387390
ASSERT_FALSE(waitForDocUpdate()) << "timed out";
388-
reset(Consumer);
389-
replaceText(DocName, 0, 0, "", Consumer);
391+
if (Consumer.DiagStage == SemaDiagStage) {
392+
// AST already built, annotations are on Consumer.
393+
} else {
394+
// Re-query.
395+
reset(Consumer);
396+
replaceText(DocName, 0, 0, "", Consumer);
397+
}
390398

391399
ASSERT_EQ(0u, Consumer.Diags.size());
392400
checkTokens(Consumer.Annotations, {
@@ -416,8 +424,13 @@ TEST_F(EditTest, AnnotationsAfterEdit) {
416424
TestConsumer Consumer;
417425
open(DocName, Contents, Args, Consumer);
418426
ASSERT_FALSE(waitForDocUpdate()) << "timed out";
419-
reset(Consumer);
420-
replaceText(DocName, 0, 0, "", Consumer);
427+
if (Consumer.DiagStage == SemaDiagStage) {
428+
// AST already built, annotations are on Consumer.
429+
} else {
430+
// Re-query.
431+
reset(Consumer);
432+
replaceText(DocName, 0, 0, "", Consumer);
433+
}
421434
checkTokens(Consumer.Annotations, {
422435
"[off=22 len=3 source.lang.swift.ref.struct system]",
423436
});
@@ -426,8 +439,14 @@ TEST_F(EditTest, AnnotationsAfterEdit) {
426439
replaceText(DocName, 61, 0, "m", Consumer);
427440
ASSERT_FALSE(waitForDocUpdate()) << "timed out";
428441

429-
reset(Consumer);
430-
replaceText(DocName, 0, 0, "", Consumer);
442+
if (Consumer.DiagStage == SemaDiagStage) {
443+
// AST already built, annotations are on Consumer.
444+
} else {
445+
// Re-query.
446+
reset(Consumer);
447+
replaceText(DocName, 0, 0, "", Consumer);
448+
}
449+
431450
ASSERT_EQ(0u, Consumer.Diags.size());
432451
checkTokens(Consumer.Annotations, {
433452
"[off=22 len=3 source.lang.swift.ref.struct system]",
@@ -550,8 +569,6 @@ TEST_F(EditTest, AnnotationsRangeShiftingAfterEditInsertEnd) {
550569
ASSERT_FALSE(waitForDocUpdate()) << "timed out";
551570
close(DocName);
552571
}
553-
// rdar://65934938 Failing in CI with ASan
554-
#if defined(__has_feature) && !__has_feature(address_sanitizer)
555572
TEST_F(EditTest, AnnotationsRangeShiftingAfterEditReplaceEnd) {
556573
const char *DocName = "test.swift";
557574
TestConsumer Consumer;
@@ -570,7 +587,6 @@ TEST_F(EditTest, AnnotationsRangeShiftingAfterEditReplaceEnd) {
570587
ASSERT_FALSE(waitForDocUpdate()) << "timed out";
571588
close(DocName);
572589
}
573-
#endif
574590
TEST_F(EditTest, AnnotationsRangeShiftingAfterEditDeleteEnd) {
575591
const char *DocName = "test.swift";
576592
TestConsumer Consumer;

0 commit comments

Comments
 (0)