From 674f4e1cade6d9f2ca46346564d543ebf5e1bd72 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Wed, 6 Sep 2017 11:06:39 -0700 Subject: [PATCH] Complete goto default; without extra colon (#21861) --- .../Symbols/Source/SourceLabelSymbol.cs | 5 + .../Semantic/Semantics/LookupPositionTests.cs | 28 +++++ .../Semantics/PatternMatchingTests.cs | 4 +- .../Test/Semantic/Semantics/SwitchTests.cs | 68 ++++++++--- .../SemanticModelGetDeclaredSymbolAPITests.cs | 2 +- .../Source/DeclaringSyntaxNodeTests.cs | 2 +- .../SymbolCompletionProviderTests.cs | 2 +- .../CSharpCompletionCommandHandlerTests.vb | 112 ++++++++++++++++++ .../CompletionUtilities.cs | 6 + 9 files changed, 206 insertions(+), 23 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLabelSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLabelSymbol.cs index a4cc96fcc92..1b541de93a1 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Source/SourceLabelSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Source/SourceLabelSymbol.cs @@ -44,6 +44,11 @@ private string MakeLabelName() var node = _identifierNodeOrToken.AsNode(); if (node != null) { + if (node.Kind() == SyntaxKind.DefaultSwitchLabel) + { + return ((DefaultSwitchLabelSyntax)node).Keyword.ToString(); + } + return node.ToString(); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/LookupPositionTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/LookupPositionTests.cs index 365d5954d73..a32fad545e5 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/LookupPositionTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/LookupPositionTests.cs @@ -1468,6 +1468,34 @@ static void Main(string[] args) Assert.True(symbols.IsEmpty); } + [Fact] + public void GotoLabelShouldNotHaveColon() + { + var source = @" +class Program +{ + static void M(object o) + { + switch (o) + { + case int i: + goto HERE; + default: +@default: +label1: + break; + } + } +} +"; + + var compilation = CreateStandardCompilation(source); + var tree = compilation.SyntaxTrees.Single(); + var model = compilation.GetSemanticModel(tree); + var symbols = model.LookupLabels(source.ToString().IndexOf("HERE", StringComparison.Ordinal)); + AssertEx.SetEqual(new[] { "default", "case int i:", "label1" }, symbols.Select(s => s.ToTestDisplayString())); + } + [WorkItem(586815, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/586815")] [WorkItem(598371, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/598371")] [Fact] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs index 9fd31d116cd..6203f65cb1d 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests.cs @@ -5608,9 +5608,9 @@ static void M1(object o) "; var compilation = CreateCompilationWithMscorlib45(source); compilation.VerifyDiagnostics( - // (9,13): error CS0152: The switch statement contains multiple cases with the label value 'default:' + // (9,13): error CS0152: The switch statement contains multiple cases with the label value 'default' // default: - Diagnostic(ErrorCode.ERR_DuplicateCaseLabel, "default:").WithArguments("default:").WithLocation(9, 13) + Diagnostic(ErrorCode.ERR_DuplicateCaseLabel, "default:").WithArguments("default").WithLocation(9, 13) ); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/SwitchTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/SwitchTests.cs index e002f2aefa6..38266e7bc6a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/SwitchTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/SwitchTests.cs @@ -377,19 +377,48 @@ public static void Main() } }"; CreateStandardCompilation(text, parseOptions: TestOptions.Regular6).VerifyDiagnostics( - // (15,13): error CS0152: The switch statement contains multiple cases with the label value 'default:' + // (15,13): error CS0152: The switch statement contains multiple cases with the label value 'default' // default: //CS0152 - Diagnostic(ErrorCode.ERR_DuplicateCaseLabel, "default:").WithArguments("default:").WithLocation(15, 13) + Diagnostic(ErrorCode.ERR_DuplicateCaseLabel, "default:").WithArguments("default").WithLocation(15, 13) ); CreateStandardCompilation(text, parseOptions: TestOptions.Regular6WithV7SwitchBinder).VerifyDiagnostics( - // (15,13): error CS0152: The switch statement contains multiple cases with the label value 'default:' + // (15,13): error CS0152: The switch statement contains multiple cases with the label value 'default' // default: //CS0152 - Diagnostic(ErrorCode.ERR_DuplicateCaseLabel, "default:").WithArguments("default:").WithLocation(15, 13) + Diagnostic(ErrorCode.ERR_DuplicateCaseLabel, "default:").WithArguments("default").WithLocation(15, 13) ); CreateStandardCompilation(text).VerifyDiagnostics( - // (15,13): error CS0152: The switch statement contains multiple cases with the label value 'default:' + // (15,13): error CS0152: The switch statement contains multiple cases with the label value 'default' // default: //CS0152 - Diagnostic(ErrorCode.ERR_DuplicateCaseLabel, "default:").WithArguments("default:").WithLocation(15, 13) + Diagnostic(ErrorCode.ERR_DuplicateCaseLabel, "default:").WithArguments("default").WithLocation(15, 13) + ); + } + + [Fact] + public void CS0152_DuplicateDefaultLabel2() + { + var text = @" +public class TestClass +{ + public static void Main() + { + int i = 10; + switch (i) + { + default: + break; + case (default): + break; + case 1: + break; + default: //CS0152 + break; + } + } +}"; + CreateStandardCompilation(text, parseOptions: TestOptions.Regular7_1).VerifyDiagnostics( + // (15,13): error CS0152: The switch statement contains multiple cases with the label value 'default' + // default: //CS0152 + Diagnostic(ErrorCode.ERR_DuplicateCaseLabel, "default:").WithArguments("default").WithLocation(15, 13) ); } @@ -2483,17 +2512,17 @@ public static int Main() CreateStandardCompilation(text, parseOptions: TestOptions.Regular6).VerifyDiagnostics( // (14,13): error CS8070: Control cannot fall out of switch from final case label ('default') // default: // CS8070 - Diagnostic(ErrorCode.ERR_SwitchFallOut, "default:").WithArguments("default:").WithLocation(14, 13) + Diagnostic(ErrorCode.ERR_SwitchFallOut, "default:").WithArguments("default").WithLocation(14, 13) ); CreateStandardCompilation(text, parseOptions: TestOptions.Regular6WithV7SwitchBinder).VerifyDiagnostics( - // (14,13): error CS8070: Control cannot fall out of switch from final case label ('default') + // (14,13): error CS8070: Control cannot fall out of switch from final case label ('default:') // default: // CS8070 Diagnostic(ErrorCode.ERR_SwitchFallOut, "default:").WithArguments("default:").WithLocation(14, 13) ); CreateStandardCompilation(text).VerifyDiagnostics( // (14,13): error CS8070: Control cannot fall out of switch from final case label ('default') // default: // CS8070 - Diagnostic(ErrorCode.ERR_SwitchFallOut, "default:").WithArguments("default:").WithLocation(14, 13) + Diagnostic(ErrorCode.ERR_SwitchFallOut, "default:").WithArguments("default").WithLocation(14, 13) ); } @@ -2589,9 +2618,10 @@ static int Main() // (24,17): error CS8070: Control cannot fall out of switch from final case label ('case 5:') // case 5: Diagnostic(ErrorCode.ERR_SwitchFallOut, "case 5:").WithArguments("case 5:").WithLocation(24, 17), - // (32,17): error CS8070: Control cannot fall out of switch from final case label ('default:') + // (32,17): error CS8070: Control cannot fall out of switch from final case label ('default') // default: - Diagnostic(ErrorCode.ERR_SwitchFallOut, "default:").WithArguments("default:").WithLocation(32, 17)); + Diagnostic(ErrorCode.ERR_SwitchFallOut, "default:").WithArguments("default").WithLocation(32, 17) + ); CreateStandardCompilation(text, parseOptions: TestOptions.Regular6WithV7SwitchBinder).VerifyDiagnostics( // (15,17): error CS8070: Control cannot fall out of switch from final case label ('case 11:') // case 11: @@ -2601,7 +2631,8 @@ static int Main() Diagnostic(ErrorCode.ERR_SwitchFallOut, "case 5:").WithArguments("case 5:").WithLocation(24, 17), // (32,17): error CS8070: Control cannot fall out of switch from final case label ('default:') // default: - Diagnostic(ErrorCode.ERR_SwitchFallOut, "default:").WithArguments("default:").WithLocation(32, 17)); + Diagnostic(ErrorCode.ERR_SwitchFallOut, "default:").WithArguments("default:").WithLocation(32, 17) // BUG + ); CreateStandardCompilation(text).VerifyDiagnostics( // (15,17): error CS8070: Control cannot fall out of switch from final case label ('case 11:') // case 11: @@ -2609,9 +2640,10 @@ static int Main() // (24,17): error CS8070: Control cannot fall out of switch from final case label ('case 5:') // case 5: Diagnostic(ErrorCode.ERR_SwitchFallOut, "case 5:").WithArguments("case 5:").WithLocation(24, 17), - // (32,17): error CS8070: Control cannot fall out of switch from final case label ('default:') + // (32,17): error CS8070: Control cannot fall out of switch from final case label ('default') // default: - Diagnostic(ErrorCode.ERR_SwitchFallOut, "default:").WithArguments("default:").WithLocation(32, 17)); + Diagnostic(ErrorCode.ERR_SwitchFallOut, "default:").WithArguments("default").WithLocation(32, 17) + ); } [Fact] @@ -2627,9 +2659,9 @@ public void SwitchFallOut_Script() Console.WriteLine(2); }"; CreateCompilationWithMscorlib45(source, references: new[] { SystemCoreRef }, parseOptions: TestOptions.Script).VerifyDiagnostics( - // (4,5): error CS0163: Control cannot fall through from one case label ('default:') to another + // (4,5): error CS0163: Control cannot fall through from one case label ('default') to another // default: - Diagnostic(ErrorCode.ERR_SwitchFallThrough, "default:").WithArguments("default:").WithLocation(4, 5), + Diagnostic(ErrorCode.ERR_SwitchFallThrough, "default:").WithArguments("default").WithLocation(4, 5), // (6,5): error CS8070: Control cannot fall out of switch from final case label ('case 2:') // case 2: Diagnostic(ErrorCode.ERR_SwitchFallOut, "case 2:").WithArguments("case 2:").WithLocation(6, 5) @@ -2656,9 +2688,9 @@ public void SwitchFallOut_Submission() // (4,5): error CS0163: Control cannot fall through from one case label ('case 1:') to another // case 1: Diagnostic(ErrorCode.ERR_SwitchFallThrough, "case 1:").WithArguments("case 1:").WithLocation(4, 5), - // (6,5): error CS8070: Control cannot fall out of switch from final case label ('default:') + // (6,5): error CS8070: Control cannot fall out of switch from final case label ('default') // default: - Diagnostic(ErrorCode.ERR_SwitchFallOut, "default:").WithArguments("default:").WithLocation(6, 5) + Diagnostic(ErrorCode.ERR_SwitchFallOut, "default:").WithArguments("default").WithLocation(6, 5) ); } diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetDeclaredSymbolAPITests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetDeclaredSymbolAPITests.cs index 53a0644212c..551a999bf7e 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetDeclaredSymbolAPITests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelGetDeclaredSymbolAPITests.cs @@ -705,7 +705,7 @@ void M() var labelSymbol = (SourceLabelSymbol)symbol1; Assert.Null(labelSymbol.SwitchCaseLabelConstant); Assert.Equal(switchLabel, labelSymbol.IdentifierNodeOrToken.AsNode()); - Assert.Equal("default:", labelSymbol.Name); + Assert.Equal("default", labelSymbol.Name); } [Fact] diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DeclaringSyntaxNodeTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DeclaringSyntaxNodeTests.cs index 4f8938e3461..81950df71d4 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DeclaringSyntaxNodeTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DeclaringSyntaxNodeTests.cs @@ -638,7 +638,7 @@ void m(int i) CheckDeclaringSyntax(comp, tree, "lab3", SymbolKind.Label); CheckDeclaringSyntax(comp, tree, "case 4:", SymbolKind.Label); CheckDeclaringSyntax(comp, tree, "case 3:", SymbolKind.Label); - CheckDeclaringSyntax(comp, tree, "default:", SymbolKind.Label); + CheckDeclaringSyntax(comp, tree, "default", SymbolKind.Label); } diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs index f3ca1237fe8..f3c0849264f 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs @@ -2261,7 +2261,7 @@ static void Main() default: goto $$"; - await VerifyItemExistsAsync(markup, "default:"); + await VerifyItemExistsAsync(markup, "default"); } [Fact, Trait(Traits.Feature, Traits.Features.Completion)] diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb index 063fa8e6a4c..7e3408bbfe8 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb @@ -574,6 +574,118 @@ class C End Using End Function + + + Public Async Function TestDefaultSwitchLabel() As Task + Using state = TestState.CreateCSharpTestState( + ) + + state.SendTypeChars("d") + Await state.AssertSelectedCompletionItem(displayText:="default", isHardSelected:=True) + state.SendTypeChars(";") + Assert.Contains("goto default;", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + + + + Public Async Function TestGotoOrdinaryLabel() As Task + Using state = TestState.CreateCSharpTestState( + ) + + state.SendTypeChars("l") + Await state.AssertSelectedCompletionItem(displayText:="label1", isHardSelected:=True) + state.SendTypeChars(";") + Assert.Contains("goto label1;", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + + + + Public Async Function TestEscapedDefaultLabel() As Task + Using state = TestState.CreateCSharpTestState( + ) + + state.SendTypeChars("d") + Await state.AssertSelectedCompletionItem(displayText:="@default", isHardSelected:=True) + state.SendTypeChars(";") + Assert.Contains("goto @default;", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + + + + Public Async Function TestEscapedDefaultLabel2() As Task + Using state = TestState.CreateCSharpTestState( + ) + + state.SendTypeChars("d") + Await state.AssertSelectedCompletionItem(displayText:="default", isHardSelected:=True) + state.SendTypeChars(";") + Assert.Contains("goto default;", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + + + + Public Async Function TestEscapedDefaultLabelWithoutSwitch() As Task + Using state = TestState.CreateCSharpTestState( + ) + + state.SendTypeChars("d") + Await state.AssertSelectedCompletionItem(displayText:="@default", isHardSelected:=True) + state.SendTypeChars(";") + Assert.Contains("goto @default;", state.GetLineTextFromCaretPosition(), StringComparison.Ordinal) + End Using + End Function + Public Async Function TestSymbolInTupleLiteral() As Task diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/CompletionUtilities.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/CompletionUtilities.cs index f3e011653c9..f6f1e6f055b 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/CompletionUtilities.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/CompletionUtilities.cs @@ -107,6 +107,12 @@ public static string GetInsertionText(ISymbol symbol, SyntaxContext context) } } + if (symbol.Kind == SymbolKind.Label && + symbol.DeclaringSyntaxReferences[0].GetSyntax().Kind() == SyntaxKind.DefaultSwitchLabel) + { + return symbol.Name; + } + return symbol.Name.EscapeIdentifier(isQueryContext: context.IsInQuery); } } -- GitLab