Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,18 @@ private static void FixOne(SyntaxEditor editor, Diagnostic diagnostic, Cancellat
var parent = binaryExpression ?? (ExpressionSyntax?)isPatternExpression;
Contract.ThrowIfNull(parent);

var (notKeyword, initialPattern) = CreatePattern(binaryExpression, isPatternExpression);

// { X.Y: pattern }
var propertyPattern = PropertyPatternClause(
OpenBraceToken.WithoutTrivia().WithAppendedTrailingTrivia(Space),
[Subpattern(
CreateExpressionColon(conditionalAccessExpression),
CreatePattern(binaryExpression, isPatternExpression).WithTrailingTrivia(Space))],
initialPattern.WithTrailingTrivia(Space))],
CloseBraceToken.WithoutTrivia());

// T { X.Y: pattern }
var newPattern = RecursivePattern(
var recursivePattern = RecursivePattern(
(TypeSyntax)asExpression.Right.WithAppendedTrailingTrivia(Space),
positionalPatternClause: null,
propertyPattern,
Expand All @@ -80,7 +82,7 @@ private static void FixOne(SyntaxEditor editor, Diagnostic diagnostic, Cancellat
var newIsExpression = IsPatternExpression(
asExpression.Left,
IsKeyword.WithTriviaFrom(asExpression.OperatorToken),
newPattern);
notKeyword == default ? recursivePattern : UnaryPattern(notKeyword, recursivePattern));

var toReplace = parent.WalkUpParentheses();
editor.ReplaceNode(
Expand Down Expand Up @@ -112,15 +114,27 @@ static ExpressionSyntax RewriteMemberBindingToExpression(ExpressionSyntax expres
return expression;
}

static PatternSyntax CreatePattern(BinaryExpressionSyntax? binaryExpression, IsPatternExpressionSyntax? isPatternExpression)
static (SyntaxToken notKeyword, PatternSyntax pattern) CreatePattern(
BinaryExpressionSyntax? binaryExpression, IsPatternExpressionSyntax? isPatternExpression)
{
// if we had `.X.Y is some_pattern` we can just convert that to `X.Y: some_pattern`
if (isPatternExpression != null)
return isPatternExpression.Pattern;
{
// If this is a `not { .. var name ... }` pattern, then we need to life the 'not' outwards to ensure
// that 'var name' is still in scope when the pattern is checked. The lang only allows this for
// top-level 'not' pattern, not for an inner 'not' pattern.
if (isPatternExpression.Pattern is UnaryPatternSyntax(kind: SyntaxKind.NotPattern) unaryPattern &&
unaryPattern.DescendantNodes().OfType<DeclarationPatternSyntax>().Any())
{
return (unaryPattern.OperatorToken, unaryPattern.Pattern);
}

return (default, isPatternExpression.Pattern);
}

Contract.ThrowIfNull(binaryExpression);

return binaryExpression.Kind() switch
PatternSyntax pattern = binaryExpression.Kind() switch
{
// `.X.Y == expr` => `X.Y: expr`
SyntaxKind.EqualsExpression => ConstantPattern(binaryExpression.Right),
Expand All @@ -134,6 +148,8 @@ SyntaxKind.LessThanExpression or
SyntaxKind.LessThanOrEqualExpression => RelationalPattern(binaryExpression.OperatorToken, binaryExpression.Right),
_ => throw ExceptionUtilities.Unreachable()
};

return (default, pattern);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1155,4 +1155,77 @@ void M(object o)
LanguageVersion = LanguageVersion.CSharp9,
}.RunAsync();
}

[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/76372")]
public async Task TestPullNotUpwards()
{
await new VerifyCS.Test
{
TestCode = """
using System;

class D
{
void Goo(object metadata)
{
if (([|metadata as C|])?.P is not E { P: G.A, M: string text })
{
return;
}

Console.WriteLine(text);
}
}
class C
{
public object P { get; set; }
}

public class E
{
public G P { get; set; }

public object M { get; set; }
}

public enum G
{
A
}
""",
FixedCode = """
using System;

class D
{
void Goo(object metadata)
{
if (metadata is not C { P: E { P: G.A, M: string text } })
{
return;
}

Console.WriteLine(text);
}
}
class C
{
public object P { get; set; }
}

public class E
{
public G P { get; set; }

public object M { get; set; }
}

public enum G
{
A
}
""",
LanguageVersion = LanguageVersion.CSharp9,
}.RunAsync();
}
}