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

List patterns: IOperation #56008

Merged
merged 12 commits into from
Oct 28, 2021
Merged
Show file tree
Hide file tree
Changes from 8 commits
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 @@ -2269,8 +2269,9 @@ private IOperation CreateBoundTypePatternOperation(BoundTypePattern boundTypePat

private IOperation CreateBoundSlicePatternOperation(BoundSlicePattern boundNode)
{
// PROTOTYPE(list-patterns) IOperation
return new DiscardPatternOperation(
return new SlicePatternOperation(
sliceSymbol: ((Symbol?)boundNode.SliceMethod ?? boundNode.IndexerAccess?.Indexer).GetPublicSymbol(),
pattern: (IPatternOperation?)Create(boundNode.Pattern),
inputType: boundNode.InputType.GetPublicSymbol(),
narrowedType: boundNode.NarrowedType.GetPublicSymbol(),
_semanticModel,
Expand All @@ -2280,8 +2281,11 @@ private IOperation CreateBoundSlicePatternOperation(BoundSlicePattern boundNode)

private IOperation CreateBoundListPatternOperation(BoundListPattern boundNode)
{
// PROTOTYPE(list-patterns) IOperation
return new DiscardPatternOperation(
return new ListPatternOperation(
lengthSymbol: boundNode.LengthProperty.GetPublicSymbol(),
indexerSymbol: (boundNode.IndexerSymbol ?? boundNode.IndexerAccess?.Indexer).GetPublicSymbol(),
patterns: boundNode.Subpatterns.SelectAsArray((p, fac) => (IPatternOperation)fac.Create(p), this),
declaredSymbol: boundNode.Variable.GetPublicSymbol(),
inputType: boundNode.InputType.GetPublicSymbol(),
narrowedType: boundNode.NarrowedType.GetPublicSymbol(),
_semanticModel,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2084,5 +2084,299 @@ void M(object o)

VerifyOperationTreeAndDiagnosticsForTest<IsPatternExpressionSyntax>(source, expectedOperationTree, expectedDiagnostics, parseOptions: TestOptions.RegularWithPatternCombinators);
}

[CompilerTrait(CompilerFeature.IOperation)]
[Fact]
public void TestIsPatternExpression_ListPatterns_Array_01()
{
string source = @"
class X
{
void M(int[] o)
{
_ = /*<bind>*/o is [42, ..]/*</bind>*/;
}
}
";
string expectedOperationTree = @"
IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean) (Syntax: 'o is [42, ..]')
Value:
IParameterReferenceOperation: o (OperationKind.ParameterReference, Type: System.Int32[]) (Syntax: 'o')
Pattern:
IListPatternOperation (OperationKind.ListPattern, Type: null) (Syntax: '[42, ..]') (InputType: System.Int32[], NarrowedType: System.Int32[], DeclaredSymbol: null, LengthSymbol: System.Int32 System.Array.Length { get; }, IndexerSymbol: null)
333fred marked this conversation as resolved.
Show resolved Hide resolved
Patterns (2):
IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: '42') (InputType: System.Int32, NarrowedType: System.Int32)
Value:
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 42) (Syntax: '42')
ISlicePatternOperation (OperationKind.SlicePattern, Type: null) (Syntax: '..'), SliceSymbol: null
Pattern:
null
";
var expectedDiagnostics = DiagnosticDescription.None;

var comp = CreateCompilation(source);
VerifyOperationTreeAndDiagnosticsForTest<IsPatternExpressionSyntax>(comp, expectedOperationTree, expectedDiagnostics);
}

[CompilerTrait(CompilerFeature.IOperation)]
[Fact]
public void TestIsPatternExpression_ListPatterns_Array_02()
{
string source = @"
class X
{
void M(int[] o)
{
_ = /*<bind>*/o is [42, .. var slice] list/*</bind>*/;
}
}
";
string expectedOperationTree = @"
IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean) (Syntax: 'o is [42, . ... slice] list')
Value:
IParameterReferenceOperation: o (OperationKind.ParameterReference, Type: System.Int32[]) (Syntax: 'o')
Pattern:
IListPatternOperation (OperationKind.ListPattern, Type: null) (Syntax: '[42, .. var slice] list') (InputType: System.Int32[], NarrowedType: System.Int32[], DeclaredSymbol: System.Int32[] list, LengthSymbol: System.Int32 System.Array.Length { get; }, IndexerSymbol: null)
Patterns (2):
IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: '42') (InputType: System.Int32, NarrowedType: System.Int32)
Value:
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 42) (Syntax: '42')
ISlicePatternOperation (OperationKind.SlicePattern, Type: null) (Syntax: '.. var slice'), SliceSymbol: null
333fred marked this conversation as resolved.
Show resolved Hide resolved
Pattern:
IDeclarationPatternOperation (OperationKind.DeclarationPattern, Type: null) (Syntax: 'var slice') (InputType: System.Int32[], NarrowedType: System.Int32[], DeclaredSymbol: System.Int32[]? slice, MatchesNull: True)
";
var expectedDiagnostics = DiagnosticDescription.None;

var comp = CreateCompilation(source);
VerifyOperationTreeAndDiagnosticsForTest<IsPatternExpressionSyntax>(comp, expectedOperationTree, expectedDiagnostics);
}

[CompilerTrait(CompilerFeature.IOperation)]
[Fact]
public void TestIsPatternExpression_ListPatterns_Span_01()
{
string source = @"
class X
{
void M(System.Span<int> o)
{
_ = /*<bind>*/o is [.., 42]/*</bind>*/;
}
}
";
string expectedOperationTree = @"
IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean) (Syntax: 'o is [.., 42]')
Value:
IParameterReferenceOperation: o (OperationKind.ParameterReference, Type: System.Span<System.Int32>) (Syntax: 'o')
Pattern:
IListPatternOperation (OperationKind.ListPattern, Type: null) (Syntax: '[.., 42]') (InputType: System.Span<System.Int32>, NarrowedType: System.Span<System.Int32>, DeclaredSymbol: null, LengthSymbol: System.Int32 System.Span<System.Int32>.Length { get; }, IndexerSymbol: ref System.Int32 System.Span<System.Int32>.this[System.Int32 i] { get; })
Patterns (2):
ISlicePatternOperation (OperationKind.SlicePattern, Type: null) (Syntax: '..'), SliceSymbol: null
Pattern:
null
IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: '42') (InputType: System.Int32, NarrowedType: System.Int32)
Value:
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 42) (Syntax: '42')
";
var expectedDiagnostics = DiagnosticDescription.None;
var comp = CreateCompilationWithIndexAndRangeAndSpan(source);
VerifyOperationTreeAndDiagnosticsForTest<IsPatternExpressionSyntax>(comp, expectedOperationTree, expectedDiagnostics);
}

[CompilerTrait(CompilerFeature.IOperation)]
[Fact]
public void TestIsPatternExpression_ListPatterns_Span_02()
{
string source = @"
class X
{
void M(System.Span<int> o)
{
_ = /*<bind>*/o is [.. var slice, 42] list/*</bind>*/;
}
}
";
string expectedOperationTree = @"
IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean) (Syntax: 'o is [.. va ... e, 42] list')
Value:
IParameterReferenceOperation: o (OperationKind.ParameterReference, Type: System.Span<System.Int32>) (Syntax: 'o')
Pattern:
IListPatternOperation (OperationKind.ListPattern, Type: null) (Syntax: '[.. var slice, 42] list') (InputType: System.Span<System.Int32>, NarrowedType: System.Span<System.Int32>, DeclaredSymbol: System.Span<System.Int32> list, LengthSymbol: System.Int32 System.Span<System.Int32>.Length { get; }, IndexerSymbol: ref System.Int32 System.Span<System.Int32>.this[System.Int32 i] { get; })
Patterns (2):
ISlicePatternOperation (OperationKind.SlicePattern, Type: null) (Syntax: '.. var slice'), SliceSymbol: System.Span<System.Int32> System.Span<System.Int32>.Slice(System.Int32 offset, System.Int32 length)
Pattern:
IDeclarationPatternOperation (OperationKind.DeclarationPattern, Type: null) (Syntax: 'var slice') (InputType: System.Span<System.Int32>, NarrowedType: System.Span<System.Int32>, DeclaredSymbol: System.Span<System.Int32> slice, MatchesNull: True)
IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: '42') (InputType: System.Int32, NarrowedType: System.Int32)
Value:
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 42) (Syntax: '42')
";
var expectedDiagnostics = DiagnosticDescription.None;

var comp = CreateCompilationWithIndexAndRangeAndSpan(source);
VerifyOperationTreeAndDiagnosticsForTest<IsPatternExpressionSyntax>(comp, expectedOperationTree, expectedDiagnostics);
}

[CompilerTrait(CompilerFeature.IOperation)]
[Fact]
public void TestIsPatternExpression_ListPatterns_MissingMember_Length()
{
string source = @"
class X
{
public int this[int i] => throw null;

void M()
{
_ = /*<bind>*/this is []/*</bind>*/;
}
}
";
string expectedOperationTree = @"
IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean, IsInvalid) (Syntax: 'this is []')
Value:
IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: X) (Syntax: 'this')
Pattern:
IListPatternOperation (OperationKind.ListPattern, Type: null, IsInvalid) (Syntax: '[]') (InputType: X, NarrowedType: X, DeclaredSymbol: null, LengthSymbol: null, IndexerSymbol: null)
Patterns (0)
";
var expectedDiagnostics = new[]
{
// (8,31): error CS9200: List patterns may not be used for a value of type 'X'.
// _ = /*<bind>*/this is []/*</bind>*/;
Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[]").WithArguments("X").WithLocation(8, 31)
};

var comp = CreateCompilation(source);
VerifyOperationTreeAndDiagnosticsForTest<IsPatternExpressionSyntax>(comp, expectedOperationTree, expectedDiagnostics);
}

[CompilerTrait(CompilerFeature.IOperation)]
[Fact]
public void TestIsPatternExpression_ListPatterns_MissingMember_Indexer()
{
string source = @"
class X
{
public int Count { get; }

void M()
{
_ = /*<bind>*/this is []/*</bind>*/;
}
}
";
string expectedOperationTree = @"
IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean, IsInvalid) (Syntax: 'this is []')
Value:
IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: X) (Syntax: 'this')
Pattern:
IListPatternOperation (OperationKind.ListPattern, Type: null, IsInvalid) (Syntax: '[]') (InputType: X, NarrowedType: X, DeclaredSymbol: null, LengthSymbol: System.Int32 X.Count { get; }, IndexerSymbol: null)
Patterns (0)
";
var expectedDiagnostics = new[]
{
// (8,31): error CS9200: List patterns may not be used for a value of type 'X'.
// _ = /*<bind>*/this is []/*</bind>*/;
Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "[]").WithArguments("X").WithLocation(8, 31)
};

var comp = CreateCompilation(source);
VerifyOperationTreeAndDiagnosticsForTest<IsPatternExpressionSyntax>(comp, expectedOperationTree, expectedDiagnostics);
}

[CompilerTrait(CompilerFeature.IOperation)]
[Fact]
public void TestIsPatternExpression_ListPatterns_MissingMember_Slice()
{
string source = @"
class X
{
public int this[int i] => throw null;
public int Count => throw null;

void M()
{
_ = /*<bind>*/this is [.. 0]/*</bind>*/;
}
}
";
string expectedOperationTree = @"
IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean, IsInvalid) (Syntax: 'this is [.. 0]')
Value:
IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: X) (Syntax: 'this')
Pattern:
IListPatternOperation (OperationKind.ListPattern, Type: null, IsInvalid) (Syntax: '[.. 0]') (InputType: X, NarrowedType: X, DeclaredSymbol: null, LengthSymbol: System.Int32 X.Count { get; }, IndexerSymbol: System.Int32 X.this[System.Int32 i] { get; })
Patterns (1):
ISlicePatternOperation (OperationKind.SlicePattern, Type: null, IsInvalid) (Syntax: '.. 0'), SliceSymbol: null
Pattern:
IConstantPatternOperation (OperationKind.ConstantPattern, Type: null, IsInvalid) (Syntax: '0') (InputType: ?, NarrowedType: System.Int32)
Value:
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsInvalid) (Syntax: '0')
";
var expectedDiagnostics = new[]
{
// (9,32): error CS9201: Slice patterns may not be used for a value of type 'X'.
// _ = /*<bind>*/this is [.. 0]/*</bind>*/;
Diagnostic(ErrorCode.ERR_UnsupportedTypeForSlicePattern, ".. 0").WithArguments("X").WithLocation(9, 32)
};

var comp = CreateCompilation(source);
VerifyOperationTreeAndDiagnosticsForTest<IsPatternExpressionSyntax>(comp, expectedOperationTree, expectedDiagnostics);
}
Copy link
Member

Choose a reason for hiding this comment

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

Consider also testing error scenario this is .. or this is .. 42


[CompilerTrait(CompilerFeature.IOperation)]
[Fact]
public void TestIsPatternExpression_ListPatterns_ControlFlow_01()
{
string source = @"
class C
{
void M(int[] o)
/*<bind>*/
{
if (o is [1, .. var slice, 2]) { }
}/*</bind>*/
}
";
string expectedFlowGraph = @"
Block[B0] - Entry
Statements (0)
Next (Regular) Block[B1]
Entering: {R1}
.locals {R1}
{
Locals: [System.Int32[]? slice]
Block[B1] - Block
Predecessors: [B0]
Statements (0)
Jump if False (Regular) to Block[B2]
IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean) (Syntax: 'o is [1, .. ... r slice, 2]')
Value:
IParameterReferenceOperation: o (OperationKind.ParameterReference, Type: System.Int32[]) (Syntax: 'o')
Pattern:
IListPatternOperation (OperationKind.ListPattern, Type: null) (Syntax: '[1, .. var slice, 2]') (InputType: System.Int32[], NarrowedType: System.Int32[], DeclaredSymbol: null, LengthSymbol: System.Int32 System.Array.Length { get; }, IndexerSymbol: null)
Patterns (3):
IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: '1') (InputType: System.Int32, NarrowedType: System.Int32)
Value:
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1')
ISlicePatternOperation (OperationKind.SlicePattern, Type: null) (Syntax: '.. var slice'), SliceSymbol: null
Pattern:
IDeclarationPatternOperation (OperationKind.DeclarationPattern, Type: null) (Syntax: 'var slice') (InputType: System.Int32[], NarrowedType: System.Int32[], DeclaredSymbol: System.Int32[]? slice, MatchesNull: True)
IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: '2') (InputType: System.Int32, NarrowedType: System.Int32)
Value:
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2')
Leaving: {R1}
Next (Regular) Block[B2]
Leaving: {R1}
}
Block[B2] - Exit
Predecessors: [B1*2]
Statements (0)
";

var expectedDiagnostics = DiagnosticDescription.None;

VerifyFlowGraphAndDiagnosticsForTest<BlockSyntax>(source, expectedFlowGraph, expectedDiagnostics, parseOptions: TestOptions.RegularWithExtendedPropertyPatterns);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -255,5 +255,9 @@ public enum OperationKind
RelationalPattern = 0x70,
/// <summary>Indicates an <see cref="IWithOperation"/>.</summary>
With = 0x71,
/// <summary>Indicates an <see cref="IListPatternOperation"/>.</summary>
ListPattern = 0x72,
/// <summary>Indicates an <see cref="ISlicePatternOperation"/>.</summary>
SlicePattern = 0x73,
}
}
Loading