From c62345b17fe80fcc2f3a1d837ea5c849ee889035 Mon Sep 17 00:00:00 2001 From: Rex Jaeschke Date: Thu, 7 Dec 2023 07:28:52 -0500 Subject: [PATCH 01/16] Add support for pattern additions Add support for pattern additions fix md and example update headings fix link warnings --- standard/lexical-structure.md | 16 ++-- standard/patterns.md | 159 +++++++++++++++++++++++++++++++++- 2 files changed, 166 insertions(+), 9 deletions(-) diff --git a/standard/lexical-structure.md b/standard/lexical-structure.md index a6f553882..575851db5 100644 --- a/standard/lexical-structure.md +++ b/standard/lexical-structure.md @@ -78,9 +78,10 @@ These productions occur in contexts where a value can occur in an expression, an If a sequence of tokens can be parsed, in context, as one of the disambiguated productions including an optional *type_argument_list* ([§8.4.2](types.md#842-type-arguments)), then the token immediately following the closing `>` token shall be examined and if it is: -- one of `( ) ] } : ; , . ? == != | ^ && || & [`; or +- one of `( ) ] } : ; , . ? == != | ^ && || & [ =>`; or - one of the relational operators `< <= >= is as`; or - a contextual query keyword appearing inside a query expression. +- In certain contexts, *identifier* is treated as a disambiguating token. Those contexts are where the sequence of tokens being disambiguated is immediately preceded by one of the keywords `is`, `case` or `out`, or arises while parsing the first element of a tuple literal (in which case the tokens are preceded by `(` or `:` and the identifier is followed by a `,`) or a subsequent element of a tuple literal. then the *type_argument_list* shall be retained as part of the disambiguated production and any other possible parse of the sequence of tokens discarded. Otherwise, the tokens parsed as a *type_argument_list* shall not be considered to be part of the disambiguated production, even if there is no other possible parse of those tokens. @@ -604,12 +605,13 @@ A ***contextual keyword*** is an identifier-like sequence of characters that has ```ANTLR contextual_keyword - : 'add' | 'alias' | 'ascending' | 'async' | 'await' - | 'by' | 'descending' | 'dynamic' | 'equals' | 'from' - | 'get' | 'global' | 'group' | 'into' | 'join' - | 'let' | 'nameof' | 'notnull' | 'on' | 'orderby' - | 'partial' | 'remove' | 'select' | 'set' | 'unmanaged' - | 'value' | 'var' | 'when' | 'where' | 'yield' + : 'add' | 'alias' | 'and' | 'ascending' | 'async' + | 'await' | 'by' | 'descending' | 'dynamic' | 'equals' + | 'from' | 'get' | 'global' | 'group' | 'into' + | 'join' | 'let' | 'nameof' | 'not' | 'notnull' + | 'on' | 'or' | 'orderby' | 'partial' | 'remove' + | 'select' | 'set' | 'unmanaged' | 'value' | 'var' + | 'when' | 'where' | 'yield' ; ``` diff --git a/standard/patterns.md b/standard/patterns.md index b74be665c..60bd38443 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -11,7 +11,7 @@ A pattern is tested against a value in a number of contexts: - In a *switch_expression*, the *pattern* of a *switch_expression_arm* is tested against the expression on the *switch_expression*’s left-hand-side. - In nested contexts, the *sub-pattern* is tested against values retrieved from properties, fields, or indexed from other input values, depending on the pattern form. -The value against which a pattern is tested is called the ***pattern input value***. +The value against which a pattern is tested is called the ***pattern input value***. Patterns may be combined using Boolean logic. ## 11.2 Pattern forms @@ -21,12 +21,16 @@ A pattern may have one of the following forms: ```ANTLR pattern - : declaration_pattern + : '(' pattern ')' + | declaration_pattern | constant_pattern | var_pattern | positional_pattern | property_pattern | discard_pattern + | type_pattern + | relational_pattern + | logical_pattern ; ``` @@ -447,6 +451,157 @@ If, after applying the preceding rule, the token `_` is still a *discard_pattern > > *end example* +### §type-pattern-new-clause Type pattern + +A *type_pattern* is used to test that the pattern input value ([§11.1](patterns.md#111-general)) has a given type. + +```ANTLR +type_pattern + : type + ; +``` + +The runtime type of the value is tested against *type* using the same rules specified in the is-type operator ([§12.14.12.1](expressions.md#1214121-the-is-type-operator)). If the test succeeds, the pattern matches that value. It is a compile-time error if the *type* is a nullable type. This pattern form never matches a `null` value. + +### §relational-pattern-new-clause Relational pattern + +A *relational_pattern* is used to relationally test the pattern input value ([§11.1](patterns.md#111-general)) against a constant value. + +```ANTLR +relational_pattern + : '<' constant_expression + | '<=' constant_expression + | '>' constant_expression + | '>=' constant_expression + ; +``` + +Relational patterns support the relational operators `<`, `<=`, `>`, and `>=` on all of the built-in types that support such binary relational operators with both operands having the same type: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, `nint`, `nuint`, and enums. + +It is a compile-time error if `constant_expression`is `double.NaN`, `float.NaN`, or `null_literal`. + +When the input value has a type for which a suitable built-in binary relational operator is defined, the evaluation of that operator is taken as the meaning of the relational pattern. Otherwise, the input value is converted to the type of `constant_expression` using an explicit nullable or unboxing conversion. It is a compile-time error if no such conversion exists. The pattern is considered to not match if the conversion fails. If the conversion succeeds, the result of the pattern-matching operation is the result of evaluating the expression `e «op» v` where `e` is the converted input, «op» is the relational operator, and `v` is the `constant_expression`. + +> *Example*: +> +> +> ```csharp +> Console.WriteLine(Classify(13)); +> Console.WriteLine(Classify(double.NaN)); +> Console.WriteLine(Classify(2.4)); +> +> static string Classify(double measurement) => measurement switch +> { +> < -4.0 => "Too low", +> > 10.0 => "Too high", +> double.NaN => "Unknown", +> _ => "Acceptable", +> }; +> ``` +> +> The output produced is +> +> ```console +> Too high +> Unknown +> Acceptable +> ``` +> +> *end example* + +### §logical-pattern-new-clause Logical pattern + +A *logical_pattern* is used to negate a pattern input value ([§11.1](patterns.md#111-general)) or to combine that value with a pattern using a Boolean operator. + +```ANTLR +logical_pattern + : disjunctive_pattern + ; + +disjunctive_pattern + : disjunctive_pattern 'or' conjunctive_pattern + | conjunctive_pattern + ; + +conjunctive_pattern + : conjunctive_pattern 'and' negated_pattern + | negated_pattern + ; + +negated_pattern + : 'not' negated_pattern + | pattern + ; +``` + +`not`, `and`, and `or` are collectively called ***pattern operators***. + +A *negated_pattern* matches if the pattern being negated does not match, and vice versa. A *conjunctive_pattern* requires both patterns to match. A *disjunctive_pattern* requires either pattern to match. Unlike their language operator counterparts, `&&` and `||`, `and` and `or` are *not* short-circuiting operators. + +> *Note*: As indicated by the grammar, `not` has precedence over `and`, which has precedence over `or`. This can be explicitly indicated or overridden by using parentheses. *end note* + +When a *pattern* is used with `is`, any pattern operators in that *pattern* have higher precedence than their logical operator counterparts. Otherwise, those pattern operators have lower precedence. + +> *Example*: +> +> +> ```csharp +> Console.WriteLine(Classify(13)); +> Console.WriteLine(Classify(-100)); +> Console.WriteLine(Classify(5.7)); +> +> static string Classify(double measurement) => measurement switch +> { +> < -40.0 => "Too low", +> >= -40.0 and < 0 => "Low", +> >= 0 and < 10.0 => "Acceptable", +> >= 10.0 and < 20.0 => "High", +> >= 20.0 => "Too high", +> double.NaN => "Unknown", +> }; +> ``` +> +> The output produced is +> +> ```console +> High +> Too low +> Acceptable +> ``` +> +> *end example* + + + +> *Example*: +> +> +> ```csharp +> Console.WriteLine(GetCalendarSeason(new DateTime(2021, 1, 19))); +> Console.WriteLine(GetCalendarSeason(new DateTime(2021, 10, 9))); +> Console.WriteLine(GetCalendarSeason(new DateTime(2021, 5, 11))); +> +> static string GetCalendarSeason(DateTime date) => date.Month switch +> { +> 3 or 4 or 5 => "spring", +> 6 or 7 or 8 => "summer", +> 9 or 10 or 11 => "autumn", +> 12 or 1 or 2 => "winter", +> _ => throw new ArgumentOutOfRangeException(nameof(date), +> $"Date with unexpected month: {date.Month}."), +> }; +> ``` +> +> The output produced is +> +> ```console +> winter +> autumn +> spring +> ``` +> +> *end example* + ## 11.3 Pattern subsumption In a switch statement ([§13.8.3](statements.md#1383-the-switch-statement)), it is an error if a case’s pattern is *subsumed* by the preceding set of *unguarded* ([§13.8.3](statements.md#1383-the-switch-statement)) cases. In a switch expression ([§12.11](expressions.md#1211-switch-expression)), it is an error if a *switch_expression_arm*’s pattern is *subsumed* by the preceding set of *unguarded* *switch_expression_arm*s’ patterns. From 0497263a8f308b21a9e5501f8803e8c6f6e757dc Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 19 Feb 2026 12:47:21 -0500 Subject: [PATCH 02/16] Address lined comments Address the comments on specific lines in #1026. --- standard/patterns.md | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/standard/patterns.md b/standard/patterns.md index 60bd38443..998b19c40 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -11,7 +11,7 @@ A pattern is tested against a value in a number of contexts: - In a *switch_expression*, the *pattern* of a *switch_expression_arm* is tested against the expression on the *switch_expression*’s left-hand-side. - In nested contexts, the *sub-pattern* is tested against values retrieved from properties, fields, or indexed from other input values, depending on the pattern form. -The value against which a pattern is tested is called the ***pattern input value***. Patterns may be combined using Boolean logic. +The value against which a pattern is tested is called the ***pattern input value***. ## 11.2 Pattern forms @@ -36,6 +36,8 @@ pattern If the input can be syntactically recognised as both a *constant_pattern* and a *positional_pattern* then the *constant_pattern* shall be chosen. +The `'(' pattern ')'` production allows a pattern to be enclosed in parentheses to enforce the order of evaluation among patterns combined using one of the *logical_pattern*s. + Some *pattern*s can result in the declaration of a local variable. Each pattern form defines the set of types for input values that the pattern may be applied to. A pattern `P` is *applicable to* a type `T` if `T` is among the types whose values the pattern may match. It is a compile-time error if a pattern `P` appears in a program to match a pattern input value ([§11.1](patterns.md#111-general)) of type `T` if `P` is not applicable to `T`. @@ -478,7 +480,7 @@ relational_pattern Relational patterns support the relational operators `<`, `<=`, `>`, and `>=` on all of the built-in types that support such binary relational operators with both operands having the same type: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, `nint`, `nuint`, and enums. -It is a compile-time error if `constant_expression`is `double.NaN`, `float.NaN`, or `null_literal`. +It is a compile-time error if `constant_expression` evaluates to one of `double.NaN`, `float.NaN`, or `null_literal`. When the input value has a type for which a suitable built-in binary relational operator is defined, the evaluation of that operator is taken as the meaning of the relational pattern. Otherwise, the input value is converted to the type of `constant_expression` using an explicit nullable or unboxing conversion. It is a compile-time error if no such conversion exists. The pattern is considered to not match if the conversion fails. If the conversion succeeds, the result of the pattern-matching operation is the result of evaluating the expression `e «op» v` where `e` is the converted input, «op» is the relational operator, and `v` is the `constant_expression`. @@ -511,7 +513,7 @@ When the input value has a type for which a suitable built-in binary relational ### §logical-pattern-new-clause Logical pattern -A *logical_pattern* is used to negate a pattern input value ([§11.1](patterns.md#111-general)) or to combine that value with a pattern using a Boolean operator. +A *logical_pattern* is used to negate the result of matching a pattern input value ([§11.1](patterns.md#111-general)) or to combine that match result with a pattern using a Boolean operator. ```ANTLR logical_pattern @@ -601,6 +603,33 @@ When a *pattern* is used with `is`, any pattern operators in that *pattern* have > ``` > > *end example* + + + +> *Example*: +> +> +> ```csharp +> object obj = 5; +> bool flag = true; +> +> // This is parsed as: (obj is (int or string)) && flag +> bool result = obj is int or string && flag; +> Console.WriteLine($"obj (5), flag (true): obj is int or string && flag: {result}"); // True +> +> // This is parsed as: (obj is int) || ((obj is string) && flag) +> result = obj is int || obj is string && flag; +> Console.WriteLine($"obj (5), flag (true): obj is int || obj is string && flag: {result}"); // True +> +> flag = false; +> // This is parsed as: (obj is (int or string)) && flag +> result = obj is int or string && flag; +> Console.WriteLine($"obj (5), flag (false): obj is int or string && flag: {result}"); // False +> +> // This is parsed as: (obj is int) || ((obj is string) && flag) +> result = obj is int || obj is string && flag; +> Console.WriteLine($"obj (5), flag (false): obj is int || obj is string && flag: {result}"); // True +> ``` ## 11.3 Pattern subsumption From 34cc9874a7053f3b926fe522c5647ad19d764e84 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 19 Feb 2026 14:38:02 -0500 Subject: [PATCH 03/16] Address general comment Address the comments in the general review comment on #1026: - I did continue to use the *logical_pattern* production. We use that term in the text, and it's a good shorthand so readers don't need to snake through the grammar of *disjunctive_pattern*, *conjunctive_pattern* and *negated_pattern*. - I haven't addressed or created issues for the open questions in the proposal yet. --- standard/patterns.md | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/standard/patterns.md b/standard/patterns.md index 998b19c40..c22e7afec 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -21,16 +21,24 @@ A pattern may have one of the following forms: ```ANTLR pattern - : '(' pattern ')' + : logical_pattern + ; + +primary_pattern + : parenthesized_pattern | declaration_pattern | constant_pattern | var_pattern | positional_pattern | property_pattern | discard_pattern + | parenthesized_pattern | type_pattern | relational_pattern - | logical_pattern + ; + +parenthesized_pattern + : '(' pattern ')' ; ``` @@ -471,10 +479,10 @@ A *relational_pattern* is used to relationally test the pattern input value ([§ ```ANTLR relational_pattern - : '<' constant_expression - | '<=' constant_expression - | '>' constant_expression - | '>=' constant_expression + : '<' relational_expression + | '<=' relational_expression + | '>' relational_expression + | '>=' relational_expression ; ``` @@ -532,7 +540,7 @@ conjunctive_pattern negated_pattern : 'not' negated_pattern - | pattern + | primary_pattern ; ``` From af464683de9dac55853355cfd5954025c0b23efa Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 19 Feb 2026 15:42:36 -0500 Subject: [PATCH 04/16] Fix build issue --- standard/patterns.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/patterns.md b/standard/patterns.md index c22e7afec..d929d39f2 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -616,7 +616,7 @@ When a *pattern* is used with `is`, any pattern operators in that *pattern* have > *Example*: > -> +> > ```csharp > object obj = 5; > bool flag = true; From bdc08acf8f51943caee164f16a22b82327fedad7 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 19 Feb 2026 16:04:11 -0500 Subject: [PATCH 05/16] Add closing tag --- standard/patterns.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/standard/patterns.md b/standard/patterns.md index d929d39f2..d618ec8c1 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -638,6 +638,8 @@ When a *pattern* is used with `is`, any pattern operators in that *pattern* have > result = obj is int || obj is string && flag; > Console.WriteLine($"obj (5), flag (false): obj is int || obj is string && flag: {result}"); // True > ``` +> +> *end example* ## 11.3 Pattern subsumption From 09c59abda1361c593ecc3e5c190146ef53556ce5 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 19 Feb 2026 16:07:12 -0500 Subject: [PATCH 06/16] fix output --- standard/patterns.md | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/standard/patterns.md b/standard/patterns.md index d618ec8c1..18d90d27d 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -623,22 +623,32 @@ When a *pattern* is used with `is`, any pattern operators in that *pattern* have > > // This is parsed as: (obj is (int or string)) && flag > bool result = obj is int or string && flag; -> Console.WriteLine($"obj (5), flag (true): obj is int or string && flag: {result}"); // True +> Console.WriteLine($"obj (5), flag (true): obj is int or string && flag: {result}"); > > // This is parsed as: (obj is int) || ((obj is string) && flag) > result = obj is int || obj is string && flag; -> Console.WriteLine($"obj (5), flag (true): obj is int || obj is string && flag: {result}"); // True +> Console.WriteLine($"obj (5), flag (true): obj is int || obj is string && flag: {result}"); > > flag = false; > // This is parsed as: (obj is (int or string)) && flag > result = obj is int or string && flag; -> Console.WriteLine($"obj (5), flag (false): obj is int or string && flag: {result}"); // False +> Console.WriteLine($"obj (5), flag (false): obj is int or string && flag: {result}"); > > // This is parsed as: (obj is int) || ((obj is string) && flag) > result = obj is int || obj is string && flag; -> Console.WriteLine($"obj (5), flag (false): obj is int || obj is string && flag: {result}"); // True +> Console.WriteLine($"obj (5), flag (false): obj is int || obj is string && flag: {result}"); > ``` > +> The output produced is +> +> ```console +> True +> True +> False +> True +> ``` +> +> > *end example* ## 11.3 Pattern subsumption From e7215117eb6996406fad60f3f811341183ff7113 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 19 Feb 2026 16:10:36 -0500 Subject: [PATCH 07/16] more fixing the output --- standard/patterns.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/standard/patterns.md b/standard/patterns.md index 18d90d27d..b6dfc7ec4 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -642,10 +642,10 @@ When a *pattern* is used with `is`, any pattern operators in that *pattern* have > The output produced is > > ```console -> True -> True -> False -> True +> obj (5), flag (true): obj is int or string && flag: True +> obj (5), flag (true): obj is int || obj is string && flag: True +> obj (5), flag (false): obj is int or string && flag: False +> obj (5), flag (false): obj is int || obj is string && flag: True > ``` > > From f2f6f94b09e09e565889cfec2fb23bdb8189ebd7 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 19 Feb 2026 17:28:32 -0500 Subject: [PATCH 08/16] Add LDM decisions --- standard/patterns.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/standard/patterns.md b/standard/patterns.md index b6dfc7ec4..f6d352808 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -548,6 +548,18 @@ negated_pattern A *negated_pattern* matches if the pattern being negated does not match, and vice versa. A *conjunctive_pattern* requires both patterns to match. A *disjunctive_pattern* requires either pattern to match. Unlike their language operator counterparts, `&&` and `||`, `and` and `or` are *not* short-circuiting operators. +In a *conjunctive_pattern*, the *input type* of the second pattern is narrowed by the *type narrowing* requirements of first pattern of the `and`. The *narrowed type* of a pattern `P` is defined as follows: + +- If `P` is a type pattern, the *narrowed type* is the type of the type pattern's type. +- Otherwise, if `P` is a declaration pattern, the *narrowed type* is the type of the declaration pattern's type. +- Otherwise, if `P` is a recursive pattern that gives an explicit type, the *narrowed type* is that type. +- Otherwise, if `P` is matched via the rules for `ITuple` in a *positional_pattern* (§11.2.5), the *narrowed type* is the type `System.Runtime.CompilerServices.ITuple`. +- Otherwise, if `P` is a constant pattern where the constant is not the null constant and where the expression has no *constant expression conversion* to the *input type*, the *narrowed type* is the type of the constant. +- Otherwise, if `P` is a relational pattern where the constant expression has no *constant expression conversion* to the *input type*, the *narrowed type* is the type of the constant. +- Otherwise, if `P` is an `or` pattern, the *narrowed type* is the common type of the *narrowed type* of the subpatterns if such a common type exists. For this purpose, the common type algorithm considers only identity, boxing, and implicit reference conversions, and it considers all subpatterns of a sequence of `or` patterns (ignoring parenthesized patterns). +- Otherwise, if `P` is an `and` pattern, the *narrowed type* is the *narrowed type* of the right pattern. Moreover, the *narrowed type* of the left pattern is the *input type* of the right pattern. +- Otherwise the *narrowed type* of `P` is `P`'s input type. + > *Note*: As indicated by the grammar, `not` has precedence over `and`, which has precedence over `or`. This can be explicitly indicated or overridden by using parentheses. *end note* When a *pattern* is used with `is`, any pattern operators in that *pattern* have higher precedence than their logical operator counterparts. Otherwise, those pattern operators have lower precedence. From 378e7f87b3068e31881973a9b8bc5466ec9c5356 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Mon, 23 Feb 2026 16:20:47 -0500 Subject: [PATCH 09/16] Add subsumption and exhaustiveness Add the subsumption and exhaustiveness rules for the new patterns in V9. The subsumption/exhaustiveness rules in the Roslyn compiler use a BDD (binary decision diagram)-based algorithm that is more powerful than can be expressed with simple prose rules. These rules attempt to capture the *observable behavior* (what the compiler accepts/rejects) rather than the implementation algorithm. Where the compiler's algorithm is too complex to fully express (e.g., relational range analysis), the spec describes the intent and give examples. --- standard/patterns.md | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/standard/patterns.md b/standard/patterns.md index f6d352808..eecef5a7d 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -548,6 +548,10 @@ negated_pattern A *negated_pattern* matches if the pattern being negated does not match, and vice versa. A *conjunctive_pattern* requires both patterns to match. A *disjunctive_pattern* requires either pattern to match. Unlike their language operator counterparts, `&&` and `||`, `and` and `or` are *not* short-circuiting operators. +It is a compile-time error for a pattern variable to be declared beneath a `not` or `or` pattern operator. + +> *Note*: Because neither `not` nor `or` can produce a definite assignment for a pattern variable, it is an error to declare one in those positions. *end note* + In a *conjunctive_pattern*, the *input type* of the second pattern is narrowed by the *type narrowing* requirements of first pattern of the `and`. The *narrowed type* of a pattern `P` is defined as follows: - If `P` is a type pattern, the *narrowed type* is the type of the type pattern's type. @@ -669,13 +673,29 @@ In a switch statement ([§13.8.3](statements.md#1383-the-switch-statement)), it > *Note*: This means that any input value would have been matched by one of the previous cases or arms. *end note* The following rules define when a set of patterns subsumes a given pattern: -A pattern `P` *would match* a constant `K` if the specification for that pattern’s runtime behavior is that `P` matches `K`. +A pattern `P` *would match* a constant `K` if any of the following conditions hold: + +- the specification for that pattern's runtime behavior is that `P` matches `K`. +- `P` is a *type_pattern* for type `T` and `K` is not `null` and the runtime type of `K` is `T` or a type derived from `T` or a type that implements `T`. +- `P` is a *relational_pattern* with operator «op» and constant `v`, and the expression `K` «op» `v` would evaluate to `true`. +- `P` is a *negated_pattern* `not P₁` and `P₁` would not match `K`. +- `P` is a *conjunctive_pattern* `P₁ and P₂` and both `P₁` would match `K` and `P₂` would match `K`. +- `P` is a *disjunctive_pattern* `P₁ or P₂` and either `P₁` would match `K` or `P₂` would match `K`. +- `P` is a *discard_pattern*. A set of patterns `Q` *subsumes* a pattern `P` if any of the following conditions hold: -- `P` is a constant pattern and any of the patterns in the set `Q` would match `P`’s *converted value* +- `P` is a constant pattern and any of the patterns in the set `Q` would match `P`'s *converted value* - `P` is a var pattern and the set of patterns `Q` is *exhaustive* ([§11.4](patterns.md#114-pattern-exhaustiveness)) for the type of the pattern input value ([§11.1](patterns.md#111-general)), and either the pattern input value is not of a nullable type or some pattern in `Q` would match `null`. - `P` is a declaration pattern with type `T` and the set of patterns `Q` is *exhaustive* for the type `T` ([§11.4](patterns.md#114-pattern-exhaustiveness)). +- `P` is a *type_pattern* for type `T` and the set of patterns `Q` is *exhaustive* for the type `T`. +- `P` is a *relational_pattern* with operator «op» and constant value `v`, and for every value of the input type satisfying the relation «op» `v`, some pattern in the set `Q` would match that value. +- `P` is a *disjunctive_pattern* `P₁ or P₂` and the set of patterns `Q` subsumes `P₁` and `Q` subsumes `P₂`. +- `P` is a *conjunctive_pattern* `P₁ and P₂` and at least one of the following holds: `Q` subsumes `P₁`, or `Q` subsumes `P₂`. +- `P` is a *negated_pattern* `not P₁` and `Q` is *exhaustive* for the input type considering only the values not matched by `P₁`. +- `P` is a *discard_pattern* and the set of patterns `Q` is *exhaustive* for the type of the pattern input value, and either the pattern input value is not of a nullable type or some pattern in `Q` would match `null`. +- Some pattern in `Q` is a *disjunctive_pattern* `Q₁ or Q₂` and replacing that pattern with `Q₁` in `Q` would yield a set that subsumes `P`, or replacing it with `Q₂` would yield a set that subsumes `P`. +- Some pattern in `Q` is a *negated_pattern* `not Q₁` and `P` would not match any value that `Q₁` would match. > *Example*: In the following switch expression, no arm is subsumed even though arms 1, 2, and 3 share the same pattern: > @@ -702,8 +722,16 @@ The following rules define when a set of patterns is *exhaustive* for a type: A set of patterns `Q` is *exhaustive* for a type `T` if any of the following conditions hold: 1. `T` is an integral or enum type, or a nullable version of one of those, and for every possible value of `T`’s non-nullable underlying type, some pattern in `Q` would match that value; or -2. Some pattern in `Q` is a *var pattern*; or -3. Some pattern in `Q` is a *declaration pattern* for type `D`, and there is an identity conversion, an implicit reference conversion, or a boxing conversion from `T` to `D`. +1. Some pattern in `Q` is a *var pattern*; or +1. Some pattern in `Q` is a *declaration pattern* for type `D`, and there is an identity conversion, an implicit reference conversion, or a boxing conversion from `T` to `D`; or +1. Some pattern in `Q` is a *type_pattern* for type `D`, and there is an identity conversion, an implicit reference conversion, or a boxing conversion from `T` to `D`; or +1. Some pattern in `Q` is a *discard_pattern*; or +1. The patterns in `Q` include a combination of *relational_pattern*s and *constant_pattern*s whose ranges collectively cover every possible value of `T`'s non-nullable underlying type. For `float` and `double` types, this includes `System.Double.NaN` or `System.Single.NaN` respectively, since `NaN` is not matched by any relational pattern; or +1. Some pattern in `Q` is a *disjunctive_pattern* `P₁ or P₂`, and replacing that pattern with both `P₁` and `P₂` in `Q` yields a set that is *exhaustive* for `T`; or +1. Some pattern in `Q` is a *negated_pattern* `not P₁`, and the patterns in `Q` together with the values not matched by `P₁` cover every possible value of `T`. A *negated_pattern* `not P₁` is exhaustive by itself when `P₁` matches no possible value of `T`; or +1. Some pattern in `Q` is a *conjunctive_pattern* `P₁ and P₂`, and the set containing only `P₁` is *exhaustive* for `T` and the set containing only `P₂` is *exhaustive* for `T`. + +> *Note*: For floating-point types, the combination of patterns `< 0` and `>= 0` is *not* exhaustive because neither relational pattern matches `NaN`. A correct exhaustive set would be `< 0`, `>= 0`, and `double.NaN` (or `float.NaN`). *end note* > *Example*: > From 84e5d08201e87d8074aae44be58be1b86d6493b2 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 27 Feb 2026 15:36:38 -0500 Subject: [PATCH 10/16] Editorial pass - Removed duplicate parenthesized_pattern from the primary_pattern production - Fixed relational pattern prose Added a sentence after the grammar: "The relational_expression in a relational_pattern is required to evaluate to a constant value." Replaced backtick-formatted `constant_expression` references with plain prose ("the expression", "the constant expression"). Changed `null_literal` to "a null constant" (it's a semantic constraint, not a syntactic one). --- standard/patterns.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/standard/patterns.md b/standard/patterns.md index eecef5a7d..94b52a30f 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -32,7 +32,6 @@ primary_pattern | positional_pattern | property_pattern | discard_pattern - | parenthesized_pattern | type_pattern | relational_pattern ; @@ -486,11 +485,13 @@ relational_pattern ; ``` +The *relational_expression* in a *relational_pattern* is required to evaluate to a constant value. + Relational patterns support the relational operators `<`, `<=`, `>`, and `>=` on all of the built-in types that support such binary relational operators with both operands having the same type: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, `nint`, `nuint`, and enums. -It is a compile-time error if `constant_expression` evaluates to one of `double.NaN`, `float.NaN`, or `null_literal`. +It is a compile-time error if the expression evaluates to `double.NaN`, `float.NaN`, or a null constant. -When the input value has a type for which a suitable built-in binary relational operator is defined, the evaluation of that operator is taken as the meaning of the relational pattern. Otherwise, the input value is converted to the type of `constant_expression` using an explicit nullable or unboxing conversion. It is a compile-time error if no such conversion exists. The pattern is considered to not match if the conversion fails. If the conversion succeeds, the result of the pattern-matching operation is the result of evaluating the expression `e «op» v` where `e` is the converted input, «op» is the relational operator, and `v` is the `constant_expression`. +When the input value has a type for which a suitable built-in binary relational operator is defined, the evaluation of that operator is taken as the meaning of the relational pattern. Otherwise, the input value is converted to the type of the constant expression using an explicit nullable or unboxing conversion. It is a compile-time error if no such conversion exists. The pattern is considered to not match if the conversion fails. If the conversion succeeds, the result of the pattern-matching operation is the result of evaluating the expression `e «op» v` where `e` is the converted input, «op» is the relational operator, and `v` is the constant expression. > *Example*: > From c1d5e9b6c82bea60e8716ff2259b011b431872bb Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 27 Feb 2026 15:41:53 -0500 Subject: [PATCH 11/16] Use consistent `ITuple` This was in the incorrect namespace --- standard/patterns.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/patterns.md b/standard/patterns.md index 94b52a30f..e42c6c0dc 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -558,7 +558,7 @@ In a *conjunctive_pattern*, the *input type* of the second pattern is narrowed b - If `P` is a type pattern, the *narrowed type* is the type of the type pattern's type. - Otherwise, if `P` is a declaration pattern, the *narrowed type* is the type of the declaration pattern's type. - Otherwise, if `P` is a recursive pattern that gives an explicit type, the *narrowed type* is that type. -- Otherwise, if `P` is matched via the rules for `ITuple` in a *positional_pattern* (§11.2.5), the *narrowed type* is the type `System.Runtime.CompilerServices.ITuple`. +- Otherwise, if `P` is matched via the rules for `ITuple` in a *positional_pattern* (§11.2.5), the *narrowed type* is the type `System.ITuple`. - Otherwise, if `P` is a constant pattern where the constant is not the null constant and where the expression has no *constant expression conversion* to the *input type*, the *narrowed type* is the type of the constant. - Otherwise, if `P` is a relational pattern where the constant expression has no *constant expression conversion* to the *input type*, the *narrowed type* is the type of the constant. - Otherwise, if `P` is an `or` pattern, the *narrowed type* is the common type of the *narrowed type* of the subpatterns if such a common type exists. For this purpose, the common type algorithm considers only identity, boxing, and implicit reference conversions, and it considers all subpatterns of a sequence of `or` patterns (ignoring parenthesized patterns). From 48009bdf92c1ea0099f4e5ae08ef830e1fc5160f Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 27 Feb 2026 15:44:05 -0500 Subject: [PATCH 12/16] Add consistent "Is applicable to" language MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Applicability rule for type pattern: "A type pattern naming a type T is applicable to every type E for which E is pattern compatible with T (§11.2.2)." Applicability rule for relational pattern: "A relational_pattern is applicable to a type T if a suitable built-in binary relational operator is defined with both operands of type T, or if an explicit nullable or unboxing conversion exists from T to the type of the constant expression." --- standard/patterns.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/standard/patterns.md b/standard/patterns.md index e42c6c0dc..040e9ffd7 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -470,6 +470,8 @@ type_pattern ; ``` +A type pattern naming a type `T` is *applicable to* every type `E` for which `E` is *pattern compatible* with `T` (§11.2.2). + The runtime type of the value is tested against *type* using the same rules specified in the is-type operator ([§12.14.12.1](expressions.md#1214121-the-is-type-operator)). If the test succeeds, the pattern matches that value. It is a compile-time error if the *type* is a nullable type. This pattern form never matches a `null` value. ### §relational-pattern-new-clause Relational pattern @@ -489,6 +491,8 @@ The *relational_expression* in a *relational_pattern* is required to evaluate to Relational patterns support the relational operators `<`, `<=`, `>`, and `>=` on all of the built-in types that support such binary relational operators with both operands having the same type: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, `nint`, `nuint`, and enums. +A *relational_pattern* is *applicable to* a type `T` if a suitable built-in binary relational operator is defined with both operands of type `T`, or if an explicit nullable or unboxing conversion exists from `T` to the type of the constant expression. + It is a compile-time error if the expression evaluates to `double.NaN`, `float.NaN`, or a null constant. When the input value has a type for which a suitable built-in binary relational operator is defined, the evaluation of that operator is taken as the meaning of the relational pattern. Otherwise, the input value is converted to the type of the constant expression using an explicit nullable or unboxing conversion. It is a compile-time error if no such conversion exists. The pattern is considered to not match if the conversion fails. If the conversion succeeds, the result of the pattern-matching operation is the result of evaluating the expression `e «op» v` where `e` is the converted input, «op» is the relational operator, and `v` is the constant expression. From 48a6efff934f9bb93f5dc6fc2c95f93323a271e9 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 27 Feb 2026 15:49:27 -0500 Subject: [PATCH 13/16] binding vs. precedence in the `is` statement. --- standard/patterns.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/standard/patterns.md b/standard/patterns.md index 040e9ffd7..31e5f1f09 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -526,7 +526,7 @@ When the input value has a type for which a suitable built-in binary relational ### §logical-pattern-new-clause Logical pattern -A *logical_pattern* is used to negate the result of matching a pattern input value ([§11.1](patterns.md#111-general)) or to combine that match result with a pattern using a Boolean operator. +A *logical_pattern* is used to negate the result of a pattern match, or to combine the results of multiple pattern matches using conjunction (`and`) or disjunction (`or`). ```ANTLR logical_pattern @@ -571,7 +571,7 @@ In a *conjunctive_pattern*, the *input type* of the second pattern is narrowed b > *Note*: As indicated by the grammar, `not` has precedence over `and`, which has precedence over `or`. This can be explicitly indicated or overridden by using parentheses. *end note* -When a *pattern* is used with `is`, any pattern operators in that *pattern* have higher precedence than their logical operator counterparts. Otherwise, those pattern operators have lower precedence. +When a *pattern* appears on the right-hand-side of `is`, the extent of the pattern is determined by the grammar; as a result, the pattern operators `and`, `or`, and `not` within the pattern bind more tightly than the logical operators `&&`, `||`, and `!` outside the pattern. > *Example*: > From 85e939d7f9a4f751afdc53ae86fe05e6c68f6573 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 3 Mar 2026 15:01:53 -0500 Subject: [PATCH 14/16] Add a couple notes --- standard/patterns.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/standard/patterns.md b/standard/patterns.md index 31e5f1f09..a43822843 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -639,9 +639,14 @@ When a *pattern* appears on the right-hand-side of `is`, the extent of the patte > > > ```csharp +> object msg = "msg"; > object obj = 5; > bool flag = true; > +> // This is parsed as: (msg is (not int) or string) +> result = msg is not int or string; +> Console.WriteLine($"msg (\"msg\"): msg is not int or string: {result}"); +> > // This is parsed as: (obj is (int or string)) && flag > bool result = obj is int or string && flag; > Console.WriteLine($"obj (5), flag (true): obj is int or string && flag: {result}"); @@ -663,6 +668,7 @@ When a *pattern* appears on the right-hand-side of `is`, the extent of the patte > The output produced is > > ```console +> msg ("msg"): msg is not int or string: True > obj (5), flag (true): obj is int or string && flag: True > obj (5), flag (true): obj is int || obj is string && flag: True > obj (5), flag (false): obj is int or string && flag: False @@ -675,7 +681,9 @@ When a *pattern* appears on the right-hand-side of `is`, the extent of the patte ## 11.3 Pattern subsumption In a switch statement ([§13.8.3](statements.md#1383-the-switch-statement)), it is an error if a case’s pattern is *subsumed* by the preceding set of *unguarded* ([§13.8.3](statements.md#1383-the-switch-statement)) cases. In a switch expression ([§12.11](expressions.md#1211-switch-expression)), it is an error if a *switch_expression_arm*’s pattern is *subsumed* by the preceding set of *unguarded* *switch_expression_arm*s’ patterns. + > *Note*: This means that any input value would have been matched by one of the previous cases or arms. *end note* + The following rules define when a set of patterns subsumes a given pattern: A pattern `P` *would match* a constant `K` if any of the following conditions hold: @@ -721,8 +729,7 @@ A set of patterns `Q` *subsumes* a pattern `P` if any of the following condition ## 11.4 Pattern exhaustiveness -Informally, a set of patterns is exhaustive for a type if, for every possible value of that type other than null, some pattern in the set is applicable. -The following rules define when a set of patterns is *exhaustive* for a type: +Informally, a set of patterns is exhaustive for a type if, for every possible value of that type other than null, some pattern in the set is applicable. The following rules define when a set of patterns is *exhaustive* for a type: A set of patterns `Q` is *exhaustive* for a type `T` if any of the following conditions hold: @@ -736,6 +743,9 @@ A set of patterns `Q` is *exhaustive* for a type `T` if any of the following con 1. Some pattern in `Q` is a *negated_pattern* `not P₁`, and the patterns in `Q` together with the values not matched by `P₁` cover every possible value of `T`. A *negated_pattern* `not P₁` is exhaustive by itself when `P₁` matches no possible value of `T`; or 1. Some pattern in `Q` is a *conjunctive_pattern* `P₁ and P₂`, and the set containing only `P₁` is *exhaustive* for `T` and the set containing only `P₂` is *exhaustive* for `T`. +> *Note*: When a type pattern includes nullable types, the pattern may be exhaustive for the type but still generate a warning because the type pattern won't match a `null` value. *end note* + + > *Note*: For floating-point types, the combination of patterns `< 0` and `>= 0` is *not* exhaustive because neither relational pattern matches `NaN`. A correct exhaustive set would be `< 0`, `>= 0`, and `double.NaN` (or `float.NaN`). *end note* > *Example*: @@ -756,3 +766,4 @@ A set of patterns `Q` is *exhaustive* for a type `T` if any of the following con > ``` > > *end example* + From 4bcefb2036e29efd80752aba4f8b98aaf3705d38 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 17 Apr 2026 15:56:26 -0400 Subject: [PATCH 15/16] Fix broken test code. --- standard/patterns.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/standard/patterns.md b/standard/patterns.md index a43822843..d16e46b67 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -644,11 +644,11 @@ When a *pattern* appears on the right-hand-side of `is`, the extent of the patte > bool flag = true; > > // This is parsed as: (msg is (not int) or string) -> result = msg is not int or string; +> bool result = msg is not int or string; > Console.WriteLine($"msg (\"msg\"): msg is not int or string: {result}"); > > // This is parsed as: (obj is (int or string)) && flag -> bool result = obj is int or string && flag; +> result = obj is int or string && flag; > Console.WriteLine($"obj (5), flag (true): obj is int or string && flag: {result}"); > > // This is parsed as: (obj is int) || ((obj is string) && flag) @@ -675,7 +675,6 @@ When a *pattern* appears on the right-hand-side of `is`, the extent of the patte > obj (5), flag (false): obj is int || obj is string && flag: True > ``` > -> > *end example* ## 11.3 Pattern subsumption From c099d5496971c2364232613a2d82c7c848d1871d Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 17 Apr 2026 17:08:28 -0400 Subject: [PATCH 16/16] Fix ANTLR grammar validator tests for pattern-matching enhancements Update grammar test infrastructure to match new pattern rule hierarchy: - pattern : logical_pattern (was 6 direct alternatives) - New rules: primary_pattern, parenthesized_pattern, type_pattern, relational_pattern, logical_pattern, disjunctive_pattern, conjunctive_pattern, negated_pattern Changes: - GrammarTestingEnv.tgz: Updated baseline grammar and Rules modification set to handle new pattern hierarchy. TypePatternPredicate now walks up the tree through intermediate pattern nodes to find relational_expression. - Reference tree files: 'is (type' -> 'is (pattern' since ReplacePatternWithType action is no longer needed (type_pattern is now a proper grammar rule). - RightShift stderr: Added 'and', 'not', 'or' to expected token sets (new pattern operator keywords). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tools/GrammarTesting/SetupAndTest | 329 ++++++++++++++++++ .../part-B/Reference/sample.gruntree.red.txt | 2 +- .../v6/RightShift/Reference/sample.stderr.txt | 2 +- .../Reference/sample.gruntree.red.txt | 8 +- .../Reference/sample.tree.red.txt | 2 +- 5 files changed, 336 insertions(+), 7 deletions(-) create mode 100755 tools/GrammarTesting/SetupAndTest diff --git a/tools/GrammarTesting/SetupAndTest b/tools/GrammarTesting/SetupAndTest new file mode 100755 index 000000000..fe86a25e5 --- /dev/null +++ b/tools/GrammarTesting/SetupAndTest @@ -0,0 +1,329 @@ +#!/bin/bash + +# Setup environment and then do checks, must pass in Standard source directory + +# Where are we? + +ScriptPath=$(realpath "${BASH_SOURCE[0]}") +ScriptDir=$(dirname "$ScriptPath") + +_usageDescription () +{ + cat <&2 + echo 1>&2 + _usageDescription 1>&2 + echo 1>&2 + exit $_optExitCode + else + echo + _usageDescription + echo + fi +} + +# scan all args + +doGrammar=false +doParsing=false +argSourceDirectory= +optWorkingDirectory= +optEnvPath= +optTestPath= + +declare -a doChecksArgs + +requireArgument= + +for i in "$@"; do + if [[ "$requireArgument" == "" ]]; then + case "$i" in + -g|--grammar) + doChecksArgs+=($i) + ;; + -p|--parsing) + doChecksArgs+=($i) + ;; + -d|--demo-parsing) + doChecksArgs+=($i) + ;; + -h|--help|-\?) + usage + exit + ;; + -v|--verbose) + beVerbose=true # so we can take note of this parameter ourselves + doChecksArgs+=($i) + ;; + -q|--quiet) + beQuiet=true # so we can take note of this parameter ourselves + doChecksArgs+=($i) + ;; + -dbg) + doChecksArgs+=($i) + ;; + -wd|--working-directory) + if [[ "$optWorkingDirectory" == "" ]]; then + requireArgument=$i + else + usage "Unexpected option $i" + fi + ;; + -wd=*|--working-directory=*) + if [[ "$optWorkingDirectory" == "" ]]; then + optWorkingDirectory="${i#*=}" + else + usage "Unexpected option $i" + fi + ;; + -e|--environment-directory) + if [[ "$optEnvPath" == "" ]]; then + requireArgument=$i + else + usage "Unexpected option $i" + fi + ;; + -e=*|--environment-directory=*) + if [[ "$optEnvPath" == "" ]]; then + optEnvPath="${i#*=}" + else + usage "Unexpected option $i" + fi + ;; + -t|--tests-directory) + if [[ "$optTestPath" == "" ]]; then + requireArgument=$i + else + usage "Unexpected option $i" + fi + ;; + -t=*|--tests-directory=*) + if [[ "$optTestPath" == "" ]]; then + optTestPath="${i#*=}" + else + usage "Unexpected option $i" + fi + ;; + -*) + usage "Unrecognised option $i" + ;; + *) + if [[ "$argSourceDirectory" == "" ]]; then + argSourceDirectory="$i" + elif [[ "$i" != "" ]]; then + usage "Unexpected argument ‘$i’" + fi + ;; + esac + else # looking for argument after option + if [[ "$i" == -* ]]; then + usage "Option $requireArgument missing argument" + else + case $requireArgument in + -wd|--working-directory) + optWorkingDirectory="$i" + requireArgument="" + ;; + -e|--environment-directory) + optEnvPath="$i" + requireArgument="" + ;; + -t|--tests-directory) + optTestPath="$i" + requireArgument="" + ;; + esac + if [[ "$requireArgument" != "" ]]; then + errorExit "Internal error, argument expected on none argument option, contact developer" + fi + fi + fi +done + +# check last command line option isn’t missing an argument + +if [[ "$requireArgument" != "" ]]; then + usage "Option $requireArgument missing argument" +fi + +if [[ "$argSourceDirectory" == "" ]]; then + usage "Missing sourceDirectory" +fi + +########################################################################################## + +# Args scanned, now locate environment directory. +# Try in order: +# - -e=path argument +# - Is there an Environment folder in the current WD? +# - Is there an Environment folder relative to the script source? +# If all fail barf + +envDirectoryName="Environment" +libRelativePath="Tools/lib/common.bash" +envPath= + +# first locate the environment +# check for script library within the folder as the (minimal) test for validity +if [[ ("$optEnvPath" != "") && (-f "${optEnvPath}/${libRelativePath}") ]]; then + envPath="${optEnvPath}" + envSrc="ARG" +elif [[ -f "${envDirectoryName}/${libRelativePath}" ]]; then + envPath="${envDirectoryName}" + envSrc="CWD" +elif [[ -f "${ScriptDir}/${envDirectoryName}/${libRelativePath}" ]]; then + envPath="${ScriptDir}/${envDirectoryName}" + envSrc="ADJ" +else + echo "Could not locate “${envDirectoryName}” directory" 1>&2 + echo "Consult the documentation" 1>&2 + exit -1 +fi + +envPath=$(realpath "$envPath") + +# Environment located, can now source our common library from the environment +source "${envPath}/${libRelativePath}" + +# we now have debugEcho… +debugEcho "envPath = [$envSrc] ${envPath}" + +########################################################################################## + +# Environment found, common source loaded, now locate the tests directory. +# this does not validate the structure of the tests folder in any way +# Try in order: +# - -t=path argument +# - Is there a Tests folder in the current WD? +# - Is there a Tests folder relative to the script source? +# If all fail barf + +# Much shorter than above as we can now use the common source version + +testPath= +searchForDirectory testPath "$optTestPath" "Tests" "" "" "$ScriptDir" "" + +# pass on to DoChecksAndTests below – avoids DoChecksAndTests needing to search (and possibly failing) +doChecksArgs+=("-t=${testPath}") + +########################################################################################## + +# Final checks and then source/call others to do the work + +# validate source directory and add to args +validateDirectoryPath sourceDirectory "$argSourceDirectory" "Invalid source directory \"$argSourceDirectory\"" -1 +doChecksArgs+=("$sourceDirectory") + +# create working directory if needed and add to command +if [[ "$optWorkingDirectory" != "" ]]; then + validateOrCreateDirectoryPath baseWorkingDirectory "$optWorkingDirectory" "Failed to create/access \"$optWorkingDirectory\"" + doChecksArgs+=("-wd=$baseWorkingDirectory") +fi + +# sanity precheck the environment +if [[ ! -f "${envPath}/setup.SourceMe" ]]; then + errorEcho "File ”setup.SourceMe” is missing in the environment directory." + errorEcho "($envPath)" + errorExit "Cannot setup the environment" +fi + +if [[ ${beVerbose} == true ]]; then + source "${envPath}/setup.SourceMe" +else + echo "Configuring environment..." + source "${envPath}/setup.SourceMe" >/dev/null +fi + +exitStatus=$? +if [[ $exitStatus != 0 ]]; then # did setup succeed? + exit $exitStatus +fi + +# sanity postcheck the environment +for i in antlr grun BuildGrammar; do + which $i >/dev/null + if [[ $? != 0 ]]; then + errorExit "Command “${i}” not found in environment" + fi +done + +for i in f:BG_BASHLIBRARY d:BG_MODIFICATIONSETS f:BG_GRUNTREE_GRAMMAR d:BG_LIB_PARSINGTESTS; do + _kind=${i:0:1} + _var=${i:2} + _target="${!_var}" + debugEcho "Checking: ${_kind} | ${_var}=${_target}" + if [[ "${_target}" == "" ]]; then + errorExit "Variable ${_var} not defined" + elif [[ (${_kind} == d) && (! -d "${_target}") ]]; then + errorExit "${_var} must reference a directory" + elif [[ (${_kind} == f) && (! -f "${_target}") ]]; then + errorExit "${_var} must reference a file" + fi +done + +# we seem to have what we expect, run the tests… + +# use a DoChecksAndTests in testPath +if [[ -x "${testPath}/DoChecksAndTests" ]]; then + "${testPath}/DoChecksAndTests" "${doChecksArgs[@]}" + exit $? +fi + +# check for DoChecksAndTests in the PATH +which DoChecksAndTests >/dev/null +if [[ $? == 0 ]]; then + DoChecksAndTests "${doChecksArgs[@]}" + exit $? +fi + +errorEcho "Script ”DoChecksAndTests” not found in the tests directory or PATH." +errorEcho "Tests directory: $testPath" +errorExit "Cannot run the tests" diff --git a/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-B/Reference/sample.gruntree.red.txt b/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-B/Reference/sample.gruntree.red.txt index c0e20fb6a..6226b8a2b 100644 --- a/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-B/Reference/sample.gruntree.red.txt +++ b/tools/GrammarTesting/Tests/Parsing/Samples/v6/AllInOneNoPreprocessor-v6-split/part-B/Reference/sample.gruntree.red.txt @@ -1213,7 +1213,7 @@ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ is ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ -⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ type +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ pattern ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ identifier ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ Action diff --git a/tools/GrammarTesting/Tests/Parsing/Samples/v6/RightShift/Reference/sample.stderr.txt b/tools/GrammarTesting/Tests/Parsing/Samples/v6/RightShift/Reference/sample.stderr.txt index 0aba53b52..852368317 100644 --- a/tools/GrammarTesting/Tests/Parsing/Samples/v6/RightShift/Reference/sample.stderr.txt +++ b/tools/GrammarTesting/Tests/Parsing/Samples/v6/RightShift/Reference/sample.stderr.txt @@ -1,3 +1,3 @@ line 10:12 no viable alternative at input '>' -line 10:14 extraneous input '>' expecting {'_', '-', '--', '!', '..', '(', '&', '^', '+', '++', '~', 'add', 'alias', 'ascending', 'async', 'await', 'base', 'bool', 'by', 'byte', 'char', 'checked', 'decimal', 'delegate', 'descending', 'double', 'dynamic', 'equals', 'float', 'from', 'get', 'global', 'group', 'int', 'into', 'join', 'let', 'long', 'nameof', 'new', 'notnull', 'object', 'on', 'orderby', 'partial', 'remove', 'sbyte', 'select', 'set', 'short', 'sizeof', 'stackalloc', 'string', 'this', 'typeof', 'uint', 'ulong', 'unchecked', 'unmanaged', 'ushort', 'value', 'var', 'when', 'where', 'yield', 'default', 'null', 'true', 'false', '*', Simple_Identifier, Integer_Literal, Real_Literal, Character_Literal, String_Literal, Interpolated_Regular_String_Start, Interpolated_Verbatim_String_Start} +line 10:14 extraneous input '>' expecting {'_', '-', '--', '!', '..', '(', '&', '^', '+', '++', '~', 'add', 'alias', 'and', 'ascending', 'async', 'await', 'base', 'bool', 'by', 'byte', 'char', 'checked', 'decimal', 'delegate', 'descending', 'double', 'dynamic', 'equals', 'float', 'from', 'get', 'global', 'group', 'int', 'into', 'join', 'let', 'long', 'nameof', 'new', 'not', 'notnull', 'object', 'on', 'or', 'orderby', 'partial', 'remove', 'sbyte', 'select', 'set', 'short', 'sizeof', 'stackalloc', 'string', 'this', 'typeof', 'uint', 'ulong', 'unchecked', 'unmanaged', 'ushort', 'value', 'var', 'when', 'where', 'yield', 'default', 'null', 'true', 'false', '*', Simple_Identifier, Integer_Literal, Real_Literal, Character_Literal, String_Literal, Interpolated_Regular_String_Start, Interpolated_Verbatim_String_Start} line 11:8 no viable alternative at input '>' diff --git a/tools/GrammarTesting/Tests/Parsing/Samples/v8/Type argument list/Reference/sample.gruntree.red.txt b/tools/GrammarTesting/Tests/Parsing/Samples/v8/Type argument list/Reference/sample.gruntree.red.txt index b0f97e8bc..99a360f15 100644 --- a/tools/GrammarTesting/Tests/Parsing/Samples/v8/Type argument list/Reference/sample.gruntree.red.txt +++ b/tools/GrammarTesting/Tests/Parsing/Samples/v8/Type argument list/Reference/sample.gruntree.red.txt @@ -482,7 +482,7 @@ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ is ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ -⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ type +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ pattern ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ namespace_or_type_name ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ @@ -1022,7 +1022,7 @@ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ is ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ -⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ type +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ pattern ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ namespace_or_type_name ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ @@ -1215,7 +1215,7 @@ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ is ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ -⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ type +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ pattern ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ namespace_or_type_name ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ @@ -1349,7 +1349,7 @@ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎝ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ is ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ -⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ type +⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ pattern ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ namespace_or_type_name ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎜ ⎛ diff --git a/tools/GrammarTesting/Tests/Parsing/Samples/v8/Type argument list/Reference/sample.tree.red.txt b/tools/GrammarTesting/Tests/Parsing/Samples/v8/Type argument list/Reference/sample.tree.red.txt index d86cb4033..6bcb6219b 100644 --- a/tools/GrammarTesting/Tests/Parsing/Samples/v8/Type argument list/Reference/sample.tree.red.txt +++ b/tools/GrammarTesting/Tests/Parsing/Samples/v8/Type argument list/Reference/sample.tree.red.txt @@ -1 +1 @@ -(prog (compilation_unit (class_declaration class (identifier TypeArgumentListChecks) (class_body { (class_member_declaration (method_declaration method_modifiers (return_type void) (method_header (member_name (identifier TypeArgumentListChecks)) ( )) (method_body (block { (statement_list (statement (expression_statement (statement_expression (invocation_expression (primary_expression (identifier F)) ( (argument_list (invocation_expression (primary_expression (simple_name (identifier G) (type_argument_list < (type_argument (identifier A)) , (type_argument (identifier B)) >))) ( (argument_list (literal 7)) ))) ))) ;)) (statement (expression_statement (statement_expression (invocation_expression (primary_expression (identifier F)) ( (argument_list (invocation_expression (primary_expression (base_access base . (identifier G) (type_argument_list < (type_argument (identifier A)) , (type_argument (identifier B)) >))) ( (argument_list (literal 7)) ))) ))) ;)) (statement (expression_statement (statement_expression (invocation_expression (primary_expression (identifier F)) ( (argument_list (argument (relational_expression (relational_expression (identifier G)) < (shift_expression (identifier A)))) , (argument (relational_expression (relational_expression (identifier B)) > (shift_expression (literal 7))))) ))) ;)) (statement (expression_statement (statement_expression (invocation_expression (primary_expression (identifier F)) ( (argument_list (argument (relational_expression (relational_expression (base_access base . (identifier G))) < (shift_expression (identifier A)))) , (argument (relational_expression (relational_expression (identifier B)) > (shift_expression (literal 7))))) ))) ;)) (statement (expression_statement (statement_expression (invocation_expression (primary_expression (identifier F)) ( (argument_list (argument (relational_expression (relational_expression (identifier G)) < (shift_expression (identifier A)))) , (argument (shift_expression (shift_expression (identifier B)) (right_shift > >) (additive_expression (literal 7))))) ))) ;)) (statement (expression_statement (statement_expression (simple_assignment (unary_expression (identifier x)) = (expression (relational_expression (relational_expression (relational_expression (identifier F)) < (shift_expression (identifier A))) > (shift_expression (unary_expression + (unary_expression (identifier y)))))))) ;)) (statement (expression_statement (statement_expression (simple_assignment (unary_expression (identifier x)) = (expression (conditional_and_expression (conditional_and_expression (relational_expression (relational_expression (identifier y)) is (type (namespace_or_type_name (identifier C) (type_argument_list < (type_argument (identifier T)) >))))) && (inclusive_or_expression (identifier z)))))) ;)) (statement (declaration_statement (local_variable_declaration (implicitly_typed_local_variable_declaration var (implicitly_typed_local_variable_declarator (identifier v) = (expression (tuple_literal ( (tuple_element (relational_expression (relational_expression (identifier A)) < (shift_expression (identifier B)))) , (tuple_element (identifier C)) )))))) ;)) (statement (declaration_statement (local_variable_declaration (implicitly_typed_local_variable_declaration var (implicitly_typed_local_variable_declarator (identifier v) = (expression (tuple_literal ( (tuple_element (relational_expression (relational_expression (identifier A)) < (shift_expression (identifier B)))) , (tuple_element (relational_expression (relational_expression (identifier C)) > (shift_expression (identifier D)))) )))))) ;)) (statement (declaration_statement (local_variable_declaration (implicitly_typed_local_variable_declaration var (implicitly_typed_local_variable_declarator (identifier v) = (expression (tuple_literal ( (tuple_element (relational_expression (relational_expression (identifier A)) < (shift_expression (identifier B)))) , (tuple_element (relational_expression (relational_expression (identifier C)) > (shift_expression (identifier D)))) , (tuple_element (identifier E)) )))))) ;)) (statement (declaration_statement (local_variable_declaration (implicitly_typed_local_variable_declaration var (implicitly_typed_local_variable_declarator (identifier v) = (expression (invocation_expression (primary_expression (identifier M)) ( (argument_list (argument (relational_expression (relational_expression (identifier A)) < (shift_expression (identifier B)))) , (argument (relational_expression (relational_expression (identifier C)) > (shift_expression (identifier D)))) , (argument (identifier E))) )))))) ;)) (statement (declaration_statement (local_variable_declaration (implicitly_typed_local_variable_declaration var (implicitly_typed_local_variable_declarator (identifier v) = (expression (invocation_expression (primary_expression (identifier M)) ( (argument_list (argument (argument_value out (declaration_expression (local_variable_type (namespace_or_type_name (identifier A) (type_argument_list < (type_argument (identifier B)) , (type_argument (identifier C)) >))) (identifier D)))) , (argument (identifier E))) )))))) ;)) (statement (if_statement if ( (boolean_expression (relational_expression (relational_expression (identifier e)) is (pattern (declaration_pattern (type (namespace_or_type_name (identifier A) (type_argument_list < (type_argument (identifier B)) >))) (simple_designation (identifier C)))))) ) (embedded_statement (expression_statement (statement_expression (invocation_expression (primary_expression (identifier W)) ( (argument_list (identifier C)) ))) ;)))) (statement (if_statement if ( (boolean_expression (relational_expression (relational_expression (identifier e)) is (type (namespace_or_type_name (identifier A) (type_argument_list < (type_argument (identifier B)) >))))) ) (embedded_statement (expression_statement (statement_expression (invocation_expression (primary_expression (identifier W)) ( (argument_list (identifier C)) ))) ;)))) (statement (switch_statement switch (selector_expression ( (expression (identifier x)) )) (switch_block { (switch_section (switch_label case (pattern (declaration_pattern (type (namespace_or_type_name (identifier A) (type_argument_list < (type_argument (identifier B)) >))) (simple_designation (identifier C)))) :) (statement_list (statement (declaration_statement (local_variable_declaration (implicitly_typed_local_variable_declaration var (implicitly_typed_local_variable_declarator (identifier z) = (expression (identifier C))))) ;)) (statement (break_statement break ;)))) }))) (statement (declaration_statement (local_variable_declaration (implicitly_typed_local_variable_declaration var (implicitly_typed_local_variable_declarator (identifier p) = (expression (relational_expression (relational_expression (relational_expression (identifier x)) is (type (namespace_or_type_name (identifier A) (type_argument_list < (type_argument (identifier B)) >)))) > (shift_expression (identifier C))))))) ;)) (statement (declaration_statement (local_variable_declaration (implicitly_typed_local_variable_declaration var (implicitly_typed_local_variable_declarator (identifier q) = (expression (relational_expression (relational_expression (identifier A)) < (shift_expression (shift_expression (identifier B)) (right_shift > >) (additive_expression (identifier C)))))))) ;)) (statement (declaration_statement (local_variable_declaration (implicitly_typed_local_variable_declaration var (implicitly_typed_local_variable_declarator (identifier r) = (expression (relational_expression (relational_expression (parenthesized_expression ( (expression (relational_expression (relational_expression (identifier x)) is (type (namespace_or_type_name (identifier A) (type_argument_list < (type_argument (identifier B)) >))))) ))) > (shift_expression (identifier C))))))) ;)) (statement (declaration_statement (local_variable_declaration (implicitly_typed_local_variable_declaration var (implicitly_typed_local_variable_declarator (identifier s) = (expression (relational_expression (relational_expression (identifier x)) is (pattern (declaration_pattern (type (namespace_or_type_name (identifier A) (type_argument_list < (type_argument (identifier B)) >))) (simple_designation (contextual_keyword orderby))))))))) ;))) })))) })))) +(prog (compilation_unit (class_declaration class (identifier TypeArgumentListChecks) (class_body { (class_member_declaration (method_declaration method_modifiers (return_type void) (method_header (member_name (identifier TypeArgumentListChecks)) ( )) (method_body (block { (statement_list (statement (expression_statement (statement_expression (invocation_expression (primary_expression (identifier F)) ( (argument_list (invocation_expression (primary_expression (simple_name (identifier G) (type_argument_list < (type_argument (identifier A)) , (type_argument (identifier B)) >))) ( (argument_list (literal 7)) ))) ))) ;)) (statement (expression_statement (statement_expression (invocation_expression (primary_expression (identifier F)) ( (argument_list (invocation_expression (primary_expression (base_access base . (identifier G) (type_argument_list < (type_argument (identifier A)) , (type_argument (identifier B)) >))) ( (argument_list (literal 7)) ))) ))) ;)) (statement (expression_statement (statement_expression (invocation_expression (primary_expression (identifier F)) ( (argument_list (argument (relational_expression (relational_expression (identifier G)) < (shift_expression (identifier A)))) , (argument (relational_expression (relational_expression (identifier B)) > (shift_expression (literal 7))))) ))) ;)) (statement (expression_statement (statement_expression (invocation_expression (primary_expression (identifier F)) ( (argument_list (argument (relational_expression (relational_expression (base_access base . (identifier G))) < (shift_expression (identifier A)))) , (argument (relational_expression (relational_expression (identifier B)) > (shift_expression (literal 7))))) ))) ;)) (statement (expression_statement (statement_expression (invocation_expression (primary_expression (identifier F)) ( (argument_list (argument (relational_expression (relational_expression (identifier G)) < (shift_expression (identifier A)))) , (argument (shift_expression (shift_expression (identifier B)) (right_shift > >) (additive_expression (literal 7))))) ))) ;)) (statement (expression_statement (statement_expression (simple_assignment (unary_expression (identifier x)) = (expression (relational_expression (relational_expression (relational_expression (identifier F)) < (shift_expression (identifier A))) > (shift_expression (unary_expression + (unary_expression (identifier y)))))))) ;)) (statement (expression_statement (statement_expression (simple_assignment (unary_expression (identifier x)) = (expression (conditional_and_expression (conditional_and_expression (relational_expression (relational_expression (identifier y)) is (pattern (namespace_or_type_name (identifier C) (type_argument_list < (type_argument (identifier T)) >))))) && (inclusive_or_expression (identifier z)))))) ;)) (statement (declaration_statement (local_variable_declaration (implicitly_typed_local_variable_declaration var (implicitly_typed_local_variable_declarator (identifier v) = (expression (tuple_literal ( (tuple_element (relational_expression (relational_expression (identifier A)) < (shift_expression (identifier B)))) , (tuple_element (identifier C)) )))))) ;)) (statement (declaration_statement (local_variable_declaration (implicitly_typed_local_variable_declaration var (implicitly_typed_local_variable_declarator (identifier v) = (expression (tuple_literal ( (tuple_element (relational_expression (relational_expression (identifier A)) < (shift_expression (identifier B)))) , (tuple_element (relational_expression (relational_expression (identifier C)) > (shift_expression (identifier D)))) )))))) ;)) (statement (declaration_statement (local_variable_declaration (implicitly_typed_local_variable_declaration var (implicitly_typed_local_variable_declarator (identifier v) = (expression (tuple_literal ( (tuple_element (relational_expression (relational_expression (identifier A)) < (shift_expression (identifier B)))) , (tuple_element (relational_expression (relational_expression (identifier C)) > (shift_expression (identifier D)))) , (tuple_element (identifier E)) )))))) ;)) (statement (declaration_statement (local_variable_declaration (implicitly_typed_local_variable_declaration var (implicitly_typed_local_variable_declarator (identifier v) = (expression (invocation_expression (primary_expression (identifier M)) ( (argument_list (argument (relational_expression (relational_expression (identifier A)) < (shift_expression (identifier B)))) , (argument (relational_expression (relational_expression (identifier C)) > (shift_expression (identifier D)))) , (argument (identifier E))) )))))) ;)) (statement (declaration_statement (local_variable_declaration (implicitly_typed_local_variable_declaration var (implicitly_typed_local_variable_declarator (identifier v) = (expression (invocation_expression (primary_expression (identifier M)) ( (argument_list (argument (argument_value out (declaration_expression (local_variable_type (namespace_or_type_name (identifier A) (type_argument_list < (type_argument (identifier B)) , (type_argument (identifier C)) >))) (identifier D)))) , (argument (identifier E))) )))))) ;)) (statement (if_statement if ( (boolean_expression (relational_expression (relational_expression (identifier e)) is (pattern (declaration_pattern (type (namespace_or_type_name (identifier A) (type_argument_list < (type_argument (identifier B)) >))) (simple_designation (identifier C)))))) ) (embedded_statement (expression_statement (statement_expression (invocation_expression (primary_expression (identifier W)) ( (argument_list (identifier C)) ))) ;)))) (statement (if_statement if ( (boolean_expression (relational_expression (relational_expression (identifier e)) is (pattern (namespace_or_type_name (identifier A) (type_argument_list < (type_argument (identifier B)) >))))) ) (embedded_statement (expression_statement (statement_expression (invocation_expression (primary_expression (identifier W)) ( (argument_list (identifier C)) ))) ;)))) (statement (switch_statement switch (selector_expression ( (expression (identifier x)) )) (switch_block { (switch_section (switch_label case (pattern (declaration_pattern (type (namespace_or_type_name (identifier A) (type_argument_list < (type_argument (identifier B)) >))) (simple_designation (identifier C)))) :) (statement_list (statement (declaration_statement (local_variable_declaration (implicitly_typed_local_variable_declaration var (implicitly_typed_local_variable_declarator (identifier z) = (expression (identifier C))))) ;)) (statement (break_statement break ;)))) }))) (statement (declaration_statement (local_variable_declaration (implicitly_typed_local_variable_declaration var (implicitly_typed_local_variable_declarator (identifier p) = (expression (relational_expression (relational_expression (relational_expression (identifier x)) is (pattern (namespace_or_type_name (identifier A) (type_argument_list < (type_argument (identifier B)) >)))) > (shift_expression (identifier C))))))) ;)) (statement (declaration_statement (local_variable_declaration (implicitly_typed_local_variable_declaration var (implicitly_typed_local_variable_declarator (identifier q) = (expression (relational_expression (relational_expression (identifier A)) < (shift_expression (shift_expression (identifier B)) (right_shift > >) (additive_expression (identifier C)))))))) ;)) (statement (declaration_statement (local_variable_declaration (implicitly_typed_local_variable_declaration var (implicitly_typed_local_variable_declarator (identifier r) = (expression (relational_expression (relational_expression (parenthesized_expression ( (expression (relational_expression (relational_expression (identifier x)) is (pattern (namespace_or_type_name (identifier A) (type_argument_list < (type_argument (identifier B)) >))))) ))) > (shift_expression (identifier C))))))) ;)) (statement (declaration_statement (local_variable_declaration (implicitly_typed_local_variable_declaration var (implicitly_typed_local_variable_declarator (identifier s) = (expression (relational_expression (relational_expression (identifier x)) is (pattern (declaration_pattern (type (namespace_or_type_name (identifier A) (type_argument_list < (type_argument (identifier B)) >))) (simple_designation (contextual_keyword orderby))))))))) ;))) })))) }))))