diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 6550d229bb5c4..1de27e706b6de 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -11967,6 +11967,21 @@ ExpressionSyntax parsePrimaryExpressionWithoutPostfix(Precedence precedence) { expr = this.AddError(expr, ErrorCode.ERR_ExpressionExpected); } + else if ( + SyntaxFacts.IsBinaryExpression(tk) || + SyntaxFacts.IsAssignmentExpressionOperatorToken(tk)) + { + // We got into the expression parsing path because we saw an error operator (see the + // default case in IsPossibleExpression), knowing we'd create a missing expr which would + // then allow the binary/assignment expr parsing to proceed. In this case, we want to + // report the invalid expr, but place it next to the operator, not whatever might have + // come arbitrarily far before us. + return WithAdditionalDiagnostics(expr, MakeError( + offset: this.CurrentToken.GetLeadingTriviaWidth(), + width: this.CurrentToken.Width, + ErrorCode.ERR_InvalidExprTerm, + SyntaxFacts.GetText(tk))); + } else { expr = this.AddError(expr, ErrorCode.ERR_InvalidExprTerm, SyntaxFacts.GetText(tk)); diff --git a/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests3.cs b/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests3.cs index 48f0d4a5838e0..0771f9922d3c0 100644 --- a/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests3.cs +++ b/src/Compilers/CSharp/Test/Emit3/Semantics/PatternMatchingTests3.cs @@ -2738,19 +2738,18 @@ class C }"; var compilation = CreateCompilation(source, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp9)); compilation.VerifyDiagnostics( - // (5,6): error CS1525: Invalid expression term '|' - // { - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("|").WithLocation(5, 6), - // (6,18): error CS1525: Invalid expression term '||' + // (6,9): error CS1525: Invalid expression term '|' // | 3 => 3, - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("||").WithLocation(6, 18), + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "|").WithArguments("|").WithLocation(6, 9), + // (7,9): error CS1525: Invalid expression term '||' + // || 4 => 4, + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "||").WithArguments("||").WithLocation(7, 9), // (8,11): error CS0211: Cannot take the address of the given expression // & 5 => 5, Diagnostic(ErrorCode.ERR_InvalidAddrOp, "5").WithLocation(8, 11), - // (8,18): error CS1525: Invalid expression term '&&' - // & 5 => 5, - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("&&").WithLocation(8, 18) - ); + // (9,9): error CS1525: Invalid expression term '&&' + // && 6 => 6, + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "&&").WithArguments("&&").WithLocation(9, 9)); } [Fact] diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICoalesceAssignmentOperation.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICoalesceAssignmentOperation.cs index 90e70ad9449b3..a144c67e01fa9 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICoalesceAssignmentOperation.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_ICoalesceAssignmentOperation.cs @@ -239,10 +239,10 @@ static void M() Children(0) "; var expectedDiagnostics = new DiagnosticDescription[] { - // file.cs(5,6): error CS1525: Invalid expression term '??=' - // { - Diagnostic(ErrorCode.ERR_InvalidExprTerm, "").WithArguments("??=").WithLocation(5, 6), - // file.cs(6,33): error CS1525: Invalid expression term ';' + // (6,19): error CS1525: Invalid expression term '??=' + // /**/??=/**/; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "??=").WithArguments("??=").WithLocation(6, 19), + // (6,33): error CS1525: Invalid expression term ';' // /**/??=/**/; Diagnostic(ErrorCode.ERR_InvalidExprTerm, ";").WithArguments(";").WithLocation(6, 33) }; diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs index be8bac428f07b..019487e219537 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/ExpressionParsingTests.cs @@ -8674,5 +8674,213 @@ public void TestParenthesizedDefaultInIsExpression() } EOF(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/58298")] + public void OperatorErrorLocation1() + { + UsingTree(""" + void M() + { + <<; + } + """, + // (3,5): error CS1525: Invalid expression term '<<' + // <<; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "<<").WithArguments("<<").WithLocation(3, 5), + // (3,7): error CS1525: Invalid expression term ';' + // <<; + Diagnostic(ErrorCode.ERR_InvalidExprTerm, ";").WithArguments(";").WithLocation(3, 7)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalFunctionStatement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.LeftShiftExpression); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.LessThanLessThanToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31774")] + public void OperatorErrorLocation2() + { + UsingTree(""" + void M() + { + // comment + / Console.ReadKey(); + } + """, + // (4,5): error CS1525: Invalid expression term '/' + // / Console.ReadKey(); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "/").WithArguments("/").WithLocation(4, 5)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalFunctionStatement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.DivideExpression); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.SlashToken); + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Console"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "ReadKey"); + } + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/31774")] + public void OperatorErrorLocation3() + { + UsingTree(""" + void M() + { + // comment + = Console.ReadKey(); + } + """, + // (4,5): error CS1525: Invalid expression term '=' + // = Console.ReadKey(); + Diagnostic(ErrorCode.ERR_InvalidExprTerm, "=").WithArguments("=").WithLocation(4, 5)); + + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.GlobalStatement); + { + N(SyntaxKind.LocalFunctionStatement); + { + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleAssignmentExpression); + { + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + N(SyntaxKind.EqualsToken); + N(SyntaxKind.InvocationExpression); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Console"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "ReadKey"); + } + } + N(SyntaxKind.ArgumentList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } } } diff --git a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs index d6212e9bb63a4..65dd675cd3f83 100644 --- a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs @@ -1933,17 +1933,17 @@ void M() var result = await service.AnalyzeChangeAsync( document, [new TextChange(new TextSpan(listIndex, 1), """ - <<<<<<< + <<<<<<< goo Goo - ======= + ======= bar Bar - >>>>>>> + >>>>>>> baz """)], CancellationToken.None); Assert.True(result.Succeeded); var diagnosticAnalysis = result.DiagnosticAnalyses.Single(d => d.Kind == DiagnosticKind.CompilerSyntax); - Assert.Equal(1, diagnosticAnalysis.IdToCount["CS8300"]); + Assert.Equal(3, diagnosticAnalysis.IdToCount["CS8300"]); Assert.Equal(1, result.CodeFixAnalysis.DiagnosticIdToCount["CS8300"]); Assert.Equal("CSharp.ConflictMarkerResolution.CSharpResolveConflictMarkerCodeFixProvider", result.CodeFixAnalysis.DiagnosticIdToProviderName["CS8300"].Single()); @@ -1967,8 +1967,8 @@ void M() Assert.Equal("TestProposalId", properties["vs.ide.vbcs.copilot.analyzechange.proposalid"]); Assert.Equal(44, properties["vs.ide.vbcs.copilot.analyzechange.olddocumentlength"]); - Assert.Equal(78, properties["vs.ide.vbcs.copilot.analyzechange.newdocumentlength"]); - Assert.Equal(34, properties["vs.ide.vbcs.copilot.analyzechange.textchangedelta"]); + Assert.Equal(90, properties["vs.ide.vbcs.copilot.analyzechange.newdocumentlength"]); + Assert.Equal(46, properties["vs.ide.vbcs.copilot.analyzechange.textchangedelta"]); Assert.Equal(1, properties["vs.ide.vbcs.copilot.analyzechange.projectdocumentcount"]); Assert.Equal(0, properties["vs.ide.vbcs.copilot.analyzechange.projectsourcegenerateddocumentcount"]); @@ -1985,16 +1985,16 @@ void M() Assert.Equal("", properties["vs.ide.vbcs.copilot.analyzechange.diagnosticanalysis_analyzersemantic_severitytocount"]); // CS1002_1 means we got one CS1002 diagnostic. Whereas CS1525_3 means we got 3 CS1525 diagnostics. - Assert.Equal("CS1002_1,CS1513_1,CS1525_3,CS8300_1", properties["vs.ide.vbcs.copilot.analyzechange.diagnosticanalysis_compilersyntax_idtocount"]); - Assert.Equal("Compiler_6", properties["vs.ide.vbcs.copilot.analyzechange.diagnosticanalysis_compilersyntax_categorytocount"]); - Assert.Equal("Error_6", properties["vs.ide.vbcs.copilot.analyzechange.diagnosticanalysis_compilersyntax_severitytocount"]); + Assert.Equal("CS1002_1,CS8300_3", properties["vs.ide.vbcs.copilot.analyzechange.diagnosticanalysis_compilersyntax_idtocount"]); + Assert.Equal("Compiler_4", properties["vs.ide.vbcs.copilot.analyzechange.diagnosticanalysis_compilersyntax_categorytocount"]); + Assert.Equal("Error_4", properties["vs.ide.vbcs.copilot.analyzechange.diagnosticanalysis_compilersyntax_severitytocount"]); Assert.Equal("CS0103_1", properties["vs.ide.vbcs.copilot.analyzechange.diagnosticanalysis_compilersemantic_idtocount"]); Assert.Equal("Compiler_1", properties["vs.ide.vbcs.copilot.analyzechange.diagnosticanalysis_compilersemantic_categorytocount"]); Assert.Equal("Error_1", properties["vs.ide.vbcs.copilot.analyzechange.diagnosticanalysis_compilersemantic_severitytocount"]); - Assert.Equal("CS0103_1,CS8300_1", properties["vs.ide.vbcs.copilot.analyzechange.codefixanalysis_diagnosticidtocount"]); - Assert.Equal("CS0103_CSharp.GenerateVariable.CSharpGenerateVariableCodeFixProvider,CS8300_CSharp.ConflictMarkerResolution.CSharpResolveConflictMarkerCodeFixProvider", + Assert.Equal("CS0103_2,CS8300_1", properties["vs.ide.vbcs.copilot.analyzechange.codefixanalysis_diagnosticidtocount"]); + Assert.Equal("CS0103_CSharp.GenerateVariable.CSharpGenerateVariableCodeFixProvider:CSharp.SpellCheck.CSharpSpellCheckCodeFixProvider,CS8300_CSharp.ConflictMarkerResolution.CSharpResolveConflictMarkerCodeFixProvider", properties["vs.ide.vbcs.copilot.analyzechange.codefixanalysis_diagnosticidtoprovidername"]); }