From 1b3094b42f491a5156c145990f29dd24135dd2b4 Mon Sep 17 00:00:00 2001 From: Neal Gafter Date: Thu, 20 Dec 2018 13:50:13 -0800 Subject: [PATCH 1/6] IOperation for recursive pattern and switch expression Work in progress #27749 --- .../Operations/CSharpOperationFactory.cs | 20 ++++-- .../IOperationTests_IForEachLoopStatement.cs | 2 +- .../IOperationTests_IIfStatement.cs | 8 +-- .../IOperationTests_IIsPatternExpression.cs | 69 ++++++++++++++----- ...tionTests_IParameterReferenceExpression.cs | 4 +- .../IOperationTests_IPatternSwitchCase.cs | 38 +++++----- .../IOperationTests_ISwitchOperation.cs | 12 ++-- .../IOperation/IOperationTests_TryCatch.cs | 4 +- .../Operations/ControlFlowGraphBuilder.cs | 11 ++- .../IDeclarationPatternOperation.cs | 7 ++ .../Operations/IRecursivePatternOperation.cs | 19 +++++ .../Portable/Operations/OperationCloner.cs | 12 +++- .../Core/Portable/Operations/OperationKind.cs | 2 + .../Portable/Operations/OperationNodes.cs | 52 ++++++++++++-- .../Core/Portable/PublicAPI.Unshipped.txt | 6 ++ .../Compilation/OperationTreeVerifier.cs | 17 +++++ .../Compilation/TestOperationVisitor.cs | 22 ++++++ 17 files changed, 237 insertions(+), 68 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 6e256d3e9bb70..a88ec0570a13b 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -1837,21 +1837,29 @@ private IDeclarationPatternOperation CreateBoundDeclarationPatternOperation(Boun variable = ((BoundDiscardExpression)boundDeclarationPattern.VariableAccess).ExpressionSymbol; } + bool acceptsNull = boundDeclarationPattern.IsVar; SyntaxNode syntax = boundDeclarationPattern.Syntax; - ITypeSymbol type = null; - Optional constantValue = default(Optional); bool isImplicit = boundDeclarationPattern.WasCompilerGenerated; - return new DeclarationPatternOperation(variable, _semanticModel, syntax, type, constantValue, isImplicit); + return new DeclarationPatternOperation(variable, acceptsNull, _semanticModel, syntax, isImplicit); } private IRecursivePatternOperation CreateBoundRecursivePatternOperation(BoundRecursivePattern boundRecursivePattern) { + ITypeSymbol matchedType = boundRecursivePattern.DeclaredType?.Type ?? boundRecursivePattern.InputType; ISymbol variable = boundRecursivePattern.Variable; + ISymbol deconstructSymbol = boundRecursivePattern.DeconstructMethod; + if (variable == null && boundRecursivePattern.VariableAccess?.Kind == BoundKind.DiscardExpression) + { + variable = ((BoundDiscardExpression)boundRecursivePattern.VariableAccess).ExpressionSymbol; + } + SyntaxNode syntax = boundRecursivePattern.Syntax; - ITypeSymbol type = null; - Optional constantValue = default(Optional); + ImmutableArray deconstructionSubpatterns = boundRecursivePattern.Deconstruction.IsDefault ? default : + boundRecursivePattern.Deconstruction.SelectAsArray(p => (IPatternOperation)Create(p.Pattern)); + ImmutableArray<(ISymbol, IPatternOperation)> propertySubpatterns = boundRecursivePattern.Properties.IsDefault ? default : + boundRecursivePattern.Properties.SelectAsArray(p => ((ISymbol)p.Symbol, (IPatternOperation)Create(p.Pattern))); bool isImplicit = boundRecursivePattern.WasCompilerGenerated; - return new RecursivePattern(variable, _semanticModel, syntax, type, constantValue, isImplicit); + return new RecursivePatternOperation(matchedType, deconstructSymbol, deconstructionSubpatterns, propertySubpatterns, variable, _semanticModel, syntax, isImplicit); } private ISwitchOperation CreateBoundSwitchStatementOperation(BoundSwitchStatement boundSwitchStatement) diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IForEachLoopStatement.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IForEachLoopStatement.cs index 6835604c50660..cb697e228f9e7 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IForEachLoopStatement.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IForEachLoopStatement.cs @@ -1442,7 +1442,7 @@ void M(object o) Expression: IParameterReferenceOperation: o (OperationKind.ParameterReference, Type: System.Object) (Syntax: 'o') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int32 x) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int x') + IDeclarationPatternOperation (Declared Symbol: System.Int32 x, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int x') Collection: ILocalReferenceOperation: arr (OperationKind.LocalReference, Type: System.Int32[]) (Syntax: 'arr') Body: diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIfStatement.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIfStatement.cs index 8a7bd1dc03425..a4f1181cc72aa 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIfStatement.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIfStatement.cs @@ -701,7 +701,7 @@ private static void A(bool flag, int number) Expression: ILocalReferenceOperation: o (OperationKind.LocalReference, Type: System.Object) (Syntax: 'o') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int32 i) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int i') + IDeclarationPatternOperation (Declared Symbol: System.Int32 i, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int i') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: number) (OperationKind.Argument, Type: null) (Syntax: '1') @@ -743,7 +743,7 @@ private void M() Expression: ILocalReferenceOperation: obj (OperationKind.LocalReference, Type: System.Object) (Syntax: 'obj') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.String str) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'string str') + IDeclarationPatternOperation (Declared Symbol: System.String str, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'string str') WhenTrue: IBlockOperation (1 statements) (OperationKind.Block, Type: null) (Syntax: '{ ... }') IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'Console.WriteLine(str);') @@ -854,7 +854,7 @@ private static void A(bool flag, int number) Expression: ILocalReferenceOperation: o (OperationKind.LocalReference, Type: System.Object) (Syntax: 'o') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int32 i) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int i') + IDeclarationPatternOperation (Declared Symbol: System.Int32 i, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int i') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: number) (OperationKind.Argument, Type: null) (Syntax: '1') @@ -1250,7 +1250,7 @@ private void M() Expression: ILocalReferenceOperation: obj (OperationKind.LocalReference, Type: System.Object) (Syntax: 'obj') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.String str) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'string str') + IDeclarationPatternOperation (Declared Symbol: System.String str, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'string str') WhenTrue: IBlockOperation (1 statements) (OperationKind.Block, Type: null) (Syntax: '{ ... }') IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'Console.WriteLine(str);') diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs index 130e692308ad9..a0a092bca5e45 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs @@ -29,7 +29,7 @@ void M() Expression: ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32?) (Syntax: 'x') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int32? y) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'var y') + IDeclarationPatternOperation (Declared Symbol: System.Int32? y, AcceptsNull: True) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'var y') "; var expectedDiagnostics = DiagnosticDescription.None; @@ -56,7 +56,7 @@ void M() Expression: ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32?) (Syntax: 'x') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int32 y) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int y') + IDeclarationPatternOperation (Declared Symbol: System.Int32 y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int y') "; var expectedDiagnostics = DiagnosticDescription.None; @@ -82,7 +82,7 @@ void M(X x) Expression: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: X) (Syntax: 'x') Pattern: - IDeclarationPatternOperation (Declared Symbol: X y) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'X y') + IDeclarationPatternOperation (Declared Symbol: X y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'X y') "; var expectedDiagnostics = DiagnosticDescription.None; @@ -108,7 +108,7 @@ void M(T x) where T: class Expression: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: T) (Syntax: 'x') Pattern: - IDeclarationPatternOperation (Declared Symbol: T y) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'T y') + IDeclarationPatternOperation (Declared Symbol: T y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'T y') "; var expectedDiagnostics = DiagnosticDescription.None; @@ -134,7 +134,7 @@ void M(X x) Expression: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: X) (Syntax: 'x') Pattern: - IDeclarationPatternOperation (Declared Symbol: dynamic y) (OperationKind.DeclarationPattern, Type: null, IsInvalid) (Syntax: 'dynamic y') + IDeclarationPatternOperation (Declared Symbol: dynamic y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null, IsInvalid) (Syntax: 'dynamic y') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS8208: It is not legal to use the type 'dynamic' in a pattern. @@ -298,7 +298,7 @@ void M() Expression: ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32?) (Syntax: 'x') Pattern: - IDeclarationPatternOperation (Declared Symbol: UndefinedType y) (OperationKind.DeclarationPattern, Type: null, IsInvalid) (Syntax: 'UndefinedType y') + IDeclarationPatternOperation (Declared Symbol: UndefinedType y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null, IsInvalid) (Syntax: 'UndefinedType y') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0246: The type or namespace name 'UndefinedType' could not be found (are you missing a using directive or an assembly reference?) @@ -362,7 +362,7 @@ void M() Expression: ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32?) (Syntax: 'x') Pattern: - IDeclarationPatternOperation (Declared Symbol: X y) (OperationKind.DeclarationPattern, Type: null, IsInvalid) (Syntax: 'X y') + IDeclarationPatternOperation (Declared Symbol: X y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null, IsInvalid) (Syntax: 'X y') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS8121: An expression of type 'int?' cannot be handled by a pattern of type 'X'. @@ -393,7 +393,7 @@ void M() Expression: ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32?) (Syntax: 'x') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int32 y) (OperationKind.DeclarationPattern, Type: null, IsInvalid) (Syntax: 'int y') + IDeclarationPatternOperation (Declared Symbol: System.Int32 y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null, IsInvalid) (Syntax: 'int y') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0128: A local variable or function named 'y' is already defined in this scope @@ -424,7 +424,7 @@ void M() Expression: ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Int32?) (Syntax: 'x') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int32 y2) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int y2') + IDeclarationPatternOperation (Declared Symbol: System.Int32 y2, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int y2') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS1026: ) expected @@ -528,7 +528,7 @@ void M(string x = /**/string.Empty is string y/**/) Instance Receiver: null Pattern: - IDeclarationPatternOperation (Declared Symbol: System.String y) (OperationKind.DeclarationPattern, Type: null, IsInvalid) (Syntax: 'string y') + IDeclarationPatternOperation (Declared Symbol: System.String y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null, IsInvalid) (Syntax: 'string y') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS1736: Default parameter value for 'x' must be a compile-time constant @@ -557,7 +557,7 @@ class C Instance Receiver: null Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int32 x) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int x') + IDeclarationPatternOperation (Declared Symbol: System.Int32 x, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int x') "; var expectedDiagnostics = DiagnosticDescription.None; @@ -586,7 +586,7 @@ public C (bool b) Expression: IParameterReferenceOperation: o (OperationKind.ParameterReference, Type: System.Object) (Syntax: 'o') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int32 x) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int x') + IDeclarationPatternOperation (Declared Symbol: System.Int32 x, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int x') "; var expectedDiagnostics = DiagnosticDescription.None; @@ -618,7 +618,7 @@ class C Instance Receiver: null Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int32 x) (OperationKind.DeclarationPattern, Type: null, IsInvalid) (Syntax: 'int x') + IDeclarationPatternOperation (Declared Symbol: System.Int32 x, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null, IsInvalid) (Syntax: 'int x') "; var expectedDiagnostics = new DiagnosticDescription[] { // CS0134: 'C.o' is of type 'object'. A const field of a reference type other than string can only be initialized with null. @@ -639,10 +639,11 @@ public void IsPattern_NoControlFlow() string source = @" class C { - void M(int? x, bool b, int x2, bool b2) + void M(int? x, bool b, int x2, bool b2, (int X, int Y)? x3, bool b3) /**/{ b = x is var y; b2 = x2 is 1; + b3 = x3 is (1, 2) { Item1: var z } p; }/**/ } "; @@ -668,7 +669,7 @@ void M(int? x, bool b, int x2, bool b2) Expression: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32?) (Syntax: 'x') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int32? y) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'var y') + IDeclarationPatternOperation (Declared Symbol: System.Int32? y, AcceptsNull: True) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'var y') IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b2 = x2 is 1;') Expression: @@ -782,7 +783,7 @@ void M(int? x1, int x2, bool b) Expression: IFlowCaptureReferenceOperation: 2 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'x1 ?? x2') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int32 y) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'var y') + IDeclarationPatternOperation (Declared Symbol: System.Int32 y, AcceptsNull: True) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'var y') Next (Regular) Block[B6] Leaving: {R1} @@ -797,6 +798,42 @@ void M(int? x1, int x2, bool b) VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); } + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [Fact] + public void IsPattern_RecursivePattern() + { + string source = @" +class C +{ + void M((int X, int Y) tuple, bool b) + { + if (/**/tuple is (1, 2) { Item1: int x } y/**/) { } + } +} +"; + string expectedOperationTree = @" +IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean) (Syntax: 'tuple is (1 ... : int x } y') + Expression: + IParameterReferenceOperation: tuple (OperationKind.ParameterReference, Type: (System.Int32 X, System.Int32 Y)) (Syntax: 'tuple') + Pattern: + IRecursivePatternOperation (Declared Symbol: (System.Int32 X, System.Int32 Y) y, MatchedType: (System.Int32 X, System.Int32 Y), Deconstruct Symbol: null) + Patterns (2): + IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: '1') + Value: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: '2') + Value: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + Properties (1): + Matched Property: System.Int32 (System.Int32 X, System.Int32 Y).Item1, Pattern: + IDeclarationPatternOperation (Declared Symbol: System.Int32 x, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int x') + (OperationKind.DeclarationPattern, Type: null) (Syntax: '(1, 2) { It ... : int x } y') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] [Fact] public void IsPattern_ControlFlowInPattern() diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IParameterReferenceExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IParameterReferenceExpression.cs index 362085c5e8eba..22ccccb76bfef 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IParameterReferenceExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IParameterReferenceExpression.cs @@ -848,7 +848,7 @@ public void M(int x) Clauses: IPatternCaseClauseOperation (Label Id: 0) (CaseKind.Pattern) (OperationKind.CaseClause, Type: null) (Syntax: 'case var y ... (x >= 10):') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int32 y) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'var y') + IDeclarationPatternOperation (Declared Symbol: System.Int32 y, AcceptsNull: True) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'var y') Guard Expression: IBinaryOperation (BinaryOperatorKind.GreaterThanOrEqual) (OperationKind.Binary, Type: System.Boolean) (Syntax: 'x >= 10') Left: @@ -1040,7 +1040,7 @@ public void Method1(object x) Expression: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Object) (Syntax: 'x') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int32 y) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int y') + IDeclarationPatternOperation (Declared Symbol: System.Int32 y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int y') "; var expectedDiagnostics = DiagnosticDescription.None; diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IPatternSwitchCase.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IPatternSwitchCase.cs index 7c19d2761a9f3..68db618e36dab 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IPatternSwitchCase.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IPatternSwitchCase.cs @@ -31,7 +31,7 @@ void M() string expectedOperationTree = @" IPatternCaseClauseOperation (Label Id: 0) (CaseKind.Pattern) (OperationKind.CaseClause, Type: null) (Syntax: 'case var y:') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int32? y) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'var y') + IDeclarationPatternOperation (Declared Symbol: System.Int32? y, AcceptsNull: True) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'var y') Guard Expression: null "; @@ -62,7 +62,7 @@ void M() string expectedOperationTree = @" IPatternCaseClauseOperation (Label Id: 0) (CaseKind.Pattern) (OperationKind.CaseClause, Type: null) (Syntax: 'case int y:') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int32 y) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int y') + IDeclarationPatternOperation (Declared Symbol: System.Int32 y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int y') Guard Expression: null "; @@ -92,7 +92,7 @@ void M(object x) string expectedOperationTree = @" IPatternCaseClauseOperation (Label Id: 0) (CaseKind.Pattern) (OperationKind.CaseClause, Type: null) (Syntax: 'case X y:') Pattern: - IDeclarationPatternOperation (Declared Symbol: X y) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'X y') + IDeclarationPatternOperation (Declared Symbol: X y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'X y') Guard Expression: null "; @@ -122,7 +122,7 @@ void M(object x) where T : class string expectedOperationTree = @" IPatternCaseClauseOperation (Label Id: 0) (CaseKind.Pattern) (OperationKind.CaseClause, Type: null) (Syntax: 'case T y:') Pattern: - IDeclarationPatternOperation (Declared Symbol: T y) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'T y') + IDeclarationPatternOperation (Declared Symbol: T y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'T y') Guard Expression: null "; @@ -152,7 +152,7 @@ void M(object x) where T : class string expectedOperationTree = @" IPatternCaseClauseOperation (Label Id: 0) (CaseKind.Pattern) (OperationKind.CaseClause, Type: null, IsInvalid) (Syntax: 'case dynamic y:') Pattern: - IDeclarationPatternOperation (Declared Symbol: dynamic y) (OperationKind.DeclarationPattern, Type: null, IsInvalid) (Syntax: 'dynamic y') + IDeclarationPatternOperation (Declared Symbol: dynamic y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null, IsInvalid) (Syntax: 'dynamic y') Guard Expression: null "; @@ -188,7 +188,7 @@ void M(object x) string expectedOperationTree = @" IPatternCaseClauseOperation (Label Id: 0) (CaseKind.Pattern) (OperationKind.CaseClause, Type: null) (Syntax: 'case X y:') Pattern: - IDeclarationPatternOperation (Declared Symbol: X y) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'X y') + IDeclarationPatternOperation (Declared Symbol: X y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'X y') Guard Expression: null "; @@ -219,7 +219,7 @@ void M(object x) string expectedOperationTree = @" IPatternCaseClauseOperation (Label Id: 0) (CaseKind.Pattern) (OperationKind.CaseClause, Type: null) (Syntax: 'case X y:') Pattern: - IDeclarationPatternOperation (Declared Symbol: X y) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'X y') + IDeclarationPatternOperation (Declared Symbol: X y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'X y') Guard Expression: null "; @@ -251,7 +251,7 @@ void M(object x) string expectedOperationTree = @" IPatternCaseClauseOperation (Label Id: 0) (CaseKind.Pattern) (OperationKind.CaseClause, Type: null) (Syntax: 'case X y:') Pattern: - IDeclarationPatternOperation (Declared Symbol: X y) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'X y') + IDeclarationPatternOperation (Declared Symbol: X y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'X y') Guard Expression: null "; @@ -281,7 +281,7 @@ void M(object x) string expectedOperationTree = @" IPatternCaseClauseOperation (Label Id: 0) (CaseKind.Pattern) (OperationKind.CaseClause, Type: null) (Syntax: 'case X y when x != null:') Pattern: - IDeclarationPatternOperation (Declared Symbol: X y) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'X y') + IDeclarationPatternOperation (Declared Symbol: X y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'X y') Guard Expression: IBinaryOperation (BinaryOperatorKind.NotEquals) (OperationKind.Binary, Type: System.Boolean) (Syntax: 'x != null') Left: @@ -318,13 +318,13 @@ void M(object x) string expectedOperationTree = @" IPatternCaseClauseOperation (Label Id: 0) (CaseKind.Pattern) (OperationKind.CaseClause, Type: null) (Syntax: 'case X y when x is X z :') Pattern: - IDeclarationPatternOperation (Declared Symbol: X y) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'X y') + IDeclarationPatternOperation (Declared Symbol: X y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'X y') Guard Expression: IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean) (Syntax: 'x is X z') Expression: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Object) (Syntax: 'x') Pattern: - IDeclarationPatternOperation (Declared Symbol: X z) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'X z') + IDeclarationPatternOperation (Declared Symbol: X z, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'X z') "; var expectedDiagnostics = DiagnosticDescription.None; @@ -352,7 +352,7 @@ void M(object x) string expectedOperationTree = @" IPatternCaseClauseOperation (Label Id: 0) (CaseKind.Pattern) (OperationKind.CaseClause, Type: null, IsInvalid) (Syntax: 'case X y when :') Pattern: - IDeclarationPatternOperation (Declared Symbol: X y) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'X y') + IDeclarationPatternOperation (Declared Symbol: X y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'X y') Guard Expression: IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid) (Syntax: '') Children(0) @@ -387,7 +387,7 @@ void M(object x) string expectedOperationTree = @" IPatternCaseClauseOperation (Label Id: 0) (CaseKind.Pattern) (OperationKind.CaseClause, Type: null, IsInvalid) (Syntax: 'case X y when x:') Pattern: - IDeclarationPatternOperation (Declared Symbol: X y) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'X y') + IDeclarationPatternOperation (Declared Symbol: X y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'X y') Guard Expression: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Boolean, IsInvalid, IsImplicit) (Syntax: 'x') Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) @@ -524,7 +524,7 @@ void M(object x) string expectedOperationTree = @" IPatternCaseClauseOperation (Label Id: 0) (CaseKind.Pattern) (OperationKind.CaseClause, Type: null, IsInvalid) (Syntax: 'case UndefinedType y:') Pattern: - IDeclarationPatternOperation (Declared Symbol: UndefinedType y) (OperationKind.DeclarationPattern, Type: null, IsInvalid) (Syntax: 'UndefinedType y') + IDeclarationPatternOperation (Declared Symbol: UndefinedType y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null, IsInvalid) (Syntax: 'UndefinedType y') Guard Expression: null "; @@ -558,7 +558,7 @@ void M(int? x) string expectedOperationTree = @" IPatternCaseClauseOperation (Label Id: 0) (CaseKind.Pattern) (OperationKind.CaseClause, Type: null, IsInvalid) (Syntax: 'case X y:') Pattern: - IDeclarationPatternOperation (Declared Symbol: X y) (OperationKind.DeclarationPattern, Type: null, IsInvalid) (Syntax: 'X y') + IDeclarationPatternOperation (Declared Symbol: X y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null, IsInvalid) (Syntax: 'X y') Guard Expression: null "; @@ -593,7 +593,7 @@ void M(int? x) string expectedOperationTree = @" IPatternCaseClauseOperation (Label Id: 0) (CaseKind.Pattern) (OperationKind.CaseClause, Type: null, IsInvalid) (Syntax: 'case int y:') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int32 y) (OperationKind.DeclarationPattern, Type: null, IsInvalid) (Syntax: 'int y') + IDeclarationPatternOperation (Declared Symbol: System.Int32 y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null, IsInvalid) (Syntax: 'int y') Guard Expression: null "; @@ -689,7 +689,7 @@ void M(object p) Clauses: IPatternCaseClauseOperation (Label Id: 1) (CaseKind.Pattern) (OperationKind.CaseClause, Type: null) (Syntax: 'case int x:') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int32 x) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int x') + IDeclarationPatternOperation (Declared Symbol: System.Int32 x, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int x') Guard Expression: null Body: @@ -699,7 +699,7 @@ void M(object p) Clauses: IPatternCaseClauseOperation (Label Id: 2) (CaseKind.Pattern) (OperationKind.CaseClause, Type: null, IsInvalid) (Syntax: 'case int y:') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int32 y) (OperationKind.DeclarationPattern, Type: null, IsInvalid) (Syntax: 'int y') + IDeclarationPatternOperation (Declared Symbol: System.Int32 y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null, IsInvalid) (Syntax: 'int y') Guard Expression: null Body: @@ -709,7 +709,7 @@ void M(object p) Clauses: IPatternCaseClauseOperation (Label Id: 3) (CaseKind.Pattern) (OperationKind.CaseClause, Type: null) (Syntax: 'case X z:') Pattern: - IDeclarationPatternOperation (Declared Symbol: X z) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'X z') + IDeclarationPatternOperation (Declared Symbol: X z, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'X z') Guard Expression: null Body: diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ISwitchOperation.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ISwitchOperation.cs index fcc0027233964..8c27728fb7c73 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ISwitchOperation.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ISwitchOperation.cs @@ -134,12 +134,12 @@ static void M(object input) Clauses: IPatternCaseClauseOperation (Label Id: 1) (CaseKind.Pattern) (OperationKind.CaseClause, Type: null) (Syntax: 'case int x:') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int32 x) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int x') + IDeclarationPatternOperation (Declared Symbol: System.Int32 x, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int x') Guard Expression: null IPatternCaseClauseOperation (Label Id: 2) (CaseKind.Pattern) (OperationKind.CaseClause, Type: null) (Syntax: 'case long y:') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int64 y) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'long y') + IDeclarationPatternOperation (Declared Symbol: System.Int64 y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'long y') Guard Expression: null Body: @@ -182,7 +182,7 @@ static void M(object input) Clauses: IPatternCaseClauseOperation (Label Id: 1) (CaseKind.Pattern) (OperationKind.CaseClause, Type: null) (Syntax: 'case int y:') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int32 y) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int y') + IDeclarationPatternOperation (Declared Symbol: System.Int32 y, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int y') Guard Expression: null Body: @@ -2680,7 +2680,7 @@ void M(bool result, object input) Expression: IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Object, IsImplicit) (Syntax: 'input') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int32 x) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int x') + IDeclarationPatternOperation (Declared Symbol: System.Int32 x, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int x') Leaving: {R2} {R1} Next (Regular) Block[B3] @@ -2757,7 +2757,7 @@ void M(bool result, object input1, bool other2, bool other3, bool other4) Expression: IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Object, IsImplicit) (Syntax: 'input1') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int32 x) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int x') + IDeclarationPatternOperation (Declared Symbol: System.Int32 x, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int x') Leaving: {R2} {R1} Next (Regular) Block[B3] @@ -3261,7 +3261,7 @@ void M(bool result, int? input) Expression: IParameterReferenceOperation: input (OperationKind.ParameterReference, Type: System.Int32?, IsInvalid) (Syntax: 'input') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int32 x1) (OperationKind.DeclarationPattern, Type: null, IsInvalid) (Syntax: 'int x1') + IDeclarationPatternOperation (Declared Symbol: System.Int32 x1, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null, IsInvalid) (Syntax: 'int x1') Next (Regular) Block[B3] Block[B3] - Block diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_TryCatch.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_TryCatch.cs index 7e6e4aa51fb74..6904151355cc4 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_TryCatch.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_TryCatch.cs @@ -646,7 +646,7 @@ Catch clauses(1): Expression: IParameterReferenceOperation: o (OperationKind.ParameterReference, Type: System.Object) (Syntax: 'o') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.String s) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'string s') + IDeclarationPatternOperation (Declared Symbol: System.String s, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'string s') Handler: IBlockOperation (0 statements) (OperationKind.Block, Type: null) (Syntax: '{ ... }') Finally: @@ -694,7 +694,7 @@ Catch clauses(1): Expression: IParameterReferenceOperation: o (OperationKind.ParameterReference, Type: System.Object) (Syntax: 'o') Pattern: - IDeclarationPatternOperation (Declared Symbol: System.String s) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'string s') + IDeclarationPatternOperation (Declared Symbol: System.String s, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'string s') Handler: IBlockOperation (0 statements) (OperationKind.Block, Type: null) (Syntax: '{ ... }') Finally: diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index ac8a8927073f8..0da1228e5aa95 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -6555,8 +6555,15 @@ public override IOperation VisitConstantPattern(IConstantPatternOperation operat public override IOperation VisitDeclarationPattern(IDeclarationPatternOperation operation, int? captureIdForResult) { - return new DeclarationPatternOperation(operation.DeclaredSymbol, semanticModel: null, - operation.Syntax, operation.Type, operation.ConstantValue, IsImplicit(operation)); + return new DeclarationPatternOperation(operation.DeclaredSymbol, operation.AcceptsNull, semanticModel: null, + operation.Syntax, IsImplicit(operation)); + } + + public override IOperation VisitRecursivePattern(IRecursivePatternOperation operation, int? argument) + { + return new RecursivePatternOperation( + operation.MatchedType, operation.DeconstructSymbol, operation.DeconstructionSubpatterns, operation.PropertySubpatterns, + operation.DeclaredSymbol, operation.SemanticModel, operation.Syntax, operation.IsImplicit); } public override IOperation VisitDelegateCreation(IDelegateCreationOperation operation, int? captureIdForResult) diff --git a/src/Compilers/Core/Portable/Operations/IDeclarationPatternOperation.cs b/src/Compilers/Core/Portable/Operations/IDeclarationPatternOperation.cs index bff612be14bc4..f2190a73bd9ed 100644 --- a/src/Compilers/Core/Portable/Operations/IDeclarationPatternOperation.cs +++ b/src/Compilers/Core/Portable/Operations/IDeclarationPatternOperation.cs @@ -15,6 +15,13 @@ namespace Microsoft.CodeAnalysis.Operations /// public interface IDeclarationPatternOperation : IPatternOperation { + /// + /// True if the pattern is of a form that accepts null. + /// For example, in C# the pattern `var x` will match a null input, + /// while the pattern `string x` will not. + /// + bool AcceptsNull { get; } + /// /// Symbol declared by the pattern. /// diff --git a/src/Compilers/Core/Portable/Operations/IRecursivePatternOperation.cs b/src/Compilers/Core/Portable/Operations/IRecursivePatternOperation.cs index a915c30c8e7a3..a0dc2f76a9278 100644 --- a/src/Compilers/Core/Portable/Operations/IRecursivePatternOperation.cs +++ b/src/Compilers/Core/Portable/Operations/IRecursivePatternOperation.cs @@ -13,6 +13,25 @@ namespace Microsoft.CodeAnalysis.Operations /// public interface IRecursivePatternOperation : IPatternOperation { + /// + /// The (explicit or implicit) type accepted for the recursive pattern. + /// + ITypeSymbol MatchedType { get; } + /// + /// The Deconstruct symbol, if any, used for the deconstruction subpatterns. + /// + ISymbol DeconstructSymbol { get; } + /// + /// If there is a deconstruction or positional subpattern, this contains the patterns contained within it. + /// If there is no deconstruction subpattern, this is a default immutable array. + /// + ImmutableArray DeconstructionSubpatterns { get; } + /// + /// If there is a property subpattern, this contains the + /// / pairs within it. + /// If there is no property subpattern, this is a default immutable array. + /// + ImmutableArray<(ISymbol, IPatternOperation)> PropertySubpatterns { get; } /// /// Symbol declared by the pattern. /// diff --git a/src/Compilers/Core/Portable/Operations/OperationCloner.cs b/src/Compilers/Core/Portable/Operations/OperationCloner.cs index 2cf7ec3f387ea..a4d193255d50f 100644 --- a/src/Compilers/Core/Portable/Operations/OperationCloner.cs +++ b/src/Compilers/Core/Portable/Operations/OperationCloner.cs @@ -47,7 +47,13 @@ internal override IOperation VisitNoneOperation(IOperation operation, object arg private ImmutableArray VisitArray(ImmutableArray nodes) where T : IOperation { // clone the array - return nodes.SelectAsArray(n => Visit(n)); + return nodes.IsDefault ? default : nodes.SelectAsArray(n => Visit(n)); + } + + private ImmutableArray<(U, T)> VisitArray(ImmutableArray<(U, T)> nodes) where T : IOperation + { + // clone the array + return nodes.IsDefault ? default : nodes.SelectAsArray(n => (n.Item1, Visit(n.Item2))); } public override IOperation VisitBlock(IBlockOperation operation, object argument) @@ -524,12 +530,12 @@ public override IOperation VisitConstantPattern(IConstantPatternOperation operat public override IOperation VisitDeclarationPattern(IDeclarationPatternOperation operation, object argument) { - return new DeclarationPatternOperation(operation.DeclaredSymbol, ((Operation)operation).OwningSemanticModel, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit); + return new DeclarationPatternOperation(operation.DeclaredSymbol, operation.AcceptsNull, ((Operation)operation).OwningSemanticModel, operation.Syntax, operation.IsImplicit); } public override IOperation VisitRecursivePattern(IRecursivePatternOperation operation, object argument) { - return new RecursivePattern(operation.DeclaredSymbol, ((Operation)operation).OwningSemanticModel, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit); + return new RecursivePatternOperation(operation.MatchedType, operation.DeconstructSymbol, VisitArray(operation.DeconstructionSubpatterns), VisitArray(operation.PropertySubpatterns), operation.DeclaredSymbol, ((Operation)operation).OwningSemanticModel, operation.Syntax, operation.IsImplicit); } public override IOperation VisitPatternCaseClause(IPatternCaseClauseOperation operation, object argument) diff --git a/src/Compilers/Core/Portable/Operations/OperationKind.cs b/src/Compilers/Core/Portable/Operations/OperationKind.cs index 296aca8303674..e1307711aec35 100644 --- a/src/Compilers/Core/Portable/Operations/OperationKind.cs +++ b/src/Compilers/Core/Portable/Operations/OperationKind.cs @@ -232,6 +232,8 @@ public enum OperationKind ReDim = 0x65, /// Indicates an . ReDimClause = 0x66, + /// Indicates an . + RecursivePattern = 0x67, // /// Indicates an . // https://github.com/dotnet/roslyn/issues/21281 diff --git a/src/Compilers/Core/Portable/Operations/OperationNodes.cs b/src/Compilers/Core/Portable/Operations/OperationNodes.cs index 799b6c2f38423..82813101b8538 100644 --- a/src/Compilers/Core/Portable/Operations/OperationNodes.cs +++ b/src/Compilers/Core/Portable/Operations/OperationNodes.cs @@ -7913,11 +7913,13 @@ public override IOperation Value /// internal sealed partial class DeclarationPatternOperation : Operation, IDeclarationPatternOperation { - public DeclarationPatternOperation(ISymbol declaredSymbol, SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional constantValue, bool isImplicit) : - base(OperationKind.DeclarationPattern, semanticModel, syntax, type, constantValue, isImplicit) + public DeclarationPatternOperation(ISymbol declaredSymbol, bool acceptsNull, SemanticModel semanticModel, SyntaxNode syntax, bool isImplicit) : + base(OperationKind.DeclarationPattern, semanticModel, syntax, type: default, constantValue: default, isImplicit) { + AcceptsNull = acceptsNull; DeclaredSymbol = declaredSymbol; } + public bool AcceptsNull { get; } /// /// Symbol declared by the pattern. /// @@ -7940,16 +7942,39 @@ public override TResult Accept(OperationVisitor - /// Represents a C# declaration pattern. + /// Represents a C# recursive pattern. /// - internal sealed partial class RecursivePattern : Operation, IRecursivePatternOperation + internal sealed partial class RecursivePatternOperation : Operation, IRecursivePatternOperation { - public RecursivePattern(ISymbol declaredSymbol, SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional constantValue, bool isImplicit) : - base(OperationKind.DeclarationPattern, semanticModel, syntax, type, constantValue, isImplicit) + public RecursivePatternOperation(ITypeSymbol matchedType, ISymbol deconstructSymbol, ImmutableArray deconstructionSubpatterns, ImmutableArray<(ISymbol, IPatternOperation)> propertySubpatterns, ISymbol declaredSymbol, SemanticModel semanticModel, SyntaxNode syntax, bool isImplicit) : + base(OperationKind.RecursivePattern, semanticModel, syntax, type: default, constantValue: default, isImplicit) { + MatchedType = matchedType; + DeconstructSymbol = deconstructSymbol; + DeconstructionSubpatterns = deconstructionSubpatterns; + PropertySubpatterns = propertySubpatterns; DeclaredSymbol = declaredSymbol; } /// + /// The (explicit or implicit) type accepted for the recursive pattern. + /// + public ITypeSymbol MatchedType { get; } + /// + /// The Deconstruct symbol, if any, used for the deconstruction subpatterns. + /// + public ISymbol DeconstructSymbol { get; } + /// + /// If there is a positional subpattern, this contains the patterns contained within it. + /// If there is no positional subpattern, this is a default immutable array. + /// + public ImmutableArray DeconstructionSubpatterns { get; } + /// + /// If there is a property subpattern, this contains the + /// / pairs within it. + /// If there is no property subpattern, this is a default immutable array. + /// + public ImmutableArray<(ISymbol, IPatternOperation)> PropertySubpatterns { get; } + /// /// Symbol declared by the pattern. /// public ISymbol DeclaredSymbol { get; } @@ -7957,7 +7982,20 @@ public override IEnumerable Children { get { - yield break; + if (!DeconstructionSubpatterns.IsDefault) + { + foreach (var p in DeconstructionSubpatterns) + { + yield return p; + } + } + if (!PropertySubpatterns.IsDefault) + { + foreach (var p in PropertySubpatterns) + { + yield return p.Item2; + } + } } } public override void Accept(OperationVisitor visitor) diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 3aa639babf72b..8bf3ea25630d4 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -5,10 +5,16 @@ Microsoft.CodeAnalysis.OperationKind.ConstructorBody = 89 -> Microsoft.CodeAnaly Microsoft.CodeAnalysis.OperationKind.FromEndIndex = 100 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.OperationKind.MethodBody = 88 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.OperationKind.Range = 99 -> Microsoft.CodeAnalysis.OperationKind +Microsoft.CodeAnalysis.OperationKind.RecursivePattern = 103 -> Microsoft.CodeAnalysis.OperationKind +Microsoft.CodeAnalysis.Operations.IDeclarationPatternOperation.AcceptsNull.get -> bool Microsoft.CodeAnalysis.OperationKind.TupleBinary = 87 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.OperationKind.Unary = 31 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation.DeclaredSymbol.get -> Microsoft.CodeAnalysis.ISymbol +Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation.DeconstructSymbol.get -> Microsoft.CodeAnalysis.ISymbol +Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation.DeconstructionSubpatterns.get -> System.Collections.Immutable.ImmutableArray +Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation.MatchedType.get -> Microsoft.CodeAnalysis.ITypeSymbol +Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation.PropertySubpatterns.get -> System.Collections.Immutable.ImmutableArray<(Microsoft.CodeAnalysis.ISymbol, Microsoft.CodeAnalysis.Operations.IPatternOperation)> abstract Microsoft.CodeAnalysis.Compilation.ClassifyCommonConversion(Microsoft.CodeAnalysis.ITypeSymbol source, Microsoft.CodeAnalysis.ITypeSymbol destination) -> Microsoft.CodeAnalysis.Operations.CommonConversion abstract Microsoft.CodeAnalysis.Compilation.ContainsSymbolsWithName(string name, Microsoft.CodeAnalysis.SymbolFilter filter = Microsoft.CodeAnalysis.SymbolFilter.TypeAndMember, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> bool abstract Microsoft.CodeAnalysis.Compilation.GetSymbolsWithName(string name, Microsoft.CodeAnalysis.SymbolFilter filter = Microsoft.CodeAnalysis.SymbolFilter.TypeAndMember, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Collections.Generic.IEnumerable diff --git a/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs b/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs index 374871ee618d8..2ab4bfc601fb6 100644 --- a/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs +++ b/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs @@ -1738,10 +1738,27 @@ public override void VisitDeclarationPattern(IDeclarationPatternOperation operat { LogString(nameof(IDeclarationPatternOperation)); LogSymbol(operation.DeclaredSymbol, " (Declared Symbol"); + LogConstant((object)operation.AcceptsNull, ", AcceptsNull"); LogString(")"); LogCommonPropertiesAndNewLine(operation); } + public override void VisitRecursivePattern(IRecursivePatternOperation operation) + { + LogString(nameof(IRecursivePatternOperation)); + LogSymbol(operation.DeclaredSymbol, " (Declared Symbol"); + LogType(operation.MatchedType, ", MatchedType"); + LogSymbol(operation.DeconstructSymbol, ", Deconstruct Symbol"); + LogString(")"); + LogNewLine(); + VisitArray(operation.DeconstructionSubpatterns, "Patterns ", true, true); + VisitArrayCommon(operation.PropertySubpatterns, "Properties ", true, true, subpat => { + LogSymbol(subpat.Item1, "Matched Property"); + Visit(subpat.Item2, ", Pattern"); + }); + LogCommonPropertiesAndNewLine(operation); + } + public override void VisitIsPattern(IIsPatternOperation operation) { LogString(nameof(IIsPatternOperation)); diff --git a/src/Test/Utilities/Portable/Compilation/TestOperationVisitor.cs b/src/Test/Utilities/Portable/Compilation/TestOperationVisitor.cs index 919c52496f968..5faf2565d7c8c 100644 --- a/src/Test/Utilities/Portable/Compilation/TestOperationVisitor.cs +++ b/src/Test/Utilities/Portable/Compilation/TestOperationVisitor.cs @@ -1122,6 +1122,28 @@ public override void VisitDeclarationPattern(IDeclarationPatternOperation operat Assert.Empty(operation.Children); } + public override void VisitRecursivePattern(IRecursivePatternOperation operation) + { + Assert.Equal(OperationKind.RecursivePattern, operation.Kind); + + if (!((operation.Syntax as CSharp.Syntax.RecursivePatternSyntax)?.Designation).IsKind(CSharp.SyntaxKind.DiscardDesignation)) + { + Assert.NotNull(operation.DeclaredSymbol); + } + + IEnumerable children = Enumerable.Empty(); + if (!operation.DeconstructionSubpatterns.IsDefault) + { + children = children.Concat(operation.DeconstructionSubpatterns); + } + if (!operation.PropertySubpatterns.IsDefault) + { + children = children.Concat(operation.PropertySubpatterns.Select(x => x.Item2)); + } + + AssertEx.Equal(children, operation.Children); + } + public override void VisitIsPattern(IIsPatternOperation operation) { Assert.Equal(OperationKind.IsPattern, operation.Kind); From f46c4081fae1a17e13aafab5c80ad9f16e6c81ec Mon Sep 17 00:00:00 2001 From: Neal Gafter Date: Thu, 20 Dec 2018 16:05:53 -0800 Subject: [PATCH 2/6] Work in progress --- .../IOperationTests_IIsPatternExpression.cs | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs index a0a092bca5e45..05812d5a51e1e 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests { public partial class IOperationTests : SemanticModelTestBase { - [CompilerTrait(CompilerFeature.IOperation)] + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Patterns)] [Fact, WorkItem(19927, "https://github.com/dotnet/roslyn/issues/19927")] public void TestIsPatternExpression_VarPatternDeclaration() { @@ -36,7 +36,7 @@ void M() VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } - [CompilerTrait(CompilerFeature.IOperation)] + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Patterns)] [Fact, WorkItem(19927, "https://github.com/dotnet/roslyn/issues/19927")] public void TestIsPatternExpression_PrimitiveTypePatternDeclaration() { @@ -63,7 +63,7 @@ void M() VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } - [CompilerTrait(CompilerFeature.IOperation)] + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Patterns)] [Fact, WorkItem(19927, "https://github.com/dotnet/roslyn/issues/19927")] public void TestIsPatternExpression_ReferenceTypePatternDeclaration() { @@ -89,7 +89,7 @@ void M(X x) VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } - [CompilerTrait(CompilerFeature.IOperation)] + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Patterns)] [Fact, WorkItem(19927, "https://github.com/dotnet/roslyn/issues/19927")] public void TestIsPatternExpression_TypeParameterTypePatternDeclaration() { @@ -115,7 +115,7 @@ void M(T x) where T: class VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } - [CompilerTrait(CompilerFeature.IOperation)] + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Patterns)] [Fact, WorkItem(19927, "https://github.com/dotnet/roslyn/issues/19927")] public void TestIsPatternExpression_DynamicTypePatternDeclaration() { @@ -145,7 +145,7 @@ void M(X x) VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } - [CompilerTrait(CompilerFeature.IOperation)] + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Patterns)] [Fact, WorkItem(19927, "https://github.com/dotnet/roslyn/issues/19927")] public void TestIsPatternExpression_ConstantPattern() { @@ -174,7 +174,7 @@ void M() VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } - [CompilerTrait(CompilerFeature.IOperation)] + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Patterns)] [Fact, WorkItem(19927, "https://github.com/dotnet/roslyn/issues/19927")] public void TestIsPatternExpression_ConstantPatternWithConversion() { @@ -206,7 +206,7 @@ void M() VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } - [CompilerTrait(CompilerFeature.IOperation)] + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Patterns)] [Fact, WorkItem(19927, "https://github.com/dotnet/roslyn/issues/19927")] public void TestIsPatternExpression_ConstantPatternWithNoImplicitConversion() { @@ -242,7 +242,7 @@ void M() VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } - [CompilerTrait(CompilerFeature.IOperation)] + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Patterns)] [Fact, WorkItem(19927, "https://github.com/dotnet/roslyn/issues/19927")] public void TestIsPatternExpression_ConstantPatternWithNoValidImplicitOrExplicitConversion() { @@ -278,7 +278,7 @@ void M() VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } - [CompilerTrait(CompilerFeature.IOperation)] + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Patterns)] [Fact, WorkItem(19927, "https://github.com/dotnet/roslyn/issues/19927")] public void TestIsPatternExpression_UndefinedTypeInPatternDeclaration() { @@ -309,7 +309,7 @@ void M() VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } - [CompilerTrait(CompilerFeature.IOperation)] + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Patterns)] [Fact, WorkItem(19927, "https://github.com/dotnet/roslyn/issues/19927")] public void TestIsPatternExpression_InvalidConstantPatternDeclaration() { @@ -342,7 +342,7 @@ void M() VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } - [CompilerTrait(CompilerFeature.IOperation)] + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Patterns)] [Fact, WorkItem(19927, "https://github.com/dotnet/roslyn/issues/19927")] public void TestIsPatternExpression_InvalidTypeInPatternDeclaration() { @@ -373,7 +373,7 @@ void M() VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } - [CompilerTrait(CompilerFeature.IOperation)] + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Patterns)] [Fact, WorkItem(19927, "https://github.com/dotnet/roslyn/issues/19927")] public void TestIsPatternExpression_DuplicateLocalInPatternDeclaration() { @@ -404,7 +404,7 @@ void M() VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } - [CompilerTrait(CompilerFeature.IOperation)] + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Patterns)] [Fact, WorkItem(19927, "https://github.com/dotnet/roslyn/issues/19927")] public void TestIsPatternExpression_InvalidMultipleLocalsInPatternDeclaration() { @@ -453,7 +453,7 @@ void M() VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } - [CompilerTrait(CompilerFeature.IOperation)] + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Patterns)] [Fact, WorkItem(19927, "https://github.com/dotnet/roslyn/issues/19927")] public void TestIsPatternExpression_InvalidConstDeclarationInPatternDeclaration() { @@ -508,7 +508,7 @@ void M() VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } - [CompilerTrait(CompilerFeature.IOperation)] + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Patterns)] [Fact, WorkItem(19927, "https://github.com/dotnet/roslyn/issues/19927")] public void TestIsPatternExpression_InvalidInDefaultParameterInitializer() { @@ -539,7 +539,7 @@ void M(string x = /**/string.Empty is string y/**/) VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } - [CompilerTrait(CompilerFeature.IOperation)] + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Patterns)] [Fact, WorkItem(19927, "https://github.com/dotnet/roslyn/issues/19927")] public void TestIsPatternExpression_InvalidInFieldInitializer() { @@ -564,7 +564,7 @@ class C VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } - [CompilerTrait(CompilerFeature.IOperation)] + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Patterns)] [Fact, WorkItem(19927, "https://github.com/dotnet/roslyn/issues/19927")] public void TestIsPatternExpression_InvalidInConstructorInitializer() { @@ -593,7 +593,7 @@ public C (bool b) VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } - [CompilerTrait(CompilerFeature.IOperation)] + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Patterns)] [Fact, WorkItem(19927, "https://github.com/dotnet/roslyn/issues/19927")] public void TestIsPatternExpression_InvalidInAttributeArgument() { @@ -632,7 +632,7 @@ class C VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } - [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow, CompilerFeature.Patterns)] [Fact] public void IsPattern_NoControlFlow() { @@ -698,7 +698,7 @@ void M(int? x, bool b, int x2, bool b2, (int X, int Y)? x3, bool b3) VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); } - [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow, CompilerFeature.Patterns)] [Fact] public void IsPattern_ControlFlowInValue() { @@ -798,7 +798,7 @@ void M(int? x1, int x2, bool b) VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); } - [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow, CompilerFeature.Patterns)] [Fact] public void IsPattern_RecursivePattern() { @@ -834,7 +834,7 @@ void M((int X, int Y) tuple, bool b) VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } - [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow, CompilerFeature.Patterns)] [Fact] public void IsPattern_ControlFlowInPattern() { @@ -918,7 +918,7 @@ void M(int? x, bool b) VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); } - [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow)] + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow, CompilerFeature.Patterns)] [Fact] public void IsPattern_ControlFlowInValueAndPattern() { From 6224a5c2feb8c9096cf5765f3d8d2ded396e7e88 Mon Sep 17 00:00:00 2001 From: Neal Gafter Date: Thu, 20 Dec 2018 20:00:43 -0800 Subject: [PATCH 3/6] Further work in progress --- .../Operations/CSharpOperationFactory.cs | 42 +++++++----- .../Operations/CSharpOperationNodes.cs | 4 +- .../IOperationTests_IIsPatternExpression.cs | 67 ++++++++++++++++++- .../Operations/ControlFlowGraphBuilder.cs | 14 ++-- .../IDeclarationPatternOperation.cs | 9 ++- .../Portable/Operations/IPatternOperation.cs | 4 ++ .../Operations/IRecursivePatternOperation.cs | 18 +++-- .../Core/Portable/Operations/Operation.cs | 2 +- .../Portable/Operations/OperationCloner.cs | 14 ++-- .../Portable/Operations/OperationNodes.cs | 55 ++++++++------- .../Core/Portable/PublicAPI.Unshipped.txt | 4 +- .../Compilation/ControlFlowGraphVerifier.cs | 1 + .../Compilation/OperationTreeVerifier.cs | 2 +- 13 files changed, 166 insertions(+), 70 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index a88ec0570a13b..b0a71097b25ac 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -1826,7 +1826,8 @@ private IConstantPatternOperation CreateBoundConstantPatternOperation(BoundConst BoundNode value = boundConstantPattern.Value; SyntaxNode syntax = boundConstantPattern.Syntax; bool isImplicit = boundConstantPattern.WasCompilerGenerated; - return new CSharpLazyConstantPatternOperation(this, value, _semanticModel, syntax, isImplicit); + TypeSymbol inputType = boundConstantPattern.InputType; + return new CSharpLazyConstantPatternOperation(inputType, this, value, _semanticModel, syntax, isImplicit); } private IDeclarationPatternOperation CreateBoundDeclarationPatternOperation(BoundDeclarationPattern boundDeclarationPattern) @@ -1837,10 +1838,12 @@ private IDeclarationPatternOperation CreateBoundDeclarationPatternOperation(Boun variable = ((BoundDiscardExpression)boundDeclarationPattern.VariableAccess).ExpressionSymbol; } + ITypeSymbol inputType = boundDeclarationPattern.InputType; bool acceptsNull = boundDeclarationPattern.IsVar; + ITypeSymbol matchedType = acceptsNull ? null : boundDeclarationPattern.DeclaredType.Type; SyntaxNode syntax = boundDeclarationPattern.Syntax; bool isImplicit = boundDeclarationPattern.WasCompilerGenerated; - return new DeclarationPatternOperation(variable, acceptsNull, _semanticModel, syntax, isImplicit); + return new DeclarationPatternOperation(inputType, matchedType, variable, acceptsNull, _semanticModel, syntax, isImplicit); } private IRecursivePatternOperation CreateBoundRecursivePatternOperation(BoundRecursivePattern boundRecursivePattern) @@ -1853,13 +1856,14 @@ private IRecursivePatternOperation CreateBoundRecursivePatternOperation(BoundRec variable = ((BoundDiscardExpression)boundRecursivePattern.VariableAccess).ExpressionSymbol; } + TypeSymbol inputType = boundRecursivePattern.InputType; SyntaxNode syntax = boundRecursivePattern.Syntax; - ImmutableArray deconstructionSubpatterns = boundRecursivePattern.Deconstruction.IsDefault ? default : + ImmutableArray deconstructionSubpatterns = boundRecursivePattern.Deconstruction.SelectAsArray(p => (IPatternOperation)Create(p.Pattern)); - ImmutableArray<(ISymbol, IPatternOperation)> propertySubpatterns = boundRecursivePattern.Properties.IsDefault ? default : + ImmutableArray<(ISymbol, IPatternOperation)> propertySubpatterns = boundRecursivePattern.Properties.SelectAsArray(p => ((ISymbol)p.Symbol, (IPatternOperation)Create(p.Pattern))); bool isImplicit = boundRecursivePattern.WasCompilerGenerated; - return new RecursivePatternOperation(matchedType, deconstructSymbol, deconstructionSubpatterns, propertySubpatterns, variable, _semanticModel, syntax, isImplicit); + return new RecursivePatternOperation(inputType, matchedType, deconstructSymbol, deconstructionSubpatterns, propertySubpatterns, variable, _semanticModel, syntax, isImplicit); } private ISwitchOperation CreateBoundSwitchStatementOperation(BoundSwitchStatement boundSwitchStatement) @@ -1939,12 +1943,14 @@ private IOperation CreateBoundRangeVariableOperation(BoundRangeVariable boundRan private IOperation CreateBoundDiscardExpressionOperation(BoundDiscardExpression boundNode) { - return new DiscardOperation((IDiscardSymbol)boundNode.ExpressionSymbol, - _semanticModel, - boundNode.Syntax, - boundNode.Type, - ConvertToOptional(boundNode.ConstantValue), - isImplicit: boundNode.WasCompilerGenerated); + return new DiscardOperation( + inputType: null, + (IDiscardSymbol)boundNode.ExpressionSymbol, + _semanticModel, + boundNode.Syntax, + boundNode.Type, + ConvertToOptional(boundNode.ConstantValue), + isImplicit: boundNode.WasCompilerGenerated); } private IOperation CreateFromEndIndexExpressionOperation(BoundFromEndIndexExpression boundIndex) @@ -1975,12 +1981,14 @@ private IOperation CreateRangeExpressionOperation(BoundRangeExpression boundRang private IOperation CreateBoundDiscardPatternOperation(BoundDiscardPattern boundNode) { - return new DiscardOperation(boundNode.DiscardSymbol, - _semanticModel, - boundNode.Syntax, - boundNode.InputType, - null, - isImplicit: boundNode.WasCompilerGenerated); + return new DiscardOperation( + boundNode.InputType, + boundNode.DiscardSymbol, + _semanticModel, + boundNode.Syntax, + boundNode.InputType, + null, + isImplicit: boundNode.WasCompilerGenerated); } } } diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationNodes.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationNodes.cs index d1560cddd5a46..c589679281392 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationNodes.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationNodes.cs @@ -1469,8 +1469,8 @@ internal sealed class CSharpLazyConstantPatternOperation : LazyConstantPatternOp private readonly CSharpOperationFactory _operationFactory; private readonly BoundNode _value; - internal CSharpLazyConstantPatternOperation(CSharpOperationFactory operationFactory, BoundNode value, SemanticModel semanticModel, SyntaxNode syntax, bool isImplicit) : - base(semanticModel, syntax, isImplicit) + internal CSharpLazyConstantPatternOperation(ITypeSymbol inputType, CSharpOperationFactory operationFactory, BoundNode value, SemanticModel semanticModel, SyntaxNode syntax, bool isImplicit) : + base(inputType, semanticModel, syntax, isImplicit) { _operationFactory = operationFactory; _value = value; diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs index 05812d5a51e1e..1d2cdc5bb810f 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs @@ -634,7 +634,7 @@ class C [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow, CompilerFeature.Patterns)] [Fact] - public void IsPattern_NoControlFlow() + public void IsPattern_NoControlFlow_01() { string source = @" class C @@ -643,7 +643,70 @@ void M(int? x, bool b, int x2, bool b2, (int X, int Y)? x3, bool b3) /**/{ b = x is var y; b2 = x2 is 1; - b3 = x3 is (1, 2) { Item1: var z } p; + }/**/ +} +"; + string expectedFlowGraph = @" +Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} + +.locals {R1} +{ + Locals: [System.Int32? y] + Block[B1] - Block + Predecessors: [B0] + Statements (2) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = x is var y;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = x is var y') + Left: + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') + Right: + IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean) (Syntax: 'x is var y') + Expression: + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32?) (Syntax: 'x') + Pattern: + IDeclarationPatternOperation (Declared Symbol: System.Int32? y, AcceptsNull: True) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'var y') + + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b2 = x2 is 1;') + Expression: + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b2 = x2 is 1') + Left: + IParameterReferenceOperation: b2 (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b2') + Right: + IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean) (Syntax: 'x2 is 1') + Expression: + IParameterReferenceOperation: x2 (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x2') + Pattern: + IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: '1') + Value: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + + Next (Regular) Block[B2] + Leaving: {R1} +} + +Block[B2] - Exit + Predecessors: [B1] + Statements (0) +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); + } + + [CompilerTrait(CompilerFeature.IOperation, CompilerFeature.Dataflow, CompilerFeature.Patterns)] + [Fact] + public void IsPattern_NoControlFlow_02() + { + string source = @" +class C +{ + void M((int X, int Y)? x, bool b) + /**/{ + b = x is (1, 2) { Item1: var z } p; }/**/ } "; diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index 0da1228e5aa95..7477487101eeb 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -6377,7 +6377,9 @@ public override IOperation VisitIncrementOrDecrement(IIncrementOrDecrementOperat public override IOperation VisitDiscardOperation(IDiscardOperation operation, int? captureIdForResult) { - return new DiscardOperation(operation.DiscardSymbol, semanticModel: null, operation.Syntax, operation.Type, operation.ConstantValue, IsImplicit(operation)); + return new DiscardOperation( + operation is IPatternOperation pat ? pat.InputType : operation.DiscardSymbol?.Type, + operation.DiscardSymbol, semanticModel: null, operation.Syntax, operation.Type, operation.ConstantValue, IsImplicit(operation)); } public override IOperation VisitOmittedArgument(IOmittedArgumentOperation operation, int? captureIdForResult) @@ -6549,21 +6551,23 @@ public override IOperation VisitTranslatedQuery(ITranslatedQueryOperation operat public override IOperation VisitConstantPattern(IConstantPatternOperation operation, int? captureIdForResult) { - return new ConstantPatternOperation(Visit(operation.Value), semanticModel: null, + return new ConstantPatternOperation(operation.InputType, Visit(operation.Value), semanticModel: null, syntax: operation.Syntax, isImplicit: IsImplicit(operation)); } public override IOperation VisitDeclarationPattern(IDeclarationPatternOperation operation, int? captureIdForResult) { - return new DeclarationPatternOperation(operation.DeclaredSymbol, operation.AcceptsNull, semanticModel: null, + return new DeclarationPatternOperation( + inputType: operation.InputType, matchedType: operation.MatchedType, operation.DeclaredSymbol, operation.MatchesNull, semanticModel: null, operation.Syntax, IsImplicit(operation)); } public override IOperation VisitRecursivePattern(IRecursivePatternOperation operation, int? argument) { return new RecursivePatternOperation( - operation.MatchedType, operation.DeconstructSymbol, operation.DeconstructionSubpatterns, operation.PropertySubpatterns, - operation.DeclaredSymbol, operation.SemanticModel, operation.Syntax, operation.IsImplicit); + inputType: operation.InputType, matchedType: operation.MatchedType, operation.DeconstructSymbol, + operation.DeconstructionSubpatterns, operation.PropertySubpatterns, + operation.DeclaredSymbol, semanticModel: null, operation.Syntax, operation.IsImplicit); } public override IOperation VisitDelegateCreation(IDelegateCreationOperation operation, int? captureIdForResult) diff --git a/src/Compilers/Core/Portable/Operations/IDeclarationPatternOperation.cs b/src/Compilers/Core/Portable/Operations/IDeclarationPatternOperation.cs index f2190a73bd9ed..c051677055ce4 100644 --- a/src/Compilers/Core/Portable/Operations/IDeclarationPatternOperation.cs +++ b/src/Compilers/Core/Portable/Operations/IDeclarationPatternOperation.cs @@ -15,15 +15,20 @@ namespace Microsoft.CodeAnalysis.Operations /// public interface IDeclarationPatternOperation : IPatternOperation { + /// + /// The type explicitly specified, or null if it was inferred (e.g. using var in C#). + /// + ITypeSymbol MatchedType { get; } + /// /// True if the pattern is of a form that accepts null. /// For example, in C# the pattern `var x` will match a null input, /// while the pattern `string x` will not. /// - bool AcceptsNull { get; } + bool MatchesNull { get; } /// - /// Symbol declared by the pattern. + /// Symbol declared by the pattern, if any. /// ISymbol DeclaredSymbol { get; } } diff --git a/src/Compilers/Core/Portable/Operations/IPatternOperation.cs b/src/Compilers/Core/Portable/Operations/IPatternOperation.cs index 2f67bb956e9fe..8b50d3ba90884 100644 --- a/src/Compilers/Core/Portable/Operations/IPatternOperation.cs +++ b/src/Compilers/Core/Portable/Operations/IPatternOperation.cs @@ -15,6 +15,10 @@ namespace Microsoft.CodeAnalysis.Operations /// public interface IPatternOperation : IOperation { + /// + /// The input type to the pattern-matching operation. + /// + ITypeSymbol InputType { get; } } } diff --git a/src/Compilers/Core/Portable/Operations/IRecursivePatternOperation.cs b/src/Compilers/Core/Portable/Operations/IRecursivePatternOperation.cs index a0dc2f76a9278..c7e2e734ad34c 100644 --- a/src/Compilers/Core/Portable/Operations/IRecursivePatternOperation.cs +++ b/src/Compilers/Core/Portable/Operations/IRecursivePatternOperation.cs @@ -14,24 +14,28 @@ namespace Microsoft.CodeAnalysis.Operations public interface IRecursivePatternOperation : IPatternOperation { /// - /// The (explicit or implicit) type accepted for the recursive pattern. + /// The explicit type accepted for the recursive pattern, or null if none was specified. /// ITypeSymbol MatchedType { get; } + /// - /// The Deconstruct symbol, if any, used for the deconstruction subpatterns. + /// The symbol, if any, used for the fetching values for subpatterns. This is either a Deconstruct + /// method, the type System.Runtime.CompilerServices.ITuple, or null (for example, in + /// error cases or when matching a tuple type). /// ISymbol DeconstructSymbol { get; } + /// - /// If there is a deconstruction or positional subpattern, this contains the patterns contained within it. - /// If there is no deconstruction subpattern, this is a default immutable array. + /// This contains the patterns contained within a deconstruction or positional subpattern. /// ImmutableArray DeconstructionSubpatterns { get; } + /// - /// If there is a property subpattern, this contains the - /// / pairs within it. - /// If there is no property subpattern, this is a default immutable array. + /// This contains the + /// / pairs within a property subpattern. /// ImmutableArray<(ISymbol, IPatternOperation)> PropertySubpatterns { get; } + /// /// Symbol declared by the pattern. /// diff --git a/src/Compilers/Core/Portable/Operations/Operation.cs b/src/Compilers/Core/Portable/Operations/Operation.cs index 892c1d3a02577..604ab7e8a34bb 100644 --- a/src/Compilers/Core/Portable/Operations/Operation.cs +++ b/src/Compilers/Core/Portable/Operations/Operation.cs @@ -21,7 +21,7 @@ internal abstract class Operation : IOperation protected static readonly IArrayInitializerOperation s_unsetArrayInitializer = new ArrayInitializerOperation(ImmutableArray.Empty, null, null, default, isImplicit: true); protected static readonly IEventReferenceOperation s_unsetEventReference = new EventReferenceOperation(null, null, null, null, null, default, isImplicit: true); protected static readonly IObjectOrCollectionInitializerOperation s_unsetObjectOrCollectionInitializer = new ObjectOrCollectionInitializerOperation(ImmutableArray.Empty, null, null, null, default, isImplicit: true); - protected static readonly IPatternOperation s_unsetPattern = new ConstantPatternOperation(null, null, null, isImplicit: true); + protected static readonly IPatternOperation s_unsetPattern = new ConstantPatternOperation(null, null, null, null, isImplicit: true); protected static readonly IVariableDeclarationGroupOperation s_unsetVariableDeclarationGroup = new VariableDeclarationGroupOperation(ImmutableArray.Empty, null, null, null, default, isImplicit: true); protected static readonly IVariableInitializerOperation s_unsetVariableInitializer = new VariableInitializerOperation(null, null, null, null, default, isImplicit: true); private readonly SemanticModel _owningSemanticModelOpt; diff --git a/src/Compilers/Core/Portable/Operations/OperationCloner.cs b/src/Compilers/Core/Portable/Operations/OperationCloner.cs index a4d193255d50f..4d18c93582934 100644 --- a/src/Compilers/Core/Portable/Operations/OperationCloner.cs +++ b/src/Compilers/Core/Portable/Operations/OperationCloner.cs @@ -525,17 +525,21 @@ public override IOperation VisitIsPattern(IIsPatternOperation operation, object public override IOperation VisitConstantPattern(IConstantPatternOperation operation, object argument) { - return new ConstantPatternOperation(Visit(operation.Value), ((Operation)operation).OwningSemanticModel, operation.Syntax, operation.IsImplicit); + return new ConstantPatternOperation(operation.InputType, Visit(operation.Value), ((Operation)operation).OwningSemanticModel, operation.Syntax, operation.IsImplicit); } public override IOperation VisitDeclarationPattern(IDeclarationPatternOperation operation, object argument) { - return new DeclarationPatternOperation(operation.DeclaredSymbol, operation.AcceptsNull, ((Operation)operation).OwningSemanticModel, operation.Syntax, operation.IsImplicit); + return new DeclarationPatternOperation( + operation.InputType, operation.MatchedType, operation.DeclaredSymbol, operation.MatchesNull, + ((Operation)operation).OwningSemanticModel, operation.Syntax, operation.IsImplicit); } public override IOperation VisitRecursivePattern(IRecursivePatternOperation operation, object argument) { - return new RecursivePatternOperation(operation.MatchedType, operation.DeconstructSymbol, VisitArray(operation.DeconstructionSubpatterns), VisitArray(operation.PropertySubpatterns), operation.DeclaredSymbol, ((Operation)operation).OwningSemanticModel, operation.Syntax, operation.IsImplicit); + return new RecursivePatternOperation( + operation.InputType, operation.MatchedType, operation.DeconstructSymbol, VisitArray(operation.DeconstructionSubpatterns), + VisitArray(operation.PropertySubpatterns), operation.DeclaredSymbol, ((Operation)operation).OwningSemanticModel, operation.Syntax, operation.IsImplicit); } public override IOperation VisitPatternCaseClause(IPatternCaseClauseOperation operation, object argument) @@ -570,7 +574,9 @@ public override IOperation VisitMethodBodyOperation(IMethodBodyOperation operati public override IOperation VisitDiscardOperation(IDiscardOperation operation, object argument) { - return new DiscardOperation(operation.DiscardSymbol, ((Operation)operation).OwningSemanticModel, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit); + return new DiscardOperation( + operation is IPatternOperation pat ? pat.InputType : operation.DiscardSymbol?.Type, + operation.DiscardSymbol, ((Operation)operation).OwningSemanticModel, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit); } public override IOperation VisitFlowCapture(IFlowCaptureOperation operation, object argument) diff --git a/src/Compilers/Core/Portable/Operations/OperationNodes.cs b/src/Compilers/Core/Portable/Operations/OperationNodes.cs index 82813101b8538..1dc6aaeeca474 100644 --- a/src/Compilers/Core/Portable/Operations/OperationNodes.cs +++ b/src/Compilers/Core/Portable/Operations/OperationNodes.cs @@ -7835,9 +7835,10 @@ public override IBlockOperation IgnoredBody /// internal abstract partial class BaseConstantPatternOperation : Operation, IConstantPatternOperation { - protected BaseConstantPatternOperation(SemanticModel semanticModel, SyntaxNode syntax, bool isImplicit) : + protected BaseConstantPatternOperation(ITypeSymbol inputType, SemanticModel semanticModel, SyntaxNode syntax, bool isImplicit) : base(OperationKind.ConstantPattern, semanticModel, syntax, type: default, constantValue: default, isImplicit) { + InputType = inputType; } public override IEnumerable Children @@ -7854,6 +7855,9 @@ public override IEnumerable Children /// Constant value of the pattern. /// public abstract IOperation Value { get; } + + public ITypeSymbol InputType { get; } + public override void Accept(OperationVisitor visitor) { visitor.VisitConstantPattern(this); @@ -7869,8 +7873,8 @@ public override TResult Accept(OperationVisitor internal sealed partial class ConstantPatternOperation : BaseConstantPatternOperation, IConstantPatternOperation { - public ConstantPatternOperation(IOperation value, SemanticModel semanticModel, SyntaxNode syntax, bool isImplicit) : - base(semanticModel, syntax, isImplicit) + public ConstantPatternOperation(ITypeSymbol inputType, IOperation value, SemanticModel semanticModel, SyntaxNode syntax, bool isImplicit) : + base(inputType, semanticModel, syntax, isImplicit) { Value = SetParentOperation(value, this); } @@ -7885,8 +7889,8 @@ internal abstract class LazyConstantPatternOperation : BaseConstantPatternOperat { private IOperation _lazyValueInterlocked = s_unset; - public LazyConstantPatternOperation(SemanticModel semanticModel, SyntaxNode syntax, bool isImplicit) : - base(semanticModel, syntax, isImplicit) + public LazyConstantPatternOperation(ITypeSymbol inputType, SemanticModel semanticModel, SyntaxNode syntax, bool isImplicit) : + base(inputType, semanticModel, syntax, isImplicit) { } @@ -7913,13 +7917,15 @@ public override IOperation Value /// internal sealed partial class DeclarationPatternOperation : Operation, IDeclarationPatternOperation { - public DeclarationPatternOperation(ISymbol declaredSymbol, bool acceptsNull, SemanticModel semanticModel, SyntaxNode syntax, bool isImplicit) : + public DeclarationPatternOperation(ITypeSymbol inputType, ITypeSymbol matchedType, ISymbol declaredSymbol, bool matchesNull, SemanticModel semanticModel, SyntaxNode syntax, bool isImplicit) : base(OperationKind.DeclarationPattern, semanticModel, syntax, type: default, constantValue: default, isImplicit) { - AcceptsNull = acceptsNull; + InputType = inputType; + MatchedType = matchedType; DeclaredSymbol = declaredSymbol; + MatchesNull = matchesNull; } - public bool AcceptsNull { get; } + public bool MatchesNull { get; } /// /// Symbol declared by the pattern. /// @@ -7931,6 +7937,11 @@ public override IEnumerable Children return Array.Empty(); } } + + public ITypeSymbol MatchedType { get; } + + public ITypeSymbol InputType { get; } + public override void Accept(OperationVisitor visitor) { visitor.VisitDeclarationPattern(this); @@ -7946,37 +7957,21 @@ public override TResult Accept(OperationVisitor internal sealed partial class RecursivePatternOperation : Operation, IRecursivePatternOperation { - public RecursivePatternOperation(ITypeSymbol matchedType, ISymbol deconstructSymbol, ImmutableArray deconstructionSubpatterns, ImmutableArray<(ISymbol, IPatternOperation)> propertySubpatterns, ISymbol declaredSymbol, SemanticModel semanticModel, SyntaxNode syntax, bool isImplicit) : + public RecursivePatternOperation(ITypeSymbol inputType, ITypeSymbol matchedType, ISymbol deconstructSymbol, ImmutableArray deconstructionSubpatterns, ImmutableArray<(ISymbol, IPatternOperation)> propertySubpatterns, ISymbol declaredSymbol, SemanticModel semanticModel, SyntaxNode syntax, bool isImplicit) : base(OperationKind.RecursivePattern, semanticModel, syntax, type: default, constantValue: default, isImplicit) { + InputType = inputType; MatchedType = matchedType; DeconstructSymbol = deconstructSymbol; DeconstructionSubpatterns = deconstructionSubpatterns; PropertySubpatterns = propertySubpatterns; DeclaredSymbol = declaredSymbol; } - /// - /// The (explicit or implicit) type accepted for the recursive pattern. - /// + public ITypeSymbol InputType { get; } public ITypeSymbol MatchedType { get; } - /// - /// The Deconstruct symbol, if any, used for the deconstruction subpatterns. - /// public ISymbol DeconstructSymbol { get; } - /// - /// If there is a positional subpattern, this contains the patterns contained within it. - /// If there is no positional subpattern, this is a default immutable array. - /// public ImmutableArray DeconstructionSubpatterns { get; } - /// - /// If there is a property subpattern, this contains the - /// / pairs within it. - /// If there is no property subpattern, this is a default immutable array. - /// public ImmutableArray<(ISymbol, IPatternOperation)> PropertySubpatterns { get; } - /// - /// Symbol declared by the pattern. - /// public ISymbol DeclaredSymbol { get; } public override IEnumerable Children { @@ -7998,6 +7993,7 @@ public override IEnumerable Children } } } + public override void Accept(OperationVisitor visitor) { visitor.VisitRecursivePattern(this); @@ -8874,9 +8870,10 @@ public override IBlockOperation ExpressionBody internal sealed class DiscardOperation : Operation, IDiscardOperation, IPatternOperation { - public DiscardOperation(IDiscardSymbol discardSymbol, SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional constantValue, bool isImplicit) : + public DiscardOperation(ITypeSymbol inputType, IDiscardSymbol discardSymbol, SemanticModel semanticModel, SyntaxNode syntax, ITypeSymbol type, Optional constantValue, bool isImplicit) : base(OperationKind.Discard, semanticModel, syntax, type, constantValue, isImplicit) { + InputType = inputType; DiscardSymbol = discardSymbol; } @@ -8884,6 +8881,8 @@ public DiscardOperation(IDiscardSymbol discardSymbol, SemanticModel semanticMode public override IEnumerable Children => Array.Empty(); + public ITypeSymbol InputType { get; } + public override void Accept(OperationVisitor visitor) { visitor.VisitDiscardOperation(this); diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 8bf3ea25630d4..71653cbb6bd59 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -6,9 +6,11 @@ Microsoft.CodeAnalysis.OperationKind.FromEndIndex = 100 -> Microsoft.CodeAnalysi Microsoft.CodeAnalysis.OperationKind.MethodBody = 88 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.OperationKind.Range = 99 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.OperationKind.RecursivePattern = 103 -> Microsoft.CodeAnalysis.OperationKind -Microsoft.CodeAnalysis.Operations.IDeclarationPatternOperation.AcceptsNull.get -> bool Microsoft.CodeAnalysis.OperationKind.TupleBinary = 87 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.OperationKind.Unary = 31 -> Microsoft.CodeAnalysis.OperationKind +Microsoft.CodeAnalysis.Operations.IDeclarationPatternOperation.MatchedType.get -> Microsoft.CodeAnalysis.ITypeSymbol +Microsoft.CodeAnalysis.Operations.IDeclarationPatternOperation.MatchesNull.get -> bool +Microsoft.CodeAnalysis.Operations.IPatternOperation.InputType.get -> Microsoft.CodeAnalysis.ITypeSymbol Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation.DeclaredSymbol.get -> Microsoft.CodeAnalysis.ISymbol Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation.DeconstructSymbol.get -> Microsoft.CodeAnalysis.ISymbol diff --git a/src/Test/Utilities/Portable/Compilation/ControlFlowGraphVerifier.cs b/src/Test/Utilities/Portable/Compilation/ControlFlowGraphVerifier.cs index 0be1fe4d4092a..02a540ba12d03 100644 --- a/src/Test/Utilities/Portable/Compilation/ControlFlowGraphVerifier.cs +++ b/src/Test/Utilities/Portable/Compilation/ControlFlowGraphVerifier.cs @@ -1782,6 +1782,7 @@ propertyReference.Parent is ISimpleAssignmentOperation simpleAssignment && case OperationKind.ReDimClause: case OperationKind.FromEndIndex: case OperationKind.Range: + case OperationKind.RecursivePattern: return true; } diff --git a/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs b/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs index 2ab4bfc601fb6..94a9a41b2f3e1 100644 --- a/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs +++ b/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs @@ -1738,7 +1738,7 @@ public override void VisitDeclarationPattern(IDeclarationPatternOperation operat { LogString(nameof(IDeclarationPatternOperation)); LogSymbol(operation.DeclaredSymbol, " (Declared Symbol"); - LogConstant((object)operation.AcceptsNull, ", AcceptsNull"); + LogConstant((object)operation.MatchesNull, ", AcceptsNull"); LogString(")"); LogCommonPropertiesAndNewLine(operation); } From d8305e1407bc40c22f8c1eb094a823bc379567d2 Mon Sep 17 00:00:00 2001 From: Neal Gafter Date: Fri, 21 Dec 2018 15:49:18 -0800 Subject: [PATCH 4/6] Implement IRecursivePatternOperation --- .../Operations/CSharpOperationFactory.cs | 10 +- .../Operations/CSharpOperationNodes.cs | 36 ++++++ .../IOperationTests_IIsPatternExpression.cs | 42 +++---- .../Operations/ControlFlowGraphBuilder.cs | 12 +- .../Operations/IRecursivePatternOperation.cs | 3 +- .../Portable/Operations/OperationCloner.cs | 11 +- .../Portable/Operations/OperationNodes.cs | 117 ++++++++++++++++-- .../Compilation/OperationTreeVerifier.cs | 1 - 8 files changed, 182 insertions(+), 50 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index b0a71097b25ac..dd228d6f19561 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -1848,6 +1848,7 @@ private IDeclarationPatternOperation CreateBoundDeclarationPatternOperation(Boun private IRecursivePatternOperation CreateBoundRecursivePatternOperation(BoundRecursivePattern boundRecursivePattern) { + bool isImplicit = boundRecursivePattern.WasCompilerGenerated; ITypeSymbol matchedType = boundRecursivePattern.DeclaredType?.Type ?? boundRecursivePattern.InputType; ISymbol variable = boundRecursivePattern.Variable; ISymbol deconstructSymbol = boundRecursivePattern.DeconstructMethod; @@ -1858,12 +1859,9 @@ private IRecursivePatternOperation CreateBoundRecursivePatternOperation(BoundRec TypeSymbol inputType = boundRecursivePattern.InputType; SyntaxNode syntax = boundRecursivePattern.Syntax; - ImmutableArray deconstructionSubpatterns = - boundRecursivePattern.Deconstruction.SelectAsArray(p => (IPatternOperation)Create(p.Pattern)); - ImmutableArray<(ISymbol, IPatternOperation)> propertySubpatterns = - boundRecursivePattern.Properties.SelectAsArray(p => ((ISymbol)p.Symbol, (IPatternOperation)Create(p.Pattern))); - bool isImplicit = boundRecursivePattern.WasCompilerGenerated; - return new RecursivePatternOperation(inputType, matchedType, deconstructSymbol, deconstructionSubpatterns, propertySubpatterns, variable, _semanticModel, syntax, isImplicit); + return new CSharpLazyRecursivePatternOperation( + this, inputType, matchedType, deconstructSymbol, boundRecursivePattern.Deconstruction, + boundRecursivePattern.Properties, variable, _semanticModel, syntax, isImplicit); } private ISwitchOperation CreateBoundSwitchStatementOperation(BoundSwitchStatement boundSwitchStatement) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationNodes.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationNodes.cs index c589679281392..84cf5ddf362c6 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationNodes.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationNodes.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp; @@ -1482,6 +1483,41 @@ protected override IOperation CreateValue() } } + /// + /// Represents a C# recursive pattern. + /// + internal sealed partial class CSharpLazyRecursivePatternOperation : LazyRecursivePatternOperation + { + private readonly CSharpOperationFactory _operationFactory; + private readonly ImmutableArray _deconstructionSubpatterns; + private readonly ImmutableArray _propertySubpatterns; + public CSharpLazyRecursivePatternOperation( + CSharpOperationFactory operationFactory, + ITypeSymbol inputType, + ITypeSymbol matchedType, + ISymbol deconstructSymbol, + ImmutableArray deconstructionSubpatterns, + ImmutableArray propertySubpatterns, + ISymbol declaredSymbol, + SemanticModel semanticModel, + SyntaxNode syntax, + bool isImplicit) + : base(inputType, matchedType, deconstructSymbol, declaredSymbol, semanticModel, syntax, isImplicit) + { + _operationFactory = operationFactory; + _deconstructionSubpatterns = deconstructionSubpatterns; + _propertySubpatterns = propertySubpatterns; + } + public override ImmutableArray CreateDeconstructionSubpatterns() + { + return _deconstructionSubpatterns.SelectAsArray(p => (IPatternOperation)_operationFactory.Create(p.Pattern)); + } + public override ImmutableArray<(ISymbol, IPatternOperation)> CreatePropertySubpatterns() + { + return _propertySubpatterns.SelectAsArray(p => ((ISymbol)p.Symbol, (IPatternOperation)_operationFactory.Create(p.Pattern))); + } + } + internal sealed class CSharpLazyPatternCaseClauseOperation : LazyPatternCaseClauseOperation { private readonly CSharpOperationFactory _operationFactory; diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs index 1d2cdc5bb810f..b06aca486061f 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs @@ -718,35 +718,31 @@ void M((int X, int Y)? x, bool b) .locals {R1} { - Locals: [System.Int32? y] + Locals: [(System.Int32 X, System.Int32 Y) p] [System.Int32 z] Block[B1] - Block Predecessors: [B0] - Statements (2) - IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = x is var y;') + Statements (1) + IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b = x is (1 ... var z } p;') Expression: - ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = x is var y') + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b = x is (1 ... : var z } p') Left: IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b') Right: - IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean) (Syntax: 'x is var y') - Expression: - IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32?) (Syntax: 'x') - Pattern: - IDeclarationPatternOperation (Declared Symbol: System.Int32? y, AcceptsNull: True) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'var y') - - IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null) (Syntax: 'b2 = x2 is 1;') - Expression: - ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Boolean) (Syntax: 'b2 = x2 is 1') - Left: - IParameterReferenceOperation: b2 (OperationKind.ParameterReference, Type: System.Boolean) (Syntax: 'b2') - Right: - IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean) (Syntax: 'x2 is 1') + IIsPatternOperation (OperationKind.IsPattern, Type: System.Boolean) (Syntax: 'x is (1, 2) ... : var z } p') Expression: - IParameterReferenceOperation: x2 (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x2') + IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: (System.Int32 X, System.Int32 Y)?) (Syntax: 'x') Pattern: - IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: '1') - Value: - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + IRecursivePatternOperation (Declared Symbol: (System.Int32 X, System.Int32 Y) p, MatchedType: (System.Int32 X, System.Int32 Y)?, Deconstruct Symbol: null) + Patterns (2): + IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: '1') + Value: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + IConstantPatternOperation (OperationKind.ConstantPattern, Type: null) (Syntax: '2') + Value: + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') + Properties (1): + Matched Property: System.Int32 (System.Int32 X, System.Int32 Y).Item1, Pattern: + IDeclarationPatternOperation (Declared Symbol: System.Int32 z, AcceptsNull: True) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'var z') Next (Regular) Block[B2] Leaving: {R1} @@ -754,8 +750,7 @@ void M((int X, int Y)? x, bool b) Block[B2] - Exit Predecessors: [B1] - Statements (0) -"; + Statements (0)"; var expectedDiagnostics = DiagnosticDescription.None; VerifyFlowGraphAndDiagnosticsForTest(source, expectedFlowGraph, expectedDiagnostics); @@ -890,7 +885,6 @@ void M((int X, int Y) tuple, bool b) Properties (1): Matched Property: System.Int32 (System.Int32 X, System.Int32 Y).Item1, Pattern: IDeclarationPatternOperation (Declared Symbol: System.Int32 x, AcceptsNull: False) (OperationKind.DeclarationPattern, Type: null) (Syntax: 'int x') - (OperationKind.DeclarationPattern, Type: null) (Syntax: '(1, 2) { It ... : int x } y') "; var expectedDiagnostics = DiagnosticDescription.None; diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index 7477487101eeb..bb597ae7e50f2 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -6565,9 +6565,15 @@ public override IOperation VisitDeclarationPattern(IDeclarationPatternOperation public override IOperation VisitRecursivePattern(IRecursivePatternOperation operation, int? argument) { return new RecursivePatternOperation( - inputType: operation.InputType, matchedType: operation.MatchedType, operation.DeconstructSymbol, - operation.DeconstructionSubpatterns, operation.PropertySubpatterns, - operation.DeclaredSymbol, semanticModel: null, operation.Syntax, operation.IsImplicit); + inputType: operation.InputType, + matchedType: operation.MatchedType, + operation.DeconstructSymbol, + operation.DeconstructionSubpatterns.SelectAsArray(p => (IPatternOperation)Visit(p)), + operation.PropertySubpatterns.SelectAsArray(p => (p.Item1, (IPatternOperation)Visit(p.Item2))), + operation.DeclaredSymbol, + semanticModel: null, + operation.Syntax, + operation.IsImplicit); } public override IOperation VisitDelegateCreation(IDelegateCreationOperation operation, int? captureIdForResult) diff --git a/src/Compilers/Core/Portable/Operations/IRecursivePatternOperation.cs b/src/Compilers/Core/Portable/Operations/IRecursivePatternOperation.cs index c7e2e734ad34c..0b39ce8307285 100644 --- a/src/Compilers/Core/Portable/Operations/IRecursivePatternOperation.cs +++ b/src/Compilers/Core/Portable/Operations/IRecursivePatternOperation.cs @@ -31,8 +31,7 @@ public interface IRecursivePatternOperation : IPatternOperation ImmutableArray DeconstructionSubpatterns { get; } /// - /// This contains the - /// / pairs within a property subpattern. + /// This contains the (symbol, property) pairs within a property subpattern. /// ImmutableArray<(ISymbol, IPatternOperation)> PropertySubpatterns { get; } diff --git a/src/Compilers/Core/Portable/Operations/OperationCloner.cs b/src/Compilers/Core/Portable/Operations/OperationCloner.cs index 4d18c93582934..93d2450623ae1 100644 --- a/src/Compilers/Core/Portable/Operations/OperationCloner.cs +++ b/src/Compilers/Core/Portable/Operations/OperationCloner.cs @@ -538,8 +538,15 @@ public override IOperation VisitDeclarationPattern(IDeclarationPatternOperation public override IOperation VisitRecursivePattern(IRecursivePatternOperation operation, object argument) { return new RecursivePatternOperation( - operation.InputType, operation.MatchedType, operation.DeconstructSymbol, VisitArray(operation.DeconstructionSubpatterns), - VisitArray(operation.PropertySubpatterns), operation.DeclaredSymbol, ((Operation)operation).OwningSemanticModel, operation.Syntax, operation.IsImplicit); + operation.InputType, + operation.MatchedType, + operation.DeconstructSymbol, + VisitArray(operation.DeconstructionSubpatterns), + VisitArray(operation.PropertySubpatterns), + operation.DeclaredSymbol, + ((Operation)operation).OwningSemanticModel, + operation.Syntax, + operation.IsImplicit); } public override IOperation VisitPatternCaseClause(IPatternCaseClauseOperation operation, object argument) diff --git a/src/Compilers/Core/Portable/Operations/OperationNodes.cs b/src/Compilers/Core/Portable/Operations/OperationNodes.cs index 1dc6aaeeca474..2a60d5a7deba4 100644 --- a/src/Compilers/Core/Portable/Operations/OperationNodes.cs +++ b/src/Compilers/Core/Portable/Operations/OperationNodes.cs @@ -7917,8 +7917,15 @@ public override IOperation Value /// internal sealed partial class DeclarationPatternOperation : Operation, IDeclarationPatternOperation { - public DeclarationPatternOperation(ITypeSymbol inputType, ITypeSymbol matchedType, ISymbol declaredSymbol, bool matchesNull, SemanticModel semanticModel, SyntaxNode syntax, bool isImplicit) : - base(OperationKind.DeclarationPattern, semanticModel, syntax, type: default, constantValue: default, isImplicit) + public DeclarationPatternOperation( + ITypeSymbol inputType, + ITypeSymbol matchedType, + ISymbol declaredSymbol, + bool matchesNull, + SemanticModel semanticModel, + SyntaxNode syntax, + bool isImplicit) + : base(OperationKind.DeclarationPattern, semanticModel, syntax, type: default, constantValue: default, isImplicit) { InputType = inputType; MatchedType = matchedType; @@ -7952,26 +7959,27 @@ public override TResult Accept(OperationVisitor - /// Represents a C# recursive pattern. - /// - internal sealed partial class RecursivePatternOperation : Operation, IRecursivePatternOperation + internal abstract partial class BaseRecursivePatternOperation: Operation, IRecursivePatternOperation { - public RecursivePatternOperation(ITypeSymbol inputType, ITypeSymbol matchedType, ISymbol deconstructSymbol, ImmutableArray deconstructionSubpatterns, ImmutableArray<(ISymbol, IPatternOperation)> propertySubpatterns, ISymbol declaredSymbol, SemanticModel semanticModel, SyntaxNode syntax, bool isImplicit) : - base(OperationKind.RecursivePattern, semanticModel, syntax, type: default, constantValue: default, isImplicit) + public BaseRecursivePatternOperation( + ITypeSymbol inputType, + ITypeSymbol matchedType, + ISymbol deconstructSymbol, + ISymbol declaredSymbol, SemanticModel semanticModel, + SyntaxNode syntax, + bool isImplicit) + : base(OperationKind.RecursivePattern, semanticModel, syntax, type: default, constantValue: default, isImplicit) { InputType = inputType; MatchedType = matchedType; DeconstructSymbol = deconstructSymbol; - DeconstructionSubpatterns = deconstructionSubpatterns; - PropertySubpatterns = propertySubpatterns; DeclaredSymbol = declaredSymbol; } public ITypeSymbol InputType { get; } public ITypeSymbol MatchedType { get; } public ISymbol DeconstructSymbol { get; } - public ImmutableArray DeconstructionSubpatterns { get; } - public ImmutableArray<(ISymbol, IPatternOperation)> PropertySubpatterns { get; } + public abstract ImmutableArray DeconstructionSubpatterns { get; } + public abstract ImmutableArray<(ISymbol, IPatternOperation)> PropertySubpatterns { get; } public ISymbol DeclaredSymbol { get; } public override IEnumerable Children { @@ -8004,6 +8012,91 @@ public override TResult Accept(OperationVisitor + /// Represents a C# recursive pattern. + /// + internal sealed partial class RecursivePatternOperation : BaseRecursivePatternOperation + { + public RecursivePatternOperation( + ITypeSymbol inputType, + ITypeSymbol matchedType, + ISymbol deconstructSymbol, + ImmutableArray deconstructionSubpatterns, + ImmutableArray<(ISymbol, IPatternOperation)> propertySubpatterns, + ISymbol declaredSymbol, SemanticModel semanticModel, + SyntaxNode syntax, + bool isImplicit) + : base(inputType, matchedType, deconstructSymbol, declaredSymbol, semanticModel, syntax, isImplicit) + { + foreach (var p in deconstructionSubpatterns) + { + SetParentOperation(p, this); + } + DeconstructionSubpatterns = deconstructionSubpatterns; + foreach (var p in propertySubpatterns) + { + SetParentOperation(p.Item2, this); + } + PropertySubpatterns = propertySubpatterns; + } + public override ImmutableArray DeconstructionSubpatterns { get; } + public override ImmutableArray<(ISymbol, IPatternOperation)> PropertySubpatterns { get; } + } + + internal abstract partial class LazyRecursivePatternOperation : BaseRecursivePatternOperation + { + private ImmutableArray _lazyDeconstructionSubpatterns; + private ImmutableArray<(ISymbol, IPatternOperation)> _lazyPropertySubpatterns; + public LazyRecursivePatternOperation( + ITypeSymbol inputType, + ITypeSymbol matchedType, + ISymbol deconstructSymbol, + ISymbol declaredSymbol, SemanticModel semanticModel, + SyntaxNode syntax, + bool isImplicit) + : base(inputType, matchedType, deconstructSymbol, declaredSymbol, semanticModel, syntax, isImplicit) + { + } + public abstract ImmutableArray CreateDeconstructionSubpatterns(); + public abstract ImmutableArray<(ISymbol, IPatternOperation)> CreatePropertySubpatterns(); + public override ImmutableArray DeconstructionSubpatterns + { + get + { + if (_lazyDeconstructionSubpatterns.IsDefault) + { + var deconstructionSubpatterns = CreateDeconstructionSubpatterns(); + foreach (var d in deconstructionSubpatterns) + { + SetParentOperation(d, this); + } + + ImmutableInterlocked.InterlockedInitialize(ref _lazyDeconstructionSubpatterns, deconstructionSubpatterns); + } + + return _lazyDeconstructionSubpatterns; + } + } + public override ImmutableArray<(ISymbol, IPatternOperation)> PropertySubpatterns + { + get + { + if (_lazyPropertySubpatterns.IsDefault) + { + var propertySubpatterns = CreatePropertySubpatterns(); + foreach (var d in propertySubpatterns) + { + SetParentOperation(d.Item2, this); + } + + ImmutableInterlocked.InterlockedInitialize(ref _lazyPropertySubpatterns, propertySubpatterns); + } + + return _lazyPropertySubpatterns; + } + } + } + /// /// Represents a C# pattern case clause. /// diff --git a/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs b/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs index 94a9a41b2f3e1..5cadb4b87bc76 100644 --- a/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs +++ b/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs @@ -1756,7 +1756,6 @@ public override void VisitRecursivePattern(IRecursivePatternOperation operation) LogSymbol(subpat.Item1, "Matched Property"); Visit(subpat.Item2, ", Pattern"); }); - LogCommonPropertiesAndNewLine(operation); } public override void VisitIsPattern(IIsPatternOperation operation) From 7da739732597d9596ee2211cc7ff268ceacca511 Mon Sep 17 00:00:00 2001 From: Neal Gafter Date: Fri, 21 Dec 2018 15:57:12 -0800 Subject: [PATCH 5/6] Small changes per review. --- .../IOperationTests_IIsPatternExpression.cs | 2 +- .../Core/Portable/Operations/OperationNodes.cs | 15 +++++---------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs index b06aca486061f..2d015707256f2 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs @@ -639,7 +639,7 @@ public void IsPattern_NoControlFlow_01() string source = @" class C { - void M(int? x, bool b, int x2, bool b2, (int X, int Y)? x3, bool b3) + void M(int? x, bool b, int x2, bool b2) /**/{ b = x is var y; b2 = x2 is 1; diff --git a/src/Compilers/Core/Portable/Operations/OperationNodes.cs b/src/Compilers/Core/Portable/Operations/OperationNodes.cs index 2a60d5a7deba4..28a578d607c49 100644 --- a/src/Compilers/Core/Portable/Operations/OperationNodes.cs +++ b/src/Compilers/Core/Portable/Operations/OperationNodes.cs @@ -7985,19 +7985,14 @@ public override IEnumerable Children { get { - if (!DeconstructionSubpatterns.IsDefault) + foreach (var p in DeconstructionSubpatterns) { - foreach (var p in DeconstructionSubpatterns) - { - yield return p; - } + yield return p; } - if (!PropertySubpatterns.IsDefault) + + foreach (var p in PropertySubpatterns) { - foreach (var p in PropertySubpatterns) - { - yield return p.Item2; - } + yield return p.Item2; } } } From 557a5f36a4311ff312cb4ba0434d6cfce879b343 Mon Sep 17 00:00:00 2001 From: Neal Gafter Date: Wed, 26 Dec 2018 14:53:52 -0800 Subject: [PATCH 6/6] Remove accidentally added file. --- Patterns.playlist | 1 - 1 file changed, 1 deletion(-) delete mode 100644 Patterns.playlist diff --git a/Patterns.playlist b/Patterns.playlist deleted file mode 100644 index 9e67c87630e39..0000000000000 --- a/Patterns.playlist +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file