Fix parsing of delegate declarations with tuple return types at top-level#80889
Fix parsing of delegate declarations with tuple return types at top-level#80889CyrusNajmabadi merged 16 commits intomainfrom
Conversation
…evel Co-authored-by: CyrusNajmabadi <4564579+CyrusNajmabadi@users.noreply.github.com>
Co-authored-by: CyrusNajmabadi <4564579+CyrusNajmabadi@users.noreply.github.com>
|
|
||
| // Scan to the matching ')' | ||
| int depth = 1; | ||
| while (depth > 0 && this.CurrentToken.Kind != SyntaxKind.EndOfFileToken) |
There was a problem hiding this comment.
@copilot i don't like this part. it's not like how we generally speculatively parse ahead. Can you instead literally just try to use ParseTupleType(), and then see if what follows is an identifier or not?
#Closed
There was a problem hiding this comment.
Updated to use ScanTupleType() instead of manual token scanning. This is more consistent with Roslyn's speculative parsing patterns. See commit 5afa2b3.
| EOF(); | ||
| } | ||
|
|
||
| [Fact] |
There was a problem hiding this comment.
@copilot add WorkItem("https://github.com/dotnet/roslyn/issues/67311") attributes to any tests you add. #Closed
There was a problem hiding this comment.
Added [WorkItem("https://github.com/dotnet/roslyn/issues/67311")] attribute to both tests. See commit 847999a.
| delegate int G((int, int) x); | ||
| delegate System.ValueTuple<int, int> H(); | ||
| delegate (int, int) I(); | ||
| """; |
There was a problem hiding this comment.
@copilot add test where it is an anonymous delegate at the top level. Also, remove the System.Console.WriteLine from the test. #Closed
There was a problem hiding this comment.
Added test AnonymousDelegateAtTopLevel and removed System.Console.WriteLine from the original test. See commit 847999a.
Co-authored-by: CyrusNajmabadi <4564579+CyrusNajmabadi@users.noreply.github.com>
| return false; | ||
| } | ||
|
|
||
| // Check if what follows is an open brace (anonymous delegate body) |
There was a problem hiding this comment.
@copilot i think at this point we should assume we're a delegate-expression, since we didn't have a valid tuple type.
We also need a test for something like delegate (ref int i) { }. With your current logic, this would break (since we wouldn't have a tuple type, but we also wouldn't see the { since we'd stop parsing early. #Closed
There was a problem hiding this comment.
Fixed the logic to return true (anonymous delegate) when ScanTupleType() doesn't succeed, since delegate declarations must have a valid return type. Added test AnonymousDelegateWithRefParameter_TopLevel for the delegate (ref int i) { } case. See commit 96b398c.
…iteLine Co-authored-by: CyrusNajmabadi <4564579+CyrusNajmabadi@users.noreply.github.com>
Co-authored-by: CyrusNajmabadi <4564579+CyrusNajmabadi@users.noreply.github.com>
src/Compilers/CSharp/Test/Syntax/Parsing/TopLevelStatementsParsingTests.cs
Outdated
Show resolved
Hide resolved
src/Compilers/CSharp/Test/Syntax/Parsing/TopLevelStatementsParsingTests.cs
Outdated
Show resolved
Hide resolved
src/Compilers/CSharp/Test/Syntax/Parsing/TopLevelStatementsParsingTests.cs
Outdated
Show resolved
Hide resolved
| // If we didn't scan a valid tuple type, assume it's an anonymous delegate expression | ||
| // since delegate declarations must have a valid return type. | ||
| // This handles cases like: delegate (ref int i) { } | ||
| if (scanResult != ScanTypeFlags.TupleType) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| // We have a tuple type but no identifier following - check for open brace | ||
| if (this.CurrentToken.Kind == SyntaxKind.OpenBraceToken) | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| // Default to delegate type declaration for other cases | ||
| return false; |
There was a problem hiding this comment.
@copilot why not just simplify from 8700-8715 with just return true; ? I'm saying: once you determine it's not a tuple-followed-by-identifier, just assume it's an anonymous-delegate at that point. #Closed
There was a problem hiding this comment.
Simplified to just return true; after the tuple+identifier check. See commit a1fa782.
Co-authored-by: CyrusNajmabadi <4564579+CyrusNajmabadi@users.noreply.github.com>
| delegate (int x) { }; | ||
| """; | ||
|
|
||
| UsingTree(test); |
There was a problem hiding this comment.
nit: consider verify diagnostics.
And for a scenario that now successfully parsed, consider also verifying diagnostics
jcouv
left a comment
There was a problem hiding this comment.
LGTM Thanks (commit 11) with some nits to consider
| } | ||
|
|
||
| [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67311")] | ||
| public void DelegateWithTupleReturnType_TopLevel() |
| delegate int F(int x); | ||
| delegate int G((int, int) x); | ||
| delegate System.ValueTuple<int, int> H(); | ||
| delegate (int, int) I(); |
| public void AnonymousDelegateAtTopLevel() | ||
| { | ||
| var test = """ | ||
| var f1 = delegate { return 42; }; |
| } | ||
|
|
||
| [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67311")] | ||
| public void DelegateWithTupleReturnType_TopLevel() |
There was a problem hiding this comment.
Please test scenarios when these declarations are nested into a namespace, nested into a type.
|
Done with review pass (commit 11) |
| var test = """ | ||
| class C | ||
| { | ||
| delegate (int, int) I(); |
There was a problem hiding this comment.
Please add some versions with tuple names.
There was a problem hiding this comment.
It would also be good to have more complicated nested types. Things that ScanType doesn't return with definitely a type, like simple names or qualified names.
956c6a1 to
c96f7fa
Compare
…el-delegate-errors
delegate (ref int i) { })Summary
Fixed a parser bug where
delegate (int, int) I();at the top level was incorrectly parsed as an anonymous delegate expression instead of a delegate type declaration with a tuple return type.Solution: Implemented
IsAnonymousDelegateExpression()that usesScanTupleType()to properly distinguish:delegate (is followed by a valid tuple type and an identifier → delegate declaration (returnfalse)true)Testing:
DelegateWithTupleReturnType_TopLevelvalidates the fix with WorkItem attributeAnonymousDelegateAtTopLevelvalidates anonymous delegates with variable assignmentAnonymousDelegateWithRefParameter_TopLevelvalidates ref parameter caseAnonymousDelegateStandaloneExpression_TopLevelvalidates standalone anonymous delegate with parametersAnonymousDelegateNoParameters_TopLevelvalidates anonymous delegate without parametersFixes #67311
Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.