Improve error recovery around 'scoped' modifier parsing#81636
Improve error recovery around 'scoped' modifier parsing#81636CyrusNajmabadi merged 32 commits intodotnet:mainfrom
Conversation
| return true; | ||
|
|
||
| using var beforeScopedResetPoint = this.GetDisposableResetPoint(resetOnDispose: false); | ||
| using var beforeScopedResetPoint = this.GetDisposableResetPoint(resetOnDispose: true); |
There was a problem hiding this comment.
no need to for dual reset points. and we can always trivially clean up. we just let the caller then eat the token if this function says it is a keyword.
| Diagnostic(ErrorCode.ERR_IdentifierExpected, "readonly").WithLocation(3, 37), | ||
| // (3,37): error CS1003: Syntax error, ',' expected | ||
| // public static void M(ref scoped readonly int p) => throw null; | ||
| Diagnostic(ErrorCode.ERR_SyntaxError, "readonly").WithArguments(",").WithLocation(3, 37), |
There was a problem hiding this comment.
lots of the PR will just be syntax errors going away, and better errors about misplaced 'scopes' modifiers.
| Diagnostic(ErrorCode.ERR_RefReadOnlyWrongOrdering, "readonly").WithLocation(3, 26), | ||
| // (3,35): error CS0246: The type or namespace name 'scoped' could not be found (are you missing a using directive or an assembly reference?) | ||
| // public static void M(readonly scoped ref int p) => throw null; | ||
| Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "scoped").WithArguments("scoped").WithLocation(3, 35), |
There was a problem hiding this comment.
or removing errors where we considered it a type name, but now realie it should be treated as a modifier.
| Diagnostic(ErrorCode.ERR_ScopedAfterInOutRefReadonly, "scoped").WithLocation(1, 29), | ||
| // (1,47): error CS8936: Feature 'ref fields' is not available in C# 10.0. Please use language version 11.0 or greater. | ||
| // void F(ref scoped int b, in scoped int c, out scoped int d) { } | ||
| Diagnostic(ErrorCode.ERR_ScopedAfterInOutRefReadonly, "scoped").WithLocation(1, 47)]); |
There was a problem hiding this comment.
the difference between these two is just the errors about a feature not being available. is tehre a helper to filter those out appropriately to make the test cleaner?
| // (1,54): error CS1003: Syntax error, ',' expected | ||
| // void F(ref scoped int b, in scoped int c, out scoped int d) { } | ||
| Diagnostic(ErrorCode.ERR_SyntaxError, "int").WithArguments(",").WithLocation(1, 54) | ||
| ); |
There was a problem hiding this comment.
in some tests all syntax diagnostics went away. as such, i added semantic diagnostics to show we still report problems with the scoped modifier.
| M(SyntaxKind.CommaToken); | ||
| N(SyntaxKind.Parameter); | ||
| { | ||
| N(SyntaxKind.ScopedKeyword); |
There was a problem hiding this comment.
these tests demonstrate better understanding, with 'scoped' properly being treated as a modifier.
| Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(1, 26)]); | ||
|
|
||
| N(SyntaxKind.ParenthesizedExpression); | ||
| N(SyntaxKind.ParenthesizedLambdaExpression); |
There was a problem hiding this comment.
we didn't even realize this was a lambda before...
|
@dotnet/roslyn-compiler this is ready for review. |
|
|
||
| private bool IsPossibleScopedKeyword(bool isFunctionPointerParameter) | ||
| { | ||
| using var _ = this.GetDisposableResetPoint(resetOnDispose: true); |
There was a problem hiding this comment.
for those keeping count, this made for a triple nested reset point. We now just have one reset point.
| // trivial case. scoped ref/out/in/this is definitely the scoped keyword. Note: the only actual legal | ||
| // cases are `scoped ref`, `scoped out`, and `scoped in`. But we detect and allow `scoped this`, `scoped | ||
| // params` and `scoped readonly` as well. These will be reported as errors later in binding. | ||
| if (IsParameterModifierExcludingScoped(this.CurrentToken)) |
There was a problem hiding this comment.
we intentionally allow a broader set than before. so you can say things like scoped this. This is caught later in the binder.
| return false; | ||
|
|
||
| // In C# 14 we decided that within a lambda 'scoped' would *always* be a keyword. | ||
| // In C# 14 we decided that within a lambda 'scoped' would *always* be a modifier, not a type. |
There was a problem hiding this comment.
refining this comment. It wasn't 100% accurate. It's not always a keyword. But it is a modifier if it previous would have been allowed as a type.
src/Compilers/CSharp/Test/Syntax/Parsing/LambdaParameterParsingTests.cs
Outdated
Show resolved
Hide resolved
src/Compilers/CSharp/Test/Syntax/Parsing/LambdaParameterParsingTests.cs
Outdated
Show resolved
Hide resolved
|
@jcouv ptal :-) |
| } | ||
| else if (seenIn || seenOut || seenRef || seenReadonly) | ||
| { | ||
| // Matches original parsing logic that disallowed parsing out 'scoped' once in/out/ref/readonly had been seen. |
There was a problem hiding this comment.
Updated both comments.
| else if (i < n - 1) | ||
| { | ||
| // Matches original parsing logic that only allowed 'scoped' to be followed by ref/out/in to | ||
| // actually be considered the modifier. |
There was a problem hiding this comment.
I didn't understand this comment. The logic below merely reports a diagnostic, how does it make it not considered a modifier? #Closed
There was a problem hiding this comment.
instead of treating it as a modifier (no error), it errors. meaning we don't accept this as a modifier in this order.
There was a problem hiding this comment.
updated comment to now say: Only allow 'scoped' followed by ref/out/in to actually be considered a valid modifier.
| // The .NET Foundation licenses this file to you under the MIT license. | ||
| // See the LICENSE file in the project root for more information. | ||
|
|
||
| using System.Linq; |
There was a problem hiding this comment.
nit: We have another open issue related to scoped ordering: #78556
Could you include a test for scoped in this int and this scoped in int to show if the PR affects those scenarios?
* upstream/main: (1373 commits) Add docs Use new test plan for dictionary expressions (dotnet#81861) Update dartlab pipeline setup (dotnet#81807) [main] Update dependencies from dotnet/roslyn (dotnet#81649) Fix LSP, everything but handlers Reapply "Update methods to be `async`." (dotnet#81808) Properly await calls to ensure exception handling works for sync and async scenarios Let Razor fill in project information on diagnostics (dotnet#81822) only analyzers Reapply "Update methods to be `async`." (dotnet#81808) only editorfeatures Reapply "Update methods to be `async`." (dotnet#81808) Enable GetTypeInfo for type of object creation syntax (dotnet#81802) Improve error recovery around 'scoped' modifier parsing (dotnet#81636) Only VisualStudio Only features Only analyzers Only editor features Reapply "Update methods to be `async`." (dotnet#81808) ...
We now allow a misplacced 'scoped' keyword to be treated as a normal modifier, and move teh reporting of issues to the binding phase. Today we get entirely thrown off and enter very bad error recovery that often gets thrown entirely off, creating tons of cascading errors.
Note: the net effect of this is much fewer errors, and much better error recovery. The added length in this PR is the addition of new tests, or updating existing tests to report binding errors to demonstrate that errors are still produced semantically where they would have previously been syntactic.