Skip to content

Conversation

armanio123
Copy link
Contributor

@armanio123 armanio123 commented Mar 31, 2021

Fixes #42527

@typescript-bot typescript-bot added the For Milestone Bug PRs that fix a bug with a specific milestone label Mar 31, 2021
@@ -916,7 +927,7 @@ namespace ts.textChanges {
const normalized = stableSort(changesInFile, (a, b) => (a.range.pos - b.range.pos) || (a.range.end - b.range.end));
// verify that change intervals do not overlap, except possibly at end points.
for (let i = 0; i < normalized.length - 1; i++) {
Debug.assert(normalized[i].range.end <= normalized[i + 1].range.pos, "Changes overlap", () =>
Debug.assert(normalized[i].range.end <= normalized[i + 1].range.pos || normalized[i].kind === ChangeKind.ReplaceWithList, "Changes overlap", () =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you paste the two JSON/debugger output of the two changes (normalized[i] and normalized[i + 1]) that trigger this assertion? I have some thoughts, but want to make sure I understand what’s happening correctly.

Copy link
Contributor Author

@armanio123 armanio123 Apr 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are pretty big nodes. But here it goes:

normalized[i] = {
  "kind": 4,
  "sourceFile": {},
  "range": {
    "pos": 15,
    "end": 16
  },
  "options": {
    "prefix": " ",
    "suffix": ", "
  },
  "node": {
    "pos": -1,
    "end": -1,
    "flags": 8,
    "modifierFlagsCache": 0,
    "transformFlags": 0,
    "kind": 266,
    "name": {
      "pos": -1,
      "end": -1,
      "flags": 8,
      "modifierFlagsCache": 0,
      "transformFlags": 0,
      "kind": 78,
      "escapedText": "Test2"
    }
  }
}
normalized[i + 1] = {
  "kind": 4,
  "sourceFile": {},
  "range": {
    "pos": 15,
    "end": 16
  },
  "options": {
    "prefix": " ",
    "suffix": ", "
  },
  "node": {
    "pos": -1,
    "end": -1,
    "flags": 8,
    "modifierFlagsCache": 0,
    "transformFlags": 0,
    "kind": 266,
    "name": {
      "pos": -1,
      "end": -1,
      "flags": 8,
      "modifierFlagsCache": 0,
      "transformFlags": 0,
      "kind": 78,
      "escapedText": "Test3"
    }
  }
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Edited ^ to remove the sourceFile node so we can look at this. The source file text was:

import { Test1, Test4 } from './file1';
interface Testing {
    test1: Test1;
    test2: Test2;
    test3: Test3;
    test4: Test4;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What’s happening is that both fixes are attempting to delete the space between Test1, and Test4 with their inserted node. They make up for the deletion of the space by including it in the prefix of their insertion. But it’s the deletion of the space that’s the alleged problem, because overlapping deletes might be problematic. It seems like the simplest fix would just be to ensure that this insertion is performed with an empty range, so it doesn’t try to delete anything.

@@ -916,7 +927,7 @@ namespace ts.textChanges {
const normalized = stableSort(changesInFile, (a, b) => (a.range.pos - b.range.pos) || (a.range.end - b.range.end));
// verify that change intervals do not overlap, except possibly at end points.
for (let i = 0; i < normalized.length - 1; i++) {
Debug.assert(normalized[i].range.end <= normalized[i + 1].range.pos, "Changes overlap", () =>
Debug.assert(normalized[i].range.end <= normalized[i + 1].range.pos || normalized[i].kind === ChangeKind.ReplaceWithList, "Changes overlap", () =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What’s happening is that both fixes are attempting to delete the space between Test1, and Test4 with their inserted node. They make up for the deletion of the space by including it in the prefix of their insertion. But it’s the deletion of the space that’s the alleged problem, because overlapping deletes might be problematic. It seems like the simplest fix would just be to ensure that this insertion is performed with an empty range, so it doesn’t try to delete anything.

@@ -769,7 +780,7 @@ namespace ts.textChanges {

// write separator and leading trivia of the next element as suffix
const suffix = `${tokenToString(nextToken.kind)}${sourceFile.text.substring(nextToken.end, containingList[index + 1].getStart(sourceFile))}`;
this.replaceRange(sourceFile, createRange(startPos, containingList[index + 1].getStart(sourceFile)), newNode, { prefix, suffix });
this.replaceRangeWithList(sourceFile, createRange(startPos, containingList[index + 1].getStart(sourceFile)), newNode, { prefix, suffix });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the problematic call, because the range is non-empty. It feels like you could just move startPos forward such that prefix is never needed, and create an empty range.

@armanio123 armanio123 force-pushed the FixAddAllMissingImports branch from fc47b78 to 1c66f9f Compare April 6, 2021 00:31
@armanio123
Copy link
Contributor Author

Done, the fix was actually super easy once you noticed that the problem is that is replacing the same text. Now instead of replacing it will insert in x position and uses the next node trivia for formatting.

@armanio123 armanio123 changed the title Added ChangeKind.ReplaceWithList Fix Add all missing imports when ordered alphabetically Apr 6, 2021
else {
// next element is located on different line that separator
// let insert position be the beginning of the line that contains next element
startPos = getStartPositionOfLine(lineAndCharOfNextElement.line, sourceFile);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a couple failing tests now; I’m guessing some of this was still important. If I had to guess, I would say that this else block needs to be preserved for insertions where the next element is on a different line.

Copy link
Contributor Author

@armanio123 armanio123 Apr 6, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really thought I had run the tests. Anyway is fixed now. We still don't need all of that we just needed to consider the trivia.

Copy link
Member

@andrewbranch andrewbranch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is awesome! Gotta love fixing bugs by deleting code, especially code so complicated it needed ASCII diagramming to explain it.

@armanio123 armanio123 merged commit 2f82d02 into microsoft:master Apr 7, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
For Milestone Bug PRs that fix a bug with a specific milestone
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Trying to add multiple alphabetically adjacent imports from the same module results in assertion fail
3 participants