-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[incrParse] Fix bug mapping a node's location back to its location in the cached syntax tree #19782
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
Conversation
… the cached syntax tree Also fix Edit::intersectsOrTouchesRange check only returning true when the ranges overlapped, rather than when they overlapped or 'touched'. Resolves rdar://problem/45108439
@swift-ci please smoke test |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch! Does the stress tester test other mutation patterns than extending a token?
I'd like to take a closer look at this since I know that I had to switch around some |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A couple comments inline that fix some more underlying bugs that have just surfaced.
@@ -38,7 +38,7 @@ struct SourceEdit { | |||
/// Check if the characters replaced by this edit fall into the given range | |||
/// or are directly adjacent to it | |||
bool intersectsOrTouchesRange(size_t RangeStart, size_t RangeEnd) { | |||
return !(End <= RangeStart || Start >= RangeEnd); | |||
return End >= RangeStart && Start <= RangeEnd; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch!
lib/Parse/SyntaxParsingCache.cpp
Outdated
@@ -85,7 +85,7 @@ llvm::Optional<Syntax> SyntaxParsingCache::lookUp(size_t NewPosition, | |||
size_t OldPosition = NewPosition; | |||
for (auto I = Edits.rbegin(), E = Edits.rend(); I != E; ++I) { | |||
auto Edit = *I; | |||
if (Edit.End <= OldPosition) { | |||
if (Edit.End < OldPosition) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After some more consideration this should be
if (Edit.End + Edit.ReplacementLength - Edit.originalLength() <= OldPosition) {
Explanation
With <=
we don't catch the following case:
Original text: foobar
Change foo
to fo
resulting in fobar
The edit range is 1-4 with original length 3 and replacement length 2.
If I now try to look up bar
, we start with OldPosition = NewPosition = 3
. Now we need to see if that position needs to be adjusted because of the edit (which it obviously needs to be), but we get Edit.End (4) <= OldPosition (3)
does not hold.
The issue is that we are comparing OldPosition
which is still in the position space of the post-edit file and Edit.End
which is in the position space of the pre-edit file, hence we need to translate Edit.End
to the post-position space as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, you're right! Ok, so this should make it correct for a single edit, but if there are multiple I think we'll need to account for the original-vs-replacement length differences of any edits earlier in the file too. I'll give that a go and update shortly.
// RUN: %incr-transfer-tree --expected-incremental-syntax-tree %S/Outputs/extend-identifier-at-eof.json %s | ||
|
||
func foo() {} | ||
_ = x<<<|||x>>> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would add comment to this file explaining that no trailing newline should be added to the end of it.
test/incrParse/simple.swift
Outdated
_ = <<REPLACE<6|||7>>></reparse REPLACE> | ||
_ = <<REPLACE_BY_LONGER<6|||"Hello World">>> | ||
_ = <<REPLACE<6|||7>>> | ||
_ = </reparse REPLACE><<REPLACE_BY_LONGER<6|||"Hello World">>> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Putting the </reparse REPLACE>
before the 6
seemed really odd to me and it turns out there is another off-by-one error here, where we don't report the last unexpectedly reparsed character.
Investigating further, I noticed that we need to remove the - 1
in swift-syntax-test.cpp:252
plus its corresponding comment. I don't know if that's related to some of your changes on SourceLoc
or has been a bug ever since, but it fixed the issue. After that the end repairs marker also needs to go at the end of the line.
That should be taken care of by traversing the edits in reverse, which we are doing. Unless I’m completely mistaking.
… On 10. Oct 2018, at 20:05, Nathan Hawes ***@***.***> wrote:
@nathawes commented on this pull request.
In lib/Parse/SyntaxParsingCache.cpp:
> @@ -85,7 +85,7 @@ llvm::Optional<Syntax> SyntaxParsingCache::lookUp(size_t NewPosition,
size_t OldPosition = NewPosition;
for (auto I = Edits.rbegin(), E = Edits.rend(); I != E; ++I) {
auto Edit = *I;
- if (Edit.End <= OldPosition) {
+ if (Edit.End < OldPosition) {
Ah, you're right! Ok, so this should make it correct for a single edit, but if there are multiple I think we'll need to account for the original-vs-replacement length differences of any edits earlier in the file too. I'll give that a go and update shortly.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.
|
Ok, I might be misunderstanding how multiple edits are meant to be understood. If I we have two edits in source order is the second edit positioned:
I was thinking it was (1), where I think doing it in reverse wouldn't help, but if it's (2) you're right. |
All changes are in terms of the original file. But because we are now speaking in terms of the post-edit position space, we need to traverse the edits in normal order again (not reverse order) to get the correct transformation. I extracted the logic, wrote a couple test cases for it and pushed it into this PR. While I was at it, I also addressed my other review comments. Sorry for pushing into your branch, but it felt like this is cleaner than creating a new PR on which you would then need to rebase. |
@swift-ci Please test |
Build failed |
Build failed |
No problem about pushing into the PR and thanks for adding the unit tests! The transformation looks good to me now, too. I came up with a different solution yesterday:
But they're equivalent, anyway (this one just applies the previous edits and current edit on the left hand side of the The test failure is just the expected syntax tree json needing to be updated for the 'don't add a new line' comment Alex added in one of the tests. I'll update it shortly and merge if all goes well. |
…ranges on the same line It wasn't accounting for the prefix length before a reparse tag previously when keeping track of pre_column_offset and post_column_offset.
…an SourceRange The only client of the 'toSourceRange' method immediately constructs a CharSourceRange from it, and ByteBasedSourceRange's EndLoc isn't the start of the last token in the range (as SourceRange expects).
@swift-ci please test |
Build failed |
Build failed |
@swift-ci please test |
Build failed |
Build failed |
After swiftlang/swift#19782 is merged, statements adjacent to an edit will no longer be reused. Hence the REPLACE_BY_LONGER line is expected to be reparsed.
We need to adjust a test case in Swift Syntax to expect the extended expected reparse range. Please test with following pull request: @swift-ci Please test |
Build failed |
Linux test failure seems unrelated. |
Please test with following pull request: @swift-ci Please smoke test Linux |
Also fix
Edit::intersectsOrTouchesRange
check only returning true when the ranges overlapped, rather than when they overlapped or touched. This was causing an edit that extended an existing identifier, for example, to reuse the existing node, and parse the remaining characters as a separate node in the tree.Resolves rdar://problem/45108439