From 38283a38b5bb30e22267517b2db694180ff339de Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Dec 2025 03:42:43 +0000 Subject: [PATCH 1/2] Initial plan From b8d51af55a90e9093a8a811f88d9d08342a5d653 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Dec 2025 03:50:42 +0000 Subject: [PATCH 2/2] Add tests for ref scoped modifier Co-authored-by: JoeRobich <611219+JoeRobich@users.noreply.github.com> --- grammars/csharp.tmLanguage | 18 ++++++++++------ grammars/csharp.tmLanguage.cson | 15 +++++++------ src/csharp.tmLanguage.yml | 24 +++++++++++---------- test/local.tests.ts | 38 +++++++++++++++++++++++++++++++++ test/method.tests.ts | 31 +++++++++++++++++++++++++++ 5 files changed, 103 insertions(+), 23 deletions(-) diff --git a/grammars/csharp.tmLanguage b/grammars/csharp.tmLanguage index 9d66b73..985acac 100644 --- a/grammars/csharp.tmLanguage +++ b/grammars/csharp.tmLanguage @@ -4799,13 +4799,13 @@ begin (?x) (?: - (?:(\bscoped)\s+(?:(\bref)\s+)?)? # scoped local - (?:(\bref)\s+(?:(\breadonly)\s+)?)? # ref local + (?:(\bscoped)\s+(?:(\bref)\s+)?)? # scoped ref local + (?:(\bref)\s+(?:(\breadonly|scoped)\s+)?)? # ref readonly or ref scoped local (\bvar\b)| (?<type_name> (?: - (?:scoped\s+(?:ref\s+)?)? # scoped local - (?:ref\s+(?:readonly\s+)?)? # ref local + (?:scoped\s+(?:ref\s+)?)? # scoped ref local + (?:ref\s+(?:(?:readonly|scoped)\s+)?)? # ref, optionally followed by readonly or scoped (?: (?:(?<identifier>@?[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification (?<name_and_type_args> # identifier + type arguments (if any) @@ -4850,7 +4850,7 @@ 4 name - storage.modifier.readonly.cs + storage.modifier.$4.cs 5 @@ -7298,6 +7298,7 @@ match (?x) (?:(?:\b(scoped|ref|params|out|in|this)\b)\s+)? +(?:(?:\b(ref|scoped|readonly)\b)\s+)? (?<type_name> (?: (?:ref\s+)? # ref return @@ -7330,6 +7331,11 @@ storage.modifier.$1.cs 2 + + name + storage.modifier.$2.cs + + 3 patterns @@ -7339,7 +7345,7 @@ - 7 + 8 name entity.name.variable.parameter.cs diff --git a/grammars/csharp.tmLanguage.cson b/grammars/csharp.tmLanguage.cson index 86263bf..24e4551 100644 --- a/grammars/csharp.tmLanguage.cson +++ b/grammars/csharp.tmLanguage.cson @@ -2931,13 +2931,13 @@ repository: begin: ''' (?x) (?: - (?:(\\bscoped)\\s+(?:(\\bref)\\s+)?)? # scoped local - (?:(\\bref)\\s+(?:(\\breadonly)\\s+)?)? # ref local + (?:(\\bscoped)\\s+(?:(\\bref)\\s+)?)? # scoped ref local + (?:(\\bref)\\s+(?:(\\breadonly|scoped)\\s+)?)? # ref readonly or ref scoped local (\\bvar\\b)| (? (?: - (?:scoped\\s+(?:ref\\s+)?)? # scoped local - (?:ref\\s+(?:readonly\\s+)?)? # ref local + (?:scoped\\s+(?:ref\\s+)?)? # scoped ref local + (?:ref\\s+(?:(?:readonly|scoped)\\s+)?)? # ref, optionally followed by readonly or scoped (?: (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification (? # identifier + type arguments (if any) @@ -2971,7 +2971,7 @@ repository: "3": name: "storage.modifier.ref.cs" "4": - name: "storage.modifier.readonly.cs" + name: "storage.modifier.$4.cs" "5": name: "storage.type.var.cs" "6": @@ -4401,6 +4401,7 @@ repository: match: ''' (?x) (?:(?:\\b(scoped|ref|params|out|in|this)\\b)\\s+)? + (?:(?:\\b(ref|scoped|readonly)\\b)\\s+)? (? (?: (?:ref\\s+)? # ref return @@ -4430,12 +4431,14 @@ repository: "1": name: "storage.modifier.$1.cs" "2": + name: "storage.modifier.$2.cs" + "3": patterns: [ { include: "#type" } ] - "7": + "8": name: "entity.name.variable.parameter.cs" "argument-list": begin: "\\(" diff --git a/src/csharp.tmLanguage.yml b/src/csharp.tmLanguage.yml index d99b878..c161052 100644 --- a/src/csharp.tmLanguage.yml +++ b/src/csharp.tmLanguage.yml @@ -1739,13 +1739,13 @@ repository: begin: |- (?x) (?: - (?:(\bscoped)\s+(?:(\bref)\s+)?)? # scoped local - (?:(\bref)\s+(?:(\breadonly)\s+)?)? # ref local + (?:(\bscoped)\s+(?:(\bref)\s+)?)? # scoped ref local + (?:(\bref)\s+(?:(\breadonly|scoped)\s+)?)? # ref readonly or ref scoped local (\bvar\b)| (? (?: - (?:scoped\s+(?:ref\s+)?)? # scoped local - (?:ref\s+(?:readonly\s+)?)? # ref local + (?:scoped\s+(?:ref\s+)?)? # scoped ref local + (?:ref\s+(?:(?:readonly|scoped)\s+)?)? # ref, optionally followed by readonly or scoped (?: (?:(?@?[_[:alpha:]][_[:alnum:]]*)\s*\:\:\s*)? # alias-qualification (? # identifier + type arguments (if any) @@ -1774,7 +1774,7 @@ repository: 1: { name: storage.modifier.scoped.cs } 2: { name: storage.modifier.ref.cs } 3: { name: storage.modifier.ref.cs } - 4: { name: storage.modifier.readonly.cs } + 4: { name: storage.modifier.$4.cs } 5: { name: storage.type.var.cs } 6: patterns: @@ -2835,6 +2835,7 @@ repository: match: |- (?x) (?:(?:\b(scoped|ref|params|out|in|this)\b)\s+)? + (?:(?:\b(ref|scoped|readonly)\b)\s+)? (? (?: (?:ref\s+)? # ref return @@ -2861,14 +2862,15 @@ repository: (\g) captures: 1: { name: storage.modifier.$1.cs } - 2: + 2: { name: storage.modifier.$2.cs } + 3: patterns: - include: '#type' - # '3': ? is a sub-expression. It's final value is not considered. - # '4': ? is a sub-expression. It's final value is not considered. - # '5': ? is a sub-expression. It's final value is not considered. - # '6': ? is a sub-expression. It's final value is not considered. - 7: { name: entity.name.variable.parameter.cs } + # '4': ? is a sub-expression. It's final value is not considered. + # '5': ? is a sub-expression. It's final value is not considered. + # '6': ? is a sub-expression. It's final value is not considered. + # '7': ? is a sub-expression. It's final value is not considered. + 8: { name: entity.name.variable.parameter.cs } argument-list: begin: \( diff --git a/test/local.tests.ts b/test/local.tests.ts index 4077bf3..988c9e6 100644 --- a/test/local.tests.ts +++ b/test/local.tests.ts @@ -169,6 +169,44 @@ describe("Locals", () => { ]); }); + it("ref scoped local", async () => { + const input = Input.InMethod(`ref scoped Span x = stackalloc int[42];`); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Keyword.Modifier.Ref, + Token.Keyword.Modifier.Scoped, + Token.Type("Span"), + Token.Punctuation.TypeParameter.Begin, + Token.PrimitiveType.Int, + Token.Punctuation.TypeParameter.End, + Token.Identifier.LocalName("x"), + Token.Operator.Assignment, + Token.Operator.Expression.StackAlloc, + Token.PrimitiveType.Int, + Token.Punctuation.OpenBracket, + Token.Literal.Numeric.Decimal("42"), + Token.Punctuation.CloseBracket, + Token.Punctuation.Semicolon + ]); + }); + + it("ref scoped local var", async () => { + const input = Input.InMethod(`ref scoped var x = ref y;`); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Keyword.Modifier.Ref, + Token.Keyword.Modifier.Scoped, + Token.Keyword.Definition.Var, + Token.Identifier.LocalName("x"), + Token.Operator.Assignment, + Token.Keyword.Modifier.Ref, + Token.Variable.ReadWrite("y"), + Token.Punctuation.Semicolon + ]); + }); + it("ref local", async () => { const input = Input.InMethod(`ref int x;`); const tokens = await tokenize(input); diff --git a/test/method.tests.ts b/test/method.tests.ts index edaba0b..b57f9f2 100644 --- a/test/method.tests.ts +++ b/test/method.tests.ts @@ -857,6 +857,37 @@ public static Span CreateSpan(scoped ref T reference, int length) {} ]); }); + it("ref scoped parameter in parameter list (#306)", async () => { + const input = Input.InClass(` +public static Span CreateSpan(ref scoped T reference, int length) {} +`); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Keyword.Modifier.Public, + Token.Keyword.Modifier.Static, + Token.Type("Span"), + Token.Punctuation.TypeParameter.Begin, + Token.Type("T"), + Token.Punctuation.TypeParameter.End, + Token.Identifier.MethodName("CreateSpan"), + Token.Punctuation.TypeParameter.Begin, + Token.Identifier.TypeParameterName("T"), + Token.Punctuation.TypeParameter.End, + Token.Punctuation.OpenParen, + Token.Keyword.Modifier.Ref, + Token.Keyword.Modifier.Scoped, + Token.Type("T"), + Token.Identifier.ParameterName("reference"), + Token.Punctuation.Comma, + Token.PrimitiveType.Int, + Token.Identifier.ParameterName("length"), + Token.Punctuation.CloseParen, + Token.Punctuation.OpenBrace, + Token.Punctuation.CloseBrace + ]); + }); + it("expression body and type constraint (issue #74)", async () => { const input = Input.InClass(` T id1(T a) => a;