Skip to content
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

Fixed List pattern is not correctly formatted in the first arm of a switch expression #72269

Open
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

victor-pogor
Copy link
Contributor

@victor-pogor victor-pogor commented Feb 25, 2024

Fixes #72196

UPD#1: converted to draft PR as other tests fail (CollectionExpressionWrappingTests), not sure the code change is the right one
UPD#2: It seems this bug was introduced by this change

I analyzed in comparison with the case if using property pattern. The code tries to get the indentation from the smallest containing interval, but since the alignment block operation does not calculate the spans correctly, it takes the indentation value from wrong (but indeed smallest) interval.

image

After code changes the same smallest interval is picked as we have in case the first switch arm is a property pattern:

image

ListPattern.mp4

@victor-pogor victor-pogor requested a review from a team as a code owner February 25, 2024 09:07
@dotnet-issue-labeler dotnet-issue-labeler bot added Area-IDE untriaged Issues and PRs which have not yet been triaged by a lead labels Feb 25, 2024
@victor-pogor victor-pogor marked this pull request as draft February 25, 2024 10:19
var lastToken = node.GetLastToken(includeZeroWidth: true);
var baseToken = firstToken.GetPreviousToken(includeZeroWidth: true);

SetAlignmentBlockOperation(list, baseToken, firstToken, lastToken, option);
Copy link
Contributor Author

@victor-pogor victor-pogor Feb 25, 2024

Choose a reason for hiding this comment

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

An attempt (reverting the code + changes state below) that fixes the failing CollectionExpressionWrappingTests

if (node.Parent is not SwitchExpressionArmSyntax)
{
    SetAlignmentBlockOperation(list, baseToken, firstToken, lastToken, option);
} else {
    AddAlignmentBlockOperationRelativeToFirstTokenOnBaseTokenLine(list, bracketPair);
}

But I have a feeling this is not the right way :)

Copy link
Member

Choose a reason for hiding this comment

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

this seems generally odd to me. i would expect taht indent block operations would be set for the switch expression, and its arms. Nothing to do with list expressions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Switch has indent block operations but not his arms. I think they don't need to have.

I think the problem is having wrong base & first tokens for list pattern

var result1 = new int[] { 1, 2 } switch // List pattern
{
    [1] => "one",
    _ => "unknown"
};

var result2 = new int[] { 1, 2 } switch // property pattern clause
{
    { Length: 0 } => "empty",
    [1] => "one",
    _ => "unknown"
};
First Switch expression arm Base Token Start Token End Token
Property pattern clause { (opening prop pattern) Length } (closing prop pattern)
List pattern expected [ (opening list pattern) 1 ] (closing list pattern)
List pattern actual { (opening switch expression) [ ] (closing list pattern)

@victor-pogor victor-pogor force-pushed the 72196-list-pattern-formatting-fix branch from cefea73 to b385a69 Compare February 26, 2024 10:01
@@ -257,7 +257,14 @@ private static void AddBracketIndentationOperation(List<IndentBlockOperation> li
if (!bracketPair.IsValidBracketOrBracePair())
return;

if (node.Parent != null && node.Kind() is SyntaxKind.ListPattern or SyntaxKind.CollectionExpression)
if (node.Parent != null && node.Kind() is SyntaxKind.ListPattern)
Copy link
Contributor Author

@victor-pogor victor-pogor Feb 27, 2024

Choose a reason for hiding this comment

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

@CyrusNajmabadi I think the safest way is to separate ListPattern from CollectionExpression and use the logic before this change.

As I know list pattern doesn't support wrapping and we don't need to handle the double indentation issue.

PS: please take a look also at the previous comment

@victor-pogor victor-pogor marked this pull request as ready for review February 28, 2024 12:18
@victor-pogor
Copy link
Contributor Author

@CyrusNajmabadi any feedback here? 🌝

@CyrusNajmabadi
Copy link
Member

Can look later. On vacation. :+)

@victor-pogor
Copy link
Contributor Author

@CyrusNajmabadi kind reminder :)

@@ -257,7 +257,14 @@ private static void AddBracketIndentationOperation(List<IndentBlockOperation> li
if (!bracketPair.IsValidBracketOrBracePair())
return;

if (node.Parent != null && node.Kind() is SyntaxKind.ListPattern or SyntaxKind.CollectionExpression)
if (node.Parent != null && node.Kind() is SyntaxKind.ListPattern)
Copy link
Member

Choose a reason for hiding this comment

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

💡 The IsKind method is more efficient than Kind

Suggested change
if (node.Parent != null && node.Kind() is SyntaxKind.ListPattern)
if (node.Parent != null && node.IsKind(SyntaxKind.ListPattern))

return;
}

if (node.Parent != null && node.Kind() is SyntaxKind.CollectionExpression)
{
// Brackets in list patterns are formatted like blocks, so align close bracket with open bracket
Copy link
Member

Choose a reason for hiding this comment

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

💡 This comment seems incorrect now

if (node.Parent != null && node.Kind() is SyntaxKind.ListPattern or SyntaxKind.CollectionExpression)
if (node.Parent != null && node.Kind() is SyntaxKind.ListPattern)
{
AddAlignmentBlockOperationRelativeToFirstTokenOnBaseTokenLine(list, bracketPair);
Copy link
Member

Choose a reason for hiding this comment

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

💭 What happens if we omit this line?

What happens if we remove the SetAlignmentBlockOperation call for CollectionExpression?

What happens if we change the call for CollectionExpression to use the same form we have for ListPattern?

}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72196")]
public async Task TestSwitchExpression_WithDiscardOperator_ShouldFormatListPattern()
Copy link
Member

Choose a reason for hiding this comment

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

📝 There is no list pattern here

Comment on lines +11010 to +11025
code: @"
var result3 = new int[] { 1, 2 } switch
{
{ Length: 0 } => ""empty"",
[1] => ""one"",
_ => ""unknown""
};
",
expected: @"
var result3 = new int[] { 1, 2 } switch
{
{ Length: 0 } => ""empty"",
[1] => ""one"",
_ => ""unknown""
};
");
Copy link
Member

Choose a reason for hiding this comment

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

If these are the same, can you extract a local variable for it?

Comment on lines +10988 to +11002
code: @"
var result3 = new int[] { 1, 2 } switch
{
{ Length: 0 } => ""empty"",
[1] => ""one"",
_ => ""unknown""
};
",
expected: @"
var result3 = new int[] { 1, 2 } switch
{
{ Length: 0 } => ""empty"",
[1] => ""one"",
_ => ""unknown""
};
Copy link
Member

Choose a reason for hiding this comment

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

If these are the same, can you extract a local variable for it?

@@ -10940,5 +10940,128 @@ public async Task TestNormalizeUsingAlias(string text, string expected)
{
await AssertFormatAsync(expected, text);
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72196")]
public async Task TestSwitchExpression_WithFormattedListPattern_ShouldNotFormatListPattern()
Copy link
Member

Choose a reason for hiding this comment

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

In theory, this test should not be necessary. Ideally each call to AssertFormatAsync should verify both of the following:

  1. Formatting code produces expected
  2. If code and expected are not the same, formatting expected also produces expected

Unfortunately, the AssertFormatAsync method is a bit convoluted to applying this change should be done in a separate pull request.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-IDE untriaged Issues and PRs which have not yet been triaged by a lead
Projects
None yet
Development

Successfully merging this pull request may close these issues.

List pattern is not correctly formatted in the first case of a switch expression
3 participants