-
-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Fix edge cases triggered by newlines in arrow functions #1217
Conversation
Yeah, this is a good solution. I had deferred thinking about this and never got around to it. It was a known issue, and it's hard to know if we can traverse all the conditions or not. But here this should work, because as you said it doesn't propagate them across the conditional group anyway, so it doesn't affect anything outside the group.
So
I don't see that change in the PR?
What do you mean by conditions? |
src/doc-utils.js
Outdated
} else if (doc.type === "group" && doc.expandedStates) { | ||
if (shouldTraverseConditionalGroups) { | ||
doc.expandedStates.forEach(traverseDocRec); | ||
} |
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 don't call traverseDocRec(doc.contents);
if we're not in shouldTraverseConditionalGroups
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.
But it traverses into contents
in a later branch which is the first group of a conditional group
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.
Just to clarify, as my above comment is wrong, I see that it will enter only this branch if it's a conditional group. But expandedStates
are all of the conditions, including the first one. contents
happens to point to the first one for convenience.
Here are the only three callsites of getFirstString: function isCurlyBracket(doc) {
const str = getFirstString(doc);
return str === "{" || str === "{}";
}
function isEmptyBlock(doc) {
const str = getFirstString(doc);
return str === "{}";
}
case "BlockStatement": {
var naked = path.call(function(bodyPath) {
return printStatementSequence(bodyPath, options, print);
}, "body");
const hasContent = getFirstString(naked); Do you know why those are reading the docs instead of checking the AST? |
I think it's just more straight-forward. But if there aren't enough uses of it we can try that. |
I removed |
Turns out, removing the last callsite of So, we can kill |
All the expandedStates. Basically, not adding a flag and always iterating through all the expandedStates. |
With your other PRs merged, are you going to get rid of |
I just got rid of getFirstString. The other two calls of https://gist.github.com/vjeux/5fb7566cc3d65974817d512d1ef6abe1 |
One thing I can do is to leave the behavior of going through the first one. This way it's a less risky change. |
This one is pretty crazy. In prettier#927, I changed ```js concat(["(", join(concat([", "]), printed), ")"]), ``` into ```js concat(["(", join(concat([", "]), printedLastArgExpanded), ")"]), ``` which makes the example in prettier#1203 look ugly. The crazy thing is that `JSON.stringify(printed) === JSON.stringify(printedLastArgExpanded)`. So they are deep equal but not the same object. In a non-mutative world, this should cause any problem, but we actually mutate those to propagate breaks. In the break propagation, we only looked at the first one in case of a conditional group. But, because the two were the same object then it also applied to the second one and happened to be the correct behavior! When I changed that piece of code to be two distinct objects, it no longer worked by accident and caused a bunch of weird issues where breaks didn't propagate correctly. The solution for this case is to propagate the breaks on all the conditions. I'm pretty sure that this is the expected behavior, we want to deeply recurse in all of them, we don't propagate it up the boundary anyway. The other use case for `traverseInDoc()` is `findInDoc()`, right now it searches for the first conditional group but it seems very arbitrary. I changed it to not search on any and none of the tests are failing, so I think it's safe(tm). If it triggers weird behavior, then it'll at least be expected and not randomly explode at us if we move groups around. I tried to go through all the conditions for `findInDoc()` but it triggers a few failures (the output look bad). I'm not really sure why. https://gist.github.com/vjeux/5fb7566cc3d65974817d512d1ef6abe1 Fix prettier#1203
This is the second part of the fix for the performance regression seen in prettier#1250. In prettier#1217, for correctness reasons, we're now traversing all the conditional groups. This means that we're now in O(n^2). But, in practice, many of those groups are === between each others. So we only need to recurse through one of the instances to know if it's going to break. This makes the first example go from not terminating to being instant. The second one going from not terminating to taking ~1s. We can also make it instant by tweaking the printing phase, but that's for another PR.
This is the second part of the fix for the performance regression seen in prettier#1250. In prettier#1217, for correctness reasons, we're now traversing all the conditional groups. This means that we're now in O(n^2). But, in practice, many of those groups are === between each others. So we only need to recurse through one of the instances to know if it's going to break. This makes the first example go from not terminating to being instant. The second one going from not terminating to taking ~1s. We can also make it instant by tweaking the printing phase, but that's for another PR.
This is the second part of the fix for the performance regression seen in #1250. In #1217, for correctness reasons, we're now traversing all the conditional groups. This means that we're now in O(n^2). But, in practice, many of those groups are === between each others. So we only need to recurse through one of the instances to know if it's going to break. This makes the first example go from not terminating to being instant. The second one going from not terminating to taking ~1s. We can also make it instant by tweaking the printing phase, but that's for another PR.
This one is pretty crazy. In #927, I changed
into
which makes the example in #1203 look ugly. The crazy thing is that
JSON.stringify(printed) === JSON.stringify(printedLastArgExpanded)
. So they are deep equal but not the same object. In a non-mutative world, this should cause any problem, but we actually mutate those to propagate breaks.In the break propagation, we only looked at the first one in case of a conditional group. But, because the two were the same object then it also applied to the second one and happened to be the correct behavior! When I changed that piece of code to be two distinct objects, it no longer worked by accident and caused a bunch of weird issues where breaks didn't propagate correctly.
The solution for this case is to propagate the breaks on all the conditions. I'm pretty sure that this is the expected behavior, we want to deeply recurse in all of them, we don't propagate it up the boundary anyway.
The other use case for
traverseInDoc()
isfindInDoc()
, right now it searches for the first conditional group but it seems very arbitrary. I changed it to not search on any and none of the tests are failing, so I think it's safe(tm). If it triggers weird behavior, then it'll at least be expected and not randomly explode at us if we move groups around.I tried to go through all the conditions for
findInDoc()
but it triggers a few failures (the output look bad). I'm not really sure why. https://gist.github.com/vjeux/5fb7566cc3d65974817d512d1ef6abe1Fix #1203