From e18682f0ff84ac5cf3347902e7274bc5e14426a1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Dec 2025 02:25:57 +0000 Subject: [PATCH 1/3] Initial plan From d4b2a75a6b82ea121471572f128a0fdaf2602eb3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Dec 2025 02:32:57 +0000 Subject: [PATCH 2/3] Add tests for var in tuple deconstruction with discards Co-authored-by: JoeRobich <611219+JoeRobich@users.noreply.github.com> --- test/tuple.tests.ts | 55 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/test/tuple.tests.ts b/test/tuple.tests.ts index f18c8df..bb58fe0 100644 --- a/test/tuple.tests.ts +++ b/test/tuple.tests.ts @@ -423,5 +423,60 @@ describe("Tuples", () => { Token.Punctuation.CloseBrace ]); }); + + it("Deconstruct with mixed type and var (int _, var _)", async () => { + const input = Input.InMethod(`(int _, var _) = (1, 2);`); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Punctuation.OpenParen, + Token.PrimitiveType.Int, + Token.Identifier.TupleElementName("_"), + Token.Punctuation.Comma, + Token.Keyword.Definition.Var, + Token.Identifier.TupleElementName("_"), + Token.Punctuation.CloseParen, + Token.Operator.Assignment, + Token.Punctuation.OpenParen, + Token.Literal.Numeric.Decimal("1"), + Token.Punctuation.Comma, + Token.Literal.Numeric.Decimal("2"), + Token.Punctuation.CloseParen, + Token.Punctuation.Semicolon + ]); + }); + + it("Deconstruct with all vars and discards (var _, var _, var _)", async () => { + const input = Input.InMethod(`(var _, var _, var _) = ('a', 'b', 'c');`); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Punctuation.OpenParen, + Token.Keyword.Definition.Var, + Token.Identifier.TupleElementName("_"), + Token.Punctuation.Comma, + Token.Keyword.Definition.Var, + Token.Identifier.TupleElementName("_"), + Token.Punctuation.Comma, + Token.Keyword.Definition.Var, + Token.Identifier.TupleElementName("_"), + Token.Punctuation.CloseParen, + Token.Operator.Assignment, + Token.Punctuation.OpenParen, + Token.Punctuation.Char.Begin, + Token.Literal.Char("a"), + Token.Punctuation.Char.End, + Token.Punctuation.Comma, + Token.Punctuation.Char.Begin, + Token.Literal.Char("b"), + Token.Punctuation.Char.End, + Token.Punctuation.Comma, + Token.Punctuation.Char.Begin, + Token.Literal.Char("c"), + Token.Punctuation.Char.End, + Token.Punctuation.CloseParen, + Token.Punctuation.Semicolon + ]); + }); }); }); From f83faae2e89376cd953991cd092e3462672e2b0c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Dec 2025 03:04:30 +0000 Subject: [PATCH 3/3] Fix var keyword tokenization in tuple deconstruction at top level Add local-tuple-declaration-deconstruction pattern to handle tuple deconstruction with type/var declarations like (int x, var y) = ... at the statement level. This fixes the issue where var was incorrectly tokenized as a variable name instead of a keyword when used in top-level tuple deconstruction. Co-authored-by: JoeRobich <611219+JoeRobich@users.noreply.github.com> --- grammars/csharp.tmLanguage | 24 ++++++++++++++ grammars/csharp.tmLanguage.cson | 16 ++++++++++ src/csharp.tmLanguage.yml | 11 +++++++ test/tuple.tests.ts | 55 +++++++++++++++++++++++++++++++++ 4 files changed, 106 insertions(+) diff --git a/grammars/csharp.tmLanguage b/grammars/csharp.tmLanguage index 3906d58..81ce2a8 100644 --- a/grammars/csharp.tmLanguage +++ b/grammars/csharp.tmLanguage @@ -4878,6 +4878,10 @@ include #local-tuple-var-deconstruction + + include + #local-tuple-declaration-deconstruction + local-variable-declaration @@ -5176,6 +5180,26 @@ + local-tuple-declaration-deconstruction + + match + (?x) # e.g. (int x, var y) = GetPoint(); +(?<tuple>\((?:[^\(\)]|\g<tuple>)+\))\s* +(?!=>|==)(?==) + captures + + 1 + + patterns + + + include + #tuple-declaration-deconstruction-element-list + + + + + tuple-deconstruction-assignment match diff --git a/grammars/csharp.tmLanguage.cson b/grammars/csharp.tmLanguage.cson index 0fb74a7..650104c 100644 --- a/grammars/csharp.tmLanguage.cson +++ b/grammars/csharp.tmLanguage.cson @@ -2984,6 +2984,9 @@ repository: { include: "#local-tuple-var-deconstruction" } + { + include: "#local-tuple-declaration-deconstruction" + } ] "local-variable-declaration": begin: ''' @@ -3196,6 +3199,19 @@ repository: include: "#variable-initializer" } ] + "local-tuple-declaration-deconstruction": + match: ''' + (?x) # e.g. (int x, var y) = GetPoint(); + (?\\((?:[^\\(\\)]|\\g)+\\))\\s* + (?!=>|==)(?==) + ''' + captures: + "1": + patterns: [ + { + include: "#tuple-declaration-deconstruction-element-list" + } + ] "tuple-deconstruction-assignment": match: ''' (?x) diff --git a/src/csharp.tmLanguage.yml b/src/csharp.tmLanguage.yml index 4576f37..6dd0cf9 100644 --- a/src/csharp.tmLanguage.yml +++ b/src/csharp.tmLanguage.yml @@ -1770,6 +1770,7 @@ repository: - include: '#local-variable-declaration' - include: '#local-function-declaration' - include: '#local-tuple-var-deconstruction' + - include: '#local-tuple-declaration-deconstruction' local-variable-declaration: begin: |- @@ -1930,6 +1931,16 @@ repository: - include: '#comment' - include: '#variable-initializer' + local-tuple-declaration-deconstruction: + match: |- + (?x) # e.g. (int x, var y) = GetPoint(); + (?\((?:[^\(\)]|\g)+\))\s* + (?!=>|==)(?==) + captures: + '1': + patterns: + - include: '#tuple-declaration-deconstruction-element-list' + tuple-deconstruction-assignment: match: |- (?x) diff --git a/test/tuple.tests.ts b/test/tuple.tests.ts index bb58fe0..7f37a42 100644 --- a/test/tuple.tests.ts +++ b/test/tuple.tests.ts @@ -478,5 +478,60 @@ describe("Tuples", () => { Token.Punctuation.Semicolon ]); }); + + it("Deconstruct with mixed type and var at top level (int _, var _)", async () => { + const input = Input.FromText(`(int _, var _) = (1, 2);`); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Punctuation.OpenParen, + Token.PrimitiveType.Int, + Token.Identifier.TupleElementName("_"), + Token.Punctuation.Comma, + Token.Keyword.Definition.Var, + Token.Identifier.TupleElementName("_"), + Token.Punctuation.CloseParen, + Token.Operator.Assignment, + Token.Punctuation.OpenParen, + Token.Literal.Numeric.Decimal("1"), + Token.Punctuation.Comma, + Token.Literal.Numeric.Decimal("2"), + Token.Punctuation.CloseParen, + Token.Punctuation.Semicolon + ]); + }); + + it("Deconstruct with all vars and discards at top level (var _, var _, var _)", async () => { + const input = Input.FromText(`(var _, var _, var _) = ('a', 'b', 'c');`); + const tokens = await tokenize(input); + + tokens.should.deep.equal([ + Token.Punctuation.OpenParen, + Token.Keyword.Definition.Var, + Token.Identifier.TupleElementName("_"), + Token.Punctuation.Comma, + Token.Keyword.Definition.Var, + Token.Identifier.TupleElementName("_"), + Token.Punctuation.Comma, + Token.Keyword.Definition.Var, + Token.Identifier.TupleElementName("_"), + Token.Punctuation.CloseParen, + Token.Operator.Assignment, + Token.Punctuation.OpenParen, + Token.Punctuation.Char.Begin, + Token.Literal.Char("a"), + Token.Punctuation.Char.End, + Token.Punctuation.Comma, + Token.Punctuation.Char.Begin, + Token.Literal.Char("b"), + Token.Punctuation.Char.End, + Token.Punctuation.Comma, + Token.Punctuation.Char.Begin, + Token.Literal.Char("c"), + Token.Punctuation.Char.End, + Token.Punctuation.CloseParen, + Token.Punctuation.Semicolon + ]); + }); }); });