Skip to content

Commit d67bdb1

Browse files
Fix property pattern indentation in positional patterns
When a property pattern appears inside a positional (tuple) pattern, it was being incorrectly dedented. The issue was that the alignment operation used the SubpatternSyntax's first token as the base, which is the same as the property pattern's opening brace, causing the operation to be skipped. The fix navigates up to find the outer RecursivePattern's parent and uses that as the base, with an additional indentation delta of 1 to properly indent the property pattern relative to the tuple. Co-authored-by: CyrusNajmabadi <4564579+CyrusNajmabadi@users.noreply.github.com>
1 parent 53fabb5 commit d67bdb1

File tree

2 files changed

+100
-7
lines changed

2 files changed

+100
-7
lines changed

src/Workspaces/CSharpTest/Formatting/FormattingTests_Patterns.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,4 +600,58 @@ void M(string[][] ss)
600600
}
601601
}
602602
""");
603+
604+
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/54288")]
605+
public Task FormatPropertyPatternInPositionalPattern1()
606+
=> AssertFormatAsync("""
607+
class C
608+
{
609+
void M(object former, object latter)
610+
{
611+
if (
612+
(former, latter) is not (
613+
{
614+
Expression: IdentifierNameSyntax
615+
{
616+
Identifier: { ValueText: var formerIdentifier }
617+
}
618+
},
619+
{
620+
Expression: IdentifierNameSyntax
621+
{
622+
Identifier: { ValueText: var latterIdentifier }
623+
}
624+
}
625+
)
626+
)
627+
{
628+
}
629+
}
630+
}
631+
""", """
632+
class C
633+
{
634+
void M(object former, object latter)
635+
{
636+
if (
637+
(former, latter) is not (
638+
{
639+
Expression: IdentifierNameSyntax
640+
{
641+
Identifier: { ValueText: var formerIdentifier }
642+
}
643+
},
644+
{
645+
Expression: IdentifierNameSyntax
646+
{
647+
Identifier: { ValueText: var latterIdentifier }
648+
}
649+
}
650+
)
651+
)
652+
{
653+
}
654+
}
655+
}
656+
""");
603657
}

src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Formatting/Rules/IndentBlockFormattingRule.cs

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -184,17 +184,56 @@ private static void AddAlignmentBlockOperation(List<IndentBlockOperation> list,
184184
SetAlignmentBlockOperation(list, withExpression.GetFirstToken(), withExpression.Initializer.OpenBraceToken, withExpression.Initializer.CloseBraceToken, IndentBlockOption.RelativeToFirstTokenOnBaseTokenLine);
185185
return;
186186
case PropertyPatternClauseSyntax propertyPatternClause:
187-
if (propertyPatternClause.Parent is RecursivePatternSyntax { Parent: { } recursivePatternParent })
187+
if (propertyPatternClause.Parent is RecursivePatternSyntax recursivePattern)
188188
{
189-
var baseTokenForAlignment = recursivePatternParent.GetFirstToken();
190-
if (baseTokenForAlignment == propertyPatternClause.OpenBraceToken)
189+
SyntaxNode? baseNode = null;
190+
IndentBlockOption options = IndentBlockOption.RelativeToFirstTokenOnBaseTokenLine | IndentBlockOption.IndentIfConditionOfAnchorToken;
191+
int indentationDelta = 0;
192+
193+
// When the recursive pattern is part of a positional pattern (tuple pattern), we need to find
194+
// a base token that isn't the property pattern's own opening brace.
195+
if (recursivePattern.Parent is SubpatternSyntax subpattern)
196+
{
197+
// Walk up to find the PositionalPatternClause, then use its parent's parent as the base
198+
if (subpattern.Parent is PositionalPatternClauseSyntax positionalPattern &&
199+
positionalPattern.Parent is RecursivePatternSyntax outerPattern &&
200+
outerPattern.Parent != null)
201+
{
202+
baseNode = outerPattern.Parent;
203+
// For patterns inside positional patterns, add one level of indentation
204+
// relative to the first token on the base line
205+
options = IndentBlockOption.RelativeToFirstTokenOnBaseTokenLine;
206+
indentationDelta = 1;
207+
}
208+
}
209+
else if (recursivePattern.Parent != null)
191210
{
192-
// It only makes sense to set the alignment for the '{' when it's on a separate line from
193-
// the base token for alignment. This is never the case when they are the same token.
194-
return;
211+
baseNode = recursivePattern.Parent;
195212
}
196213

197-
SetAlignmentBlockOperation(list, baseTokenForAlignment, propertyPatternClause.OpenBraceToken, propertyPatternClause.CloseBraceToken, IndentBlockOption.RelativeToFirstTokenOnBaseTokenLine | IndentBlockOption.IndentIfConditionOfAnchorToken);
214+
if (baseNode != null)
215+
{
216+
var baseTokenForAlignment = baseNode.GetFirstToken();
217+
if (baseTokenForAlignment != propertyPatternClause.OpenBraceToken)
218+
{
219+
// It only makes sense to set the alignment for the '{' when it's on a separate line from
220+
// the base token for alignment.
221+
if (indentationDelta != 0)
222+
{
223+
// Create the operation directly with the custom indentation delta
224+
list.Add(FormattingOperations.CreateRelativeIndentBlockOperation(
225+
baseTokenForAlignment,
226+
propertyPatternClause.OpenBraceToken,
227+
propertyPatternClause.CloseBraceToken,
228+
indentationDelta,
229+
options));
230+
}
231+
else
232+
{
233+
SetAlignmentBlockOperation(list, baseTokenForAlignment, propertyPatternClause.OpenBraceToken, propertyPatternClause.CloseBraceToken, options);
234+
}
235+
}
236+
}
198237
}
199238

200239
return;

0 commit comments

Comments
 (0)