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
+ ]);
+ });
});
});