Skip to content

Commit feb1350

Browse files
Recover better when a user uses commas in a for-statement instead of semicolons (#75632)
2 parents 29e09e0 + 857ad2c commit feb1350

File tree

11 files changed

+900
-288
lines changed

11 files changed

+900
-288
lines changed

src/Compilers/CSharp/Portable/Parser/LanguageParser.cs

Lines changed: 119 additions & 87 deletions
Large diffs are not rendered by default.

src/Compilers/CSharp/Portable/Parser/SyntaxParser.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -890,13 +890,20 @@ protected static SyntaxDiagnosticInfo MakeError(ErrorCode code, params object[]
890890
return new SyntaxDiagnosticInfo(code, args);
891891
}
892892

893-
protected TNode AddLeadingSkippedSyntax<TNode>(TNode node, GreenNode skippedSyntax) where TNode : CSharpSyntaxNode
893+
#nullable enable
894+
895+
protected TNode AddLeadingSkippedSyntax<TNode>(TNode node, GreenNode? skippedSyntax) where TNode : CSharpSyntaxNode
894896
{
897+
if (skippedSyntax is null)
898+
return node;
899+
895900
var oldToken = node as SyntaxToken ?? node.GetFirstToken();
896901
var newToken = AddSkippedSyntax(oldToken, skippedSyntax, trailing: false);
897902
return SyntaxFirstTokenReplacer.Replace(node, oldToken, newToken, skippedSyntax.FullWidth);
898903
}
899904

905+
#nullable disable
906+
900907
protected void AddTrailingSkippedSyntax(SyntaxListBuilder list, GreenNode skippedSyntax)
901908
{
902909
list[list.Count - 1] = AddTrailingSkippedSyntax((CSharpSyntaxNode)list[list.Count - 1], skippedSyntax);

src/Compilers/CSharp/Test/Emit3/Semantics/OutVarTests.cs

Lines changed: 109 additions & 61 deletions
Large diffs are not rendered by default.

src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTestBase.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,9 @@ protected static void AssertContainedInDeclaratorArguments(SingleVariableDesigna
350350
Assert.True(decl.Ancestors().OfType<VariableDeclaratorSyntax>().First().ArgumentList.Contains(decl));
351351
}
352352

353+
protected static void AssertNotContainedInDeclaratorArguments(SingleVariableDesignationSyntax decl)
354+
=> Assert.Empty(decl.Ancestors().OfType<VariableDeclaratorSyntax>());
355+
353356
protected static void AssertContainedInDeclaratorArguments(params SingleVariableDesignationSyntax[] decls)
354357
{
355358
foreach (var decl in decls)
@@ -358,6 +361,12 @@ protected static void AssertContainedInDeclaratorArguments(params SingleVariable
358361
}
359362
}
360363

364+
protected static void AssertNotContainedInDeclaratorArguments(params SingleVariableDesignationSyntax[] decls)
365+
{
366+
foreach (var decl in decls)
367+
AssertNotContainedInDeclaratorArguments(decl);
368+
}
369+
361370
protected static void VerifyModelNotSupported(
362371
SemanticModel model,
363372
SingleVariableDesignationSyntax designation,

src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests_Scope.cs

Lines changed: 109 additions & 60 deletions
Large diffs are not rendered by default.

src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IForLoopStatement.cs

Lines changed: 59 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2678,7 +2678,7 @@ static void Main(string[] args)
26782678

26792679
[CompilerTrait(CompilerFeature.IOperation)]
26802680
[Fact, WorkItem(17602, "https://github.com/dotnet/roslyn/issues/17602")]
2681-
public void IForLoopStatement_InvalidExpression()
2681+
public void IForLoopStatement_InvalidExpression1()
26822682
{
26832683
string source = @"
26842684
class C
@@ -2691,45 +2691,65 @@ static void Main(string[] args)
26912691
}
26922692
}
26932693
";
2694-
string expectedOperationTree = @"
2695-
IForLoopOperation (LoopKind.For, Continue Label Id: 0, Exit Label Id: 1) (OperationKind.Loop, Type: null, IsInvalid) (Syntax: 'for (int k ... 100, j > 5;')
2696-
Locals: Local_1: System.Int32 k
2697-
Local_2: System.Int32 j
2698-
Condition:
2699-
IBinaryOperation (BinaryOperatorKind.LessThan) (OperationKind.Binary, Type: System.Boolean, IsInvalid) (Syntax: 'k < 100')
2700-
Left:
2701-
ILocalReferenceOperation: k (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'k')
2702-
Right:
2703-
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 100, IsInvalid) (Syntax: '100')
2704-
Before:
2705-
IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsImplicit) (Syntax: 'int k = 0, j = 0')
2706-
IVariableDeclarationOperation (2 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'int k = 0, j = 0')
2707-
Declarators:
2708-
IVariableDeclaratorOperation (Symbol: System.Int32 k) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'k = 0')
2709-
Initializer:
2710-
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= 0')
2711-
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0')
2712-
IVariableDeclaratorOperation (Symbol: System.Int32 j) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'j = 0')
2713-
Initializer:
2714-
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= 0')
2715-
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0')
2716-
Initializer:
2717-
null
2718-
AtLoopBottom:
2719-
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid, IsImplicit) (Syntax: '')
2720-
Expression:
2721-
IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid) (Syntax: '')
2722-
Children(0)
2723-
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid, IsImplicit) (Syntax: 'j > 5')
2724-
Expression:
2725-
IBinaryOperation (BinaryOperatorKind.GreaterThan) (OperationKind.Binary, Type: System.Boolean, IsInvalid) (Syntax: 'j > 5')
2726-
Left:
2727-
ILocalReferenceOperation: j (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'j')
2728-
Right:
2729-
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 5, IsInvalid) (Syntax: '5')
2730-
Body:
2731-
IEmptyOperation (OperationKind.Empty, Type: null, IsInvalid) (Syntax: ';')
2694+
var tree = GetOperationTreeForTest<ForStatementSyntax>(source);
2695+
Assert.Null(tree);
2696+
}
2697+
2698+
[CompilerTrait(CompilerFeature.IOperation)]
2699+
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17602")]
2700+
public void IForLoopStatement_InvalidExpression2()
2701+
{
2702+
string source = @"
2703+
class C
2704+
{
2705+
static void Main(string[] args)
2706+
{
2707+
/*<bind>*/for (int k = 0, j = 0; k < 100, j > 5; k++)
2708+
{
2709+
}/*</bind>*/
2710+
}
2711+
}
27322712
";
2713+
string expectedOperationTree = """
2714+
IForLoopOperation (LoopKind.For, Continue Label Id: 0, Exit Label Id: 1) (OperationKind.Loop, Type: null, IsInvalid) (Syntax: 'for (int k ... }')
2715+
Locals: Local_1: System.Int32 k
2716+
Local_2: System.Int32 j
2717+
Condition:
2718+
IBinaryOperation (BinaryOperatorKind.LessThan) (OperationKind.Binary, Type: System.Boolean, IsInvalid) (Syntax: 'k < 100')
2719+
Left:
2720+
ILocalReferenceOperation: k (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'k')
2721+
Right:
2722+
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 100, IsInvalid) (Syntax: '100')
2723+
Before:
2724+
IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsImplicit) (Syntax: 'int k = 0, j = 0')
2725+
IVariableDeclarationOperation (2 declarators) (OperationKind.VariableDeclaration, Type: null) (Syntax: 'int k = 0, j = 0')
2726+
Declarators:
2727+
IVariableDeclaratorOperation (Symbol: System.Int32 k) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'k = 0')
2728+
Initializer:
2729+
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= 0')
2730+
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0')
2731+
IVariableDeclaratorOperation (Symbol: System.Int32 j) (OperationKind.VariableDeclarator, Type: null) (Syntax: 'j = 0')
2732+
Initializer:
2733+
IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null) (Syntax: '= 0')
2734+
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0')
2735+
Initializer:
2736+
null
2737+
AtLoopBottom:
2738+
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsInvalid, IsImplicit) (Syntax: 'j > 5')
2739+
Expression:
2740+
IBinaryOperation (BinaryOperatorKind.GreaterThan) (OperationKind.Binary, Type: System.Boolean, IsInvalid) (Syntax: 'j > 5')
2741+
Left:
2742+
ILocalReferenceOperation: j (OperationKind.LocalReference, Type: System.Int32, IsInvalid) (Syntax: 'j')
2743+
Right:
2744+
ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 5, IsInvalid) (Syntax: '5')
2745+
IExpressionStatementOperation (OperationKind.ExpressionStatement, Type: null, IsImplicit) (Syntax: 'k++')
2746+
Expression:
2747+
IIncrementOrDecrementOperation (Postfix) (OperationKind.Increment, Type: System.Int32) (Syntax: 'k++')
2748+
Target:
2749+
ILocalReferenceOperation: k (OperationKind.LocalReference, Type: System.Int32) (Syntax: 'k')
2750+
Body:
2751+
IBlockOperation (0 statements) (OperationKind.Block, Type: null) (Syntax: '{ ... }')
2752+
""";
27332753
VerifyOperationTreeForTest<ForStatementSyntax>(source, expectedOperationTree);
27342754
}
27352755

src/Compilers/CSharp/Test/Semantic/Semantics/ForLoopErrorTests.cs

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

5-
#nullable disable
6-
7-
using Microsoft.CodeAnalysis.Test.Utilities;
85
using Xunit;
96

107
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics
@@ -27,16 +24,16 @@ static void Main(string[] args)
2724
}
2825
}
2926
";
30-
CreateCompilation(text).
31-
VerifyDiagnostics(
32-
Diagnostic(ErrorCode.ERR_SemicolonExpected, ","),
33-
Diagnostic(ErrorCode.ERR_InvalidExprTerm, ",").WithArguments(","),
34-
Diagnostic(ErrorCode.ERR_CloseParenExpected, ";"),
35-
Diagnostic(ErrorCode.ERR_SemicolonExpected, ")"),
36-
Diagnostic(ErrorCode.ERR_RbraceExpected, ")"),
37-
Diagnostic(ErrorCode.ERR_IllegalStatement, "j > 5"),
38-
Diagnostic(ErrorCode.ERR_NameNotInContext, "k").WithArguments("k")
39-
);
27+
CreateCompilation(text).VerifyDiagnostics(
28+
// (6,39): error CS1002: ; expected
29+
// for (int k = 0, j = 0; k < 100, j > 5; k++)
30+
Diagnostic(ErrorCode.ERR_SemicolonExpected, ",").WithLocation(6, 39),
31+
// (6,46): error CS1003: Syntax error, ',' expected
32+
// for (int k = 0, j = 0; k < 100, j > 5; k++)
33+
Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(",").WithLocation(6, 46),
34+
// (6,41): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement
35+
// for (int k = 0, j = 0; k < 100, j > 5; k++)
36+
Diagnostic(ErrorCode.ERR_IllegalStatement, "j > 5").WithLocation(6, 41));
4037
}
4138

4239
// Condition expression must be bool type
@@ -94,11 +91,16 @@ static void Main(string[] args)
9491
}
9592
}
9693
";
97-
CreateCompilation(text).
98-
VerifyDiagnostics(
99-
Diagnostic(ErrorCode.ERR_CloseParenExpected, ";"),
100-
Diagnostic(ErrorCode.ERR_RbraceExpected, ")")
101-
);
94+
CreateCompilation(text).VerifyDiagnostics(
95+
// (6,34): error CS1525: Invalid expression term ';'
96+
// for (int i = 10; i < 100;;);
97+
Diagnostic(ErrorCode.ERR_InvalidExprTerm, ";").WithArguments(";").WithLocation(6, 34),
98+
// (6,34): error CS1003: Syntax error, ',' expected
99+
// for (int i = 10; i < 100;;);
100+
Diagnostic(ErrorCode.ERR_SyntaxError, ";").WithArguments(",").WithLocation(6, 34),
101+
// (6,35): error CS1525: Invalid expression term ')'
102+
// for (int i = 10; i < 100;;);
103+
Diagnostic(ErrorCode.ERR_InvalidExprTerm, ")").WithArguments(")").WithLocation(6, 35));
102104

103105
text =
104106
@"

0 commit comments

Comments
 (0)