diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs index 6f4ba91f68ac0..8c749c8358698 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser_Patterns.cs @@ -210,7 +210,7 @@ private PatternSyntax ParsePrimaryPattern(Precedence precedence, bool afterIs, b case SyntaxKind.DotDotToken: return _syntaxFactory.SlicePattern( CheckFeatureAvailability(EatToken(), MessageID.IDS_FeatureSlicePattern), - IsPossibleSubpatternElement() ? ParseNegatedPattern(precedence, afterIs: false, whenIsKeyword) : null); + IsPossibleSubpatternElement() ? ParsePattern(precedence, afterIs: false, whenIsKeyword) : null); case SyntaxKind.LessThanToken: case SyntaxKind.LessThanEqualsToken: case SyntaxKind.GreaterThanToken: @@ -452,6 +452,7 @@ private bool IsValidPatternDesignation(bool whenIsKeyword) case SyntaxKind.IdentifierToken: case SyntaxKind.OpenBraceToken: case SyntaxKind.OpenParenToken: + case SyntaxKind.OpenBracketToken: // these all can start a pattern return false; default: @@ -541,7 +542,10 @@ private PropertyPatternClauseSyntax ParsePropertyPatternClause() closeKind: SyntaxKind.CloseBraceToken, out bool isPropertyPatternClause); var kind = isPropertyPatternClause ? SyntaxKind.PropertyPatternClause : SyntaxKind.ListPatternClause; - return _syntaxFactory.PropertyPatternClause(kind, openBraceToken, subPatterns, closeBraceToken); + var result = _syntaxFactory.PropertyPatternClause(kind, openBraceToken, subPatterns, closeBraceToken); + if (!isPropertyPatternClause) + result = CheckFeatureAvailability(result, MessageID.IDS_FeatureListPattern); + return result; } private void ParseSubpatternList( diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs index e541f8072149e..55bf3c822a564 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs @@ -1088,6 +1088,9 @@ void M1(object o, bool b) var compilation = CreateCompilation(source, new[] { vbCompilation.EmitToImageReference() }, parseOptions: TestOptions.Regular8); compilation.VerifyDiagnostics( + // (6,31): error CS8652: The feature 'list pattern' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // b = /**/o is C1 { Prop[1]: var x }/**/; + Diagnostic(ErrorCode.ERR_FeatureInPreview, "{ Prop[1]: var x }").WithArguments("list pattern").WithLocation(6, 31), // (6,31): error CS9200: List patterns may not be used for a value of type 'C1'. // b = /**/o is C1 { Prop[1]: var x }/**/; Diagnostic(ErrorCode.ERR_UnsupportedTypeForListPattern, "{ Prop[1]: var x }").WithArguments("C1").WithLocation(6, 31), diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs index 78d4fd087ca21..6e3ccd6d971b9 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests_ListPatterns.cs @@ -238,7 +238,7 @@ .locals init (char V_0, //first } [Fact] - public void LengthPattern() + public void LengthPattern_01() { var source = @" using System; @@ -335,6 +335,122 @@ .locals init (int V_0, //length }"); } + [Fact] + public void LengthPattern_02() + { + var source = @" +using System; +class X +{ + public static bool Test1(int[] a) => a is [1] or [2]; + public static bool Test2(int[] a) => a is [1 or 2]; + public static bool Test3(int[] a) => a is [>=1] and [<3]; + public static bool Test4(int[] a) => a is [>=1 and <3]; + public static bool Test5(int[] a) => a is [not 3]; + public static bool Test6(int[] a) => a is {} and not [3]; + + public static void Main() + { + foreach (var a in new[] { new int[1], new int[2], new int[3] }) + { + Console.WriteLine(Test1(a)); + Console.WriteLine(Test2(a)); + Console.WriteLine(Test3(a)); + Console.WriteLine(Test4(a)); + Console.WriteLine(Test5(a)); + Console.WriteLine(Test6(a)); + } + } +} +"; + var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularWithListPatterns, options: TestOptions.ReleaseExe); + compilation.VerifyEmitDiagnostics(); + string expectedOutput = @" +True +True +True +True +True +True +True +True +True +True +True +True +False +False +False +False +False +False +"; + var verifier = CompileAndVerify(compilation, expectedOutput: expectedOutput); + string expectedIL12 = @"{ + // Code size 24 (0x18) + .maxstack 2 + .locals init (int V_0, + bool V_1) + IL_0000: ldarg.0 + IL_0001: brfalse.s IL_0014 + IL_0003: ldarg.0 + IL_0004: callvirt ""int System.Array.Length.get"" + IL_0009: stloc.0 + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: sub + IL_000d: ldc.i4.1 + IL_000e: bgt.un.s IL_0014 + IL_0010: ldc.i4.1 + IL_0011: stloc.1 + IL_0012: br.s IL_0016 + IL_0014: ldc.i4.0 + IL_0015: stloc.1 + IL_0016: ldloc.1 + IL_0017: ret +}"; + string expectedIL34 = @"{ + // Code size 21 (0x15) + .maxstack 2 + .locals init (int V_0) + IL_0000: ldarg.0 + IL_0001: brfalse.s IL_0013 + IL_0003: ldarg.0 + IL_0004: callvirt ""int System.Array.Length.get"" + IL_0009: stloc.0 + IL_000a: ldloc.0 + IL_000b: ldc.i4.1 + IL_000c: blt.s IL_0013 + IL_000e: ldloc.0 + IL_000f: ldc.i4.3 + IL_0010: clt + IL_0012: ret + IL_0013: ldc.i4.0 + IL_0014: ret +}"; + string expectedIL56 = @"{ + // Code size 18 (0x12) + .maxstack 2 + IL_0000: ldarg.0 + IL_0001: brfalse.s IL_0010 + IL_0003: ldarg.0 + IL_0004: callvirt ""int System.Array.Length.get"" + IL_0009: ldc.i4.3 + IL_000a: ceq + IL_000c: ldc.i4.0 + IL_000d: ceq + IL_000f: ret + IL_0010: ldc.i4.0 + IL_0011: ret +}"; + verifier.VerifyIL("X.Test1", expectedIL12); + verifier.VerifyIL("X.Test2", expectedIL12); + verifier.VerifyIL("X.Test3", expectedIL34); + verifier.VerifyIL("X.Test4", expectedIL34); + verifier.VerifyIL("X.Test5", expectedIL56); + verifier.VerifyIL("X.Test6", expectedIL56); + } + [Fact] public void LengthPattern_InputType() { @@ -1584,7 +1700,8 @@ public void M(int[] a) _ = a is {1,2,3} and {1,2,3}; _ = a is ([>0]) and ([<0]); // 5 _ = a is ([>0]) and ([>=0]); - // PROTOTYPE(list-patterns) Parsing length patterns inside combinators + _ = a is [>0] and [<0]; // 6 + _ = a is [>0] and [>=0]; } } "; @@ -1604,7 +1721,10 @@ public void M(int[] a) Diagnostic(ErrorCode.ERR_IsPatternImpossible, "a is {1,2,3} and {1,2,4}").WithArguments("int[]").WithLocation(13, 13), // (15,13): error CS8518: An expression of type 'int[]' can never match the provided pattern. // _ = a is ([>0]) and ([<0]); // 5 - Diagnostic(ErrorCode.ERR_IsPatternImpossible, "a is ([>0]) and ([<0])").WithArguments("int[]").WithLocation(15, 13) + Diagnostic(ErrorCode.ERR_IsPatternImpossible, "a is ([>0]) and ([<0])").WithArguments("int[]").WithLocation(15, 13), + // (17,13): error CS8518: An expression of type 'int[]' can never match the provided pattern. + // _ = a is [>0] and [<0]; // 6 + Diagnostic(ErrorCode.ERR_IsPatternImpossible, "a is [>0] and [<0]").WithArguments("int[]").WithLocation(17, 13) ); } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests.cs index fee8745b097f5..e1d173dc3b38d 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests.cs @@ -6656,6 +6656,9 @@ public void TrailingCommaInPropertyPattern_01() public void TrailingCommaInPropertyPattern_02() { UsingExpression("e is { , }", + // (1,6): error CS8652: The feature 'list pattern' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // e is { , } + Diagnostic(ErrorCode.ERR_FeatureInPreview, "{ , }").WithArguments("list pattern").WithLocation(1, 6), // (1,8): error CS8504: Pattern missing // e is { , } Diagnostic(ErrorCode.ERR_MissingPattern, ",").WithLocation(1, 8) diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests_ListPatterns.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests_ListPatterns.cs index 024c9af699370..08c0da6496d25 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests_ListPatterns.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/PatternParsingTests_ListPatterns.cs @@ -673,6 +673,146 @@ public void LengthPattern_16() EOF(); } + [Fact] + public void LengthPattern_17() + { + UsingExpression(@"c is not [<0]"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.NotPattern); + { + N(SyntaxKind.NotKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.LengthPatternClause); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.RelationalPattern); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + } + } + EOF(); + } + + [Fact] + public void LengthPattern_18() + { + UsingExpression(@"c is [0] and [1]"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.AndPattern); + { + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.LengthPatternClause); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.AndKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.LengthPatternClause); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + } + } + EOF(); + } + + [Fact] + public void LengthPattern_19() + { + UsingExpression(@"c is int [0] or [1]"); + + N(SyntaxKind.IsPatternExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "c"); + } + N(SyntaxKind.IsKeyword); + N(SyntaxKind.OrPattern); + { + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + N(SyntaxKind.LengthPatternClause); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "0"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + N(SyntaxKind.OrKeyword); + N(SyntaxKind.RecursivePattern); + { + N(SyntaxKind.LengthPatternClause); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.ConstantPattern); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + } + } + EOF(); + } + [Fact] public void NoRegressionOnEmptyPropertyPattern() { @@ -735,7 +875,10 @@ public void ListPattern_01() UsingExpression(@"c is {{}}"); verify(); - UsingExpression(@"c is {{}}", TestOptions.Regular9); + UsingExpression(@"c is {{}}", TestOptions.Regular9, + // (1,6): error CS8652: The feature 'list pattern' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // c is {{}} + Diagnostic(ErrorCode.ERR_FeatureInPreview, "{{}}").WithArguments("list pattern").WithLocation(1, 6)); verify(); void verify() @@ -831,6 +974,9 @@ public void SlicePattern_01() verify(); UsingExpression(@"c is {..}", TestOptions.Regular9, + // (1,6): error CS8652: The feature 'list pattern' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. + // c is {..} + Diagnostic(ErrorCode.ERR_FeatureInPreview, "{..}").WithArguments("list pattern").WithLocation(1, 6), // (1,7): error CS8652: The feature 'slice pattern' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version. // c is {..} Diagnostic(ErrorCode.ERR_FeatureInPreview, "..").WithArguments("slice pattern").WithLocation(1, 7)); @@ -1052,11 +1198,11 @@ public void SlicePattern_07() N(SyntaxKind.OpenBraceToken); N(SyntaxKind.Subpattern); { - N(SyntaxKind.OrPattern); + N(SyntaxKind.SlicePattern); { - N(SyntaxKind.SlicePattern); + N(SyntaxKind.DotDotToken); + N(SyntaxKind.OrPattern); { - N(SyntaxKind.DotDotToken); N(SyntaxKind.ConstantPattern); { N(SyntaxKind.IdentifierName); @@ -1064,13 +1210,13 @@ public void SlicePattern_07() N(SyntaxKind.IdentifierToken, "p"); } } - } - N(SyntaxKind.OrKeyword); - N(SyntaxKind.ConstantPattern); - { - N(SyntaxKind.IdentifierName); + N(SyntaxKind.OrKeyword); + N(SyntaxKind.ConstantPattern); { - N(SyntaxKind.IdentifierToken, "q"); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "q"); + } } } } @@ -1101,11 +1247,11 @@ public void SlicePattern_08() N(SyntaxKind.OpenBraceToken); N(SyntaxKind.Subpattern); { - N(SyntaxKind.OrPattern); + N(SyntaxKind.SlicePattern); { - N(SyntaxKind.SlicePattern); + N(SyntaxKind.DotDotToken); + N(SyntaxKind.OrPattern); { - N(SyntaxKind.DotDotToken); N(SyntaxKind.ConstantPattern); { N(SyntaxKind.IdentifierName); @@ -1113,16 +1259,16 @@ public void SlicePattern_08() N(SyntaxKind.IdentifierToken, "p"); } } - } - N(SyntaxKind.OrKeyword); - N(SyntaxKind.SlicePattern); - { - N(SyntaxKind.DotDotToken); - N(SyntaxKind.ConstantPattern); + N(SyntaxKind.OrKeyword); + N(SyntaxKind.SlicePattern); { - N(SyntaxKind.IdentifierName); + N(SyntaxKind.DotDotToken); + N(SyntaxKind.ConstantPattern); { - N(SyntaxKind.IdentifierToken, "q"); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "q"); + } } } }