未验证 提交 7650489c 编写于 作者: D David 提交者: GitHub

Issue 27925 - Squiggle first line / entire parenthetical expression in remove...

Issue 27925 - Squiggle first line / entire parenthetical expression in remove unnecessary parentheses diagnostic (#32500)

* Update unnecessary parentheses analyzer to squiggle the entire
expression / first line.
Resolves #27925 
上级 0137cfab
...@@ -2135,7 +2135,7 @@ void Goo() ...@@ -2135,7 +2135,7 @@ void Goo()
public async Task TestAttribute() public async Task TestAttribute()
{ {
var input = @"[ assembly : [|Guid|] ( ""9ed54f84-a89d-4fcd-a854-44251e925f09"" ) ] "; var input = @"[ assembly : [|Guid|] ( ""9ed54f84-a89d-4fcd-a854-44251e925f09"" ) ] ";
await TestActionCountAsync(input, 1); await TestActionCountAsync(input, 2);
await TestAsync( await TestAsync(
input, input,
......
...@@ -4394,7 +4394,7 @@ void Goo() ...@@ -4394,7 +4394,7 @@ void Goo()
{ {
} }
}", }",
count: 3); count: 6);
} }
[WorkItem(543061, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543061")] [WorkItem(543061, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543061")]
......
...@@ -1052,7 +1052,7 @@ static void Main(string[] args) ...@@ -1052,7 +1052,7 @@ static void Main(string[] args)
public async Task TestAttribute() public async Task TestAttribute()
{ {
var input = @"[ assembly : [|Guid|] ( ""9ed54f84-a89d-4fcd-a854-44251e925f09"" ) ] "; var input = @"[ assembly : [|Guid|] ( ""9ed54f84-a89d-4fcd-a854-44251e925f09"" ) ] ";
await TestActionCountAsync(input, 1); await TestActionCountAsync(input, 2);
await TestInRegularAndScriptAsync( await TestInRegularAndScriptAsync(
input, input,
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System; using System;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryParentheses; using Microsoft.CodeAnalysis.CSharp.RemoveUnnecessaryParentheses;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics; using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics;
using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities; using Roslyn.Test.Utilities;
using Xunit; using Xunit;
...@@ -17,13 +19,13 @@ public partial class RemoveUnnecessaryParenthesesTests : AbstractCSharpDiagnosti ...@@ -17,13 +19,13 @@ public partial class RemoveUnnecessaryParenthesesTests : AbstractCSharpDiagnosti
internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace)
=> (new CSharpRemoveUnnecessaryParenthesesDiagnosticAnalyzer(), new CSharpRemoveUnnecessaryParenthesesCodeFixProvider()); => (new CSharpRemoveUnnecessaryParenthesesDiagnosticAnalyzer(), new CSharpRemoveUnnecessaryParenthesesCodeFixProvider());
private async Task TestAsync(string initial, string expected, bool offeredWhenRequireForClarityIsEnabled) private async Task TestAsync(string initial, string expected, bool offeredWhenRequireForClarityIsEnabled, int index = 0)
{ {
await TestInRegularAndScriptAsync(initial, expected, options: RemoveAllUnnecessaryParentheses); await TestInRegularAndScriptAsync(initial, expected, options: RemoveAllUnnecessaryParentheses, index: index);
if (offeredWhenRequireForClarityIsEnabled) if (offeredWhenRequireForClarityIsEnabled)
{ {
await TestInRegularAndScriptAsync(initial, expected, options: RequireAllParenthesesForClarity); await TestInRegularAndScriptAsync(initial, expected, options: RequireAllParenthesesForClarity, index: index);
} }
else else
{ {
...@@ -31,6 +33,17 @@ private async Task TestAsync(string initial, string expected, bool offeredWhenRe ...@@ -31,6 +33,17 @@ private async Task TestAsync(string initial, string expected, bool offeredWhenRe
} }
} }
internal override bool ShouldSkipMessageDescriptionVerification(DiagnosticDescriptor descriptor)
{
return descriptor.CustomTags.Contains(WellKnownDiagnosticTags.Unnecessary) && descriptor.DefaultSeverity == DiagnosticSeverity.Hidden;
}
private DiagnosticDescription GetRemoveUnnecessaryParenthesesDiagnostic(string text, int line, int column)
{
var diagnosticId = IDEDiagnosticIds.RemoveUnnecessaryParenthesesDiagnosticId;
return TestHelpers.Diagnostic(diagnosticId, text, startLocation: new LinePosition(line, column));
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessaryParentheses)] [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessaryParentheses)]
public async Task TestVariableInitializer_TestWithAllOptionsSetToIgnore() public async Task TestVariableInitializer_TestWithAllOptionsSetToIgnore()
{ {
...@@ -388,7 +401,7 @@ void M() ...@@ -388,7 +401,7 @@ void M()
{ {
int i = ( 1 + 2 ); int i = ( 1 + 2 );
} }
}", offeredWhenRequireForClarityIsEnabled: true); }", offeredWhenRequireForClarityIsEnabled: true, index: 1);
} }
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessaryParentheses)] [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessaryParentheses)]
...@@ -2064,7 +2077,7 @@ void M() ...@@ -2064,7 +2077,7 @@ void M()
#endif #endif
} }
}", }",
offeredWhenRequireForClarityIsEnabled: true); offeredWhenRequireForClarityIsEnabled: true, index: 1);
} }
[WorkItem(29454, "https://github.com/dotnet/roslyn/issues/29454")] [WorkItem(29454, "https://github.com/dotnet/roslyn/issues/29454")]
...@@ -2276,9 +2289,90 @@ public async Task TestMissingForNestedConditionalExpressionInLambda() ...@@ -2276,9 +2289,90 @@ public async Task TestMissingForNestedConditionalExpressionInLambda()
void Test(bool a) void Test(bool a)
{ {
Func<int, string> lambda = Func<int, string> lambda =
number => (number + $""{ ($$a ? ""foo"" : ""bar"") }""); number => number + $""{ ($$a ? ""foo"" : ""bar"") }"";
} }
}", new TestParameters(options: RemoveAllUnnecessaryParentheses)); }", new TestParameters(options: RemoveAllUnnecessaryParentheses));
} }
[WorkItem(27925, "https://github.com/dotnet/roslyn/issues/27925")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessaryParentheses)]
public async Task TestUnnecessaryParenthesisDiagnosticSingleLineExpression()
{
var openParenthesesDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(", 4, 16);
var parentheticalExpressionDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(1 + 2)", 4, 16);
var closeParenthesesDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic(")", 4, 22);
await TestDiagnosticsAsync(
@"class C
{
void M()
{
int x = [|(1 + 2)|];
}
}", new TestParameters(options: RemoveAllUnnecessaryParentheses), parentheticalExpressionDiagnostic, openParenthesesDiagnostic, closeParenthesesDiagnostic);
}
[WorkItem(27925, "https://github.com/dotnet/roslyn/issues/27925")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessaryParentheses)]
public async Task TestUnnecessaryParenthesisDiagnosticInMultiLineExpression()
{
var openParenthesesDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(", 4, 16);
var firstLineParentheticalExpressionDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(1 +", 4, 16);
var closeParenthesesDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic(")", 5, 13);
await TestDiagnosticsAsync(
@"class C
{
void M()
{
int x = [|(1 +
2)|];
}
}", new TestParameters(options: RemoveAllUnnecessaryParentheses), firstLineParentheticalExpressionDiagnostic, openParenthesesDiagnostic, closeParenthesesDiagnostic);
}
[WorkItem(27925, "https://github.com/dotnet/roslyn/issues/27925")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessaryParentheses)]
public async Task TestUnnecessaryParenthesisDiagnosticInNestedExpression()
{
var outerOpenParenthesesDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(", 4, 16);
var outerParentheticalExpressionDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(1 + (2 + 3) + 4)", 4, 16);
var outerCloseParenthesesDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic(")", 4, 32);
var innerOpenParenthesesDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(", 4, 21);
var innerParentheticalExpressionDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(2 + 3)", 4, 21);
var innerCloseParenthesesDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic(")", 4, 27);
var expectedDiagnostics = new DiagnosticDescription[] { outerParentheticalExpressionDiagnostic, outerOpenParenthesesDiagnostic,
outerCloseParenthesesDiagnostic, innerParentheticalExpressionDiagnostic, innerOpenParenthesesDiagnostic, innerCloseParenthesesDiagnostic };
await TestDiagnosticsAsync(
@"class C
{
void M()
{
int x = [|(1 + (2 + 3) + 4)|];
}
}", new TestParameters(options: RemoveAllUnnecessaryParentheses), expectedDiagnostics);
}
[WorkItem(27925, "https://github.com/dotnet/roslyn/issues/27925")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessaryParentheses)]
public async Task TestUnnecessaryParenthesisDiagnosticInNestedMultiLineExpression()
{
var outerOpenParenthesesDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(", 4, 16);
var outerFirstLineParentheticalExpressionDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(1 + 2 +", 4, 16);
var outerCloseParenthesesDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic(")", 6, 17);
var innerOpenParenthesesDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(", 5, 12);
var innerParentheticalExpressionDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(3 + 4)", 5, 12);
var innerCloseParenthesesDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic(")", 5, 18);
var expectedDiagnostics = new DiagnosticDescription[] { outerFirstLineParentheticalExpressionDiagnostic, outerOpenParenthesesDiagnostic,
outerCloseParenthesesDiagnostic, innerParentheticalExpressionDiagnostic, innerOpenParenthesesDiagnostic, innerCloseParenthesesDiagnostic };
await TestDiagnosticsAsync(
@"class C
{
void M()
{
int x = [|(1 + 2 +
(3 + 4) +
5 + 6)|];
}
}", new TestParameters(options: RemoveAllUnnecessaryParentheses), expectedDiagnostics);
}
} }
} }
...@@ -38,6 +38,19 @@ internal virtual (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderA ...@@ -38,6 +38,19 @@ internal virtual (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderA
: CreateDiagnosticProviderAndFixer(workspace, parameters); : CreateDiagnosticProviderAndFixer(workspace, parameters);
} }
internal virtual bool ShouldSkipMessageDescriptionVerification(DiagnosticDescriptor descriptor)
{
if (descriptor.CustomTags.Contains(WellKnownDiagnosticTags.NotConfigurable))
{
if (!descriptor.IsEnabledByDefault || descriptor.DefaultSeverity == DiagnosticSeverity.Hidden)
{
// The message only displayed if either enabled and not hidden, or configurable
return true;
}
}
return false;
}
[Fact] [Fact]
public void TestSupportedDiagnosticsMessageTitle() public void TestSupportedDiagnosticsMessageTitle()
{ {
...@@ -75,13 +88,9 @@ public void TestSupportedDiagnosticsMessageDescription() ...@@ -75,13 +88,9 @@ public void TestSupportedDiagnosticsMessageDescription()
foreach (var descriptor in diagnosticAnalyzer.SupportedDiagnostics) foreach (var descriptor in diagnosticAnalyzer.SupportedDiagnostics)
{ {
if (descriptor.CustomTags.Contains(WellKnownDiagnosticTags.NotConfigurable)) if (ShouldSkipMessageDescriptionVerification(descriptor))
{ {
if (!descriptor.IsEnabledByDefault || descriptor.DefaultSeverity == DiagnosticSeverity.Hidden) continue;
{
// The message only displayed if either enabled and not hidden, or configurable
continue;
}
} }
Assert.NotEqual("", descriptor.MessageFormat?.ToString() ?? ""); Assert.NotEqual("", descriptor.MessageFormat?.ToString() ?? "");
......
...@@ -193,20 +193,13 @@ protected Document GetDocumentAndAnnotatedSpan(TestWorkspace workspace, out stri ...@@ -193,20 +193,13 @@ protected Document GetDocumentAndAnnotatedSpan(TestWorkspace workspace, out stri
CancellationToken.None); CancellationToken.None);
await fixer.RegisterCodeFixesAsync(context); await fixer.RegisterCodeFixesAsync(context);
if (fixes.Count > 0)
{
break;
}
} }
var actions = fixes.SelectAsArray(f => f.Action); var actions = fixes.SelectAsArray(f => f.Action);
if (actions.Length == 1)
{ actions = actions.SelectMany(a => a is TopLevelSuppressionCodeAction
if (actions[0] is TopLevelSuppressionCodeAction suppressionAction) ? a.NestedCodeActions
{ : ImmutableArray.Create(a)).ToImmutableArray();
actions = suppressionAction.NestedCodeActions;
}
}
actions = MassageActions(actions); actions = MassageActions(actions);
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryParentheses Imports Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessaryParentheses
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.RemoveUnnecessaryParentheses Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.RemoveUnnecessaryParentheses
...@@ -18,11 +19,21 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.RemoveUnnecessaryP ...@@ -18,11 +19,21 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.RemoveUnnecessaryP
Return (New VisualBasicRemoveUnnecessaryParenthesesDiagnosticAnalyzer(), New VisualBasicRemoveUnnecessaryParenthesesCodeFixProvider()) Return (New VisualBasicRemoveUnnecessaryParenthesesDiagnosticAnalyzer(), New VisualBasicRemoveUnnecessaryParenthesesCodeFixProvider())
End Function End Function
Private Shadows Async Function TestAsync(initial As String, expected As String, offeredWhenRequireAllParenthesesForClarityIsEnabled As Boolean) As Task Friend Function GetRemoveUnnecessaryParenthesesDiagnostic(text As String, line As Integer, column As Integer) As DiagnosticDescription
Await TestInRegularAndScriptAsync(initial, expected, options:=RemoveAllUnnecessaryParentheses) Return TestHelpers.Diagnostic(IDEDiagnosticIds.RemoveUnnecessaryParenthesesDiagnosticId, text, startLocation:=New LinePosition(line, column))
End Function
Friend Overrides Function ShouldSkipMessageDescriptionVerification(descriptor As DiagnosticDescriptor) As Boolean
Return descriptor.CustomTags.Contains(WellKnownDiagnosticTags.Unnecessary) And descriptor.DefaultSeverity = DiagnosticSeverity.Hidden
End Function
Private Shadows Async Function TestAsync(initial As String, expected As String,
offeredWhenRequireAllParenthesesForClarityIsEnabled As Boolean,
Optional ByVal index As Integer = 0) As Task
Await TestInRegularAndScriptAsync(initial, expected, options:=RemoveAllUnnecessaryParentheses, index:=index)
If (offeredWhenRequireAllParenthesesForClarityIsEnabled) Then If (offeredWhenRequireAllParenthesesForClarityIsEnabled) Then
Await TestInRegularAndScriptAsync(initial, expected, options:=MyBase.RequireAllParenthesesForClarity) Await TestInRegularAndScriptAsync(initial, expected, options:=MyBase.RequireAllParenthesesForClarity, index:=index)
Else Else
Await TestMissingAsync(initial, parameters:=New TestParameters(options:=MyBase.RequireAllParenthesesForClarity)) Await TestMissingAsync(initial, parameters:=New TestParameters(options:=MyBase.RequireAllParenthesesForClarity))
End If End If
...@@ -297,7 +308,7 @@ end class", ...@@ -297,7 +308,7 @@ end class",
sub M() sub M()
dim i = ( 1 + 2 ) dim i = ( 1 + 2 )
end sub end sub
end class", offeredWhenRequireAllParenthesesForClarityIsEnabled:=True) end class", offeredWhenRequireAllParenthesesForClarityIsEnabled:=True, index:=1)
End Function End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessaryParentheses)> <Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessaryParentheses)>
...@@ -545,5 +556,74 @@ end class", ...@@ -545,5 +556,74 @@ end class",
end sub end sub
end class", parameters:=New TestParameters(options:=RemoveAllUnnecessaryParentheses)) end class", parameters:=New TestParameters(options:=RemoveAllUnnecessaryParentheses))
End Function End Function
<WorkItem(27925, "https://github.com/dotnet/roslyn/issues/27925")>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessaryParentheses)>
Public Async Function TestUnnecessaryParenthesisDiagnosticSingleLineExpression() As Task
Dim openParenthesesDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(", 2, 16)
Dim parentheticalExpressionDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(1 + 2)", 2, 16)
Dim closeParenthesesDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic(")", 2, 22)
Await TestDiagnosticsAsync(
"class C
sub M()
dim x = [|(1 + 2)|]
end sub
end class", New TestParameters(options:=RemoveAllUnnecessaryParentheses), parentheticalExpressionDiagnostic, openParenthesesDiagnostic, closeParenthesesDiagnostic)
End Function
<WorkItem(27925, "https://github.com/dotnet/roslyn/issues/27925")>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessaryParentheses)>
Public Async Function TestUnnecessaryParenthesisDiagnosticInMultiLineExpression() As Task
Dim openParenthesesDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(", 2, 16)
Dim firstLineParentheticalExpressionDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(1 +", 2, 16)
Dim closeParenthesesDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic(")", 3, 13)
Await TestDiagnosticsAsync(
"class C
sub M()
dim x = [|(1 +
2)|]
end sub
end class", New TestParameters(options:=RemoveAllUnnecessaryParentheses), firstLineParentheticalExpressionDiagnostic, openParenthesesDiagnostic, closeParenthesesDiagnostic)
End Function
<WorkItem(27925, "https://github.com/dotnet/roslyn/issues/27925")>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessaryParentheses)>
Public Async Function TestUnnecessaryParenthesisDiagnosticInNestedExpression() As Task
Dim outerOpenParenthesesDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(", 2, 16)
Dim outerParentheticalExpressionDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(1 + (2 + 3) + 4)", 2, 16)
Dim outerCloseParenthesesDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic(")", 2, 32)
Dim innerOpenParenthesesDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(", 2, 21)
Dim innerParentheticalExpressionDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(2 + 3)", 2, 21)
Dim innerCloseParenthesesDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic(")", 2, 27)
Dim expectedDiagnostics = New DiagnosticDescription() {outerParentheticalExpressionDiagnostic, outerOpenParenthesesDiagnostic,
outerCloseParenthesesDiagnostic, innerParentheticalExpressionDiagnostic, innerOpenParenthesesDiagnostic, innerCloseParenthesesDiagnostic}
Await TestDiagnosticsAsync(
"class C
sub M()
dim x = [|(1 + (2 + 3) + 4)|]
end sub
end class", New TestParameters(options:=RemoveAllUnnecessaryParentheses), expectedDiagnostics)
End Function
<WorkItem(27925, "https://github.com/dotnet/roslyn/issues/27925")>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessaryParentheses)>
Public Async Function TestUnnecessaryParenthesisDiagnosticInNestedMultiLineExpression() As Task
Dim outerOpenParenthesesDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(", 2, 16)
Dim outerFirstLineParentheticalExpressionDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(1 + 2 +", 2, 16)
Dim outerCloseParenthesesDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic(")", 4, 17)
Dim innerOpenParenthesesDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(", 3, 12)
Dim innerParentheticalExpressionDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic("(3 + 4)", 3, 12)
Dim innerCloseParenthesesDiagnostic = GetRemoveUnnecessaryParenthesesDiagnostic(")", 3, 18)
Dim expectedDiagnostics = New DiagnosticDescription() {outerFirstLineParentheticalExpressionDiagnostic, outerOpenParenthesesDiagnostic,
outerCloseParenthesesDiagnostic, innerParentheticalExpressionDiagnostic, innerOpenParenthesesDiagnostic, innerCloseParenthesesDiagnostic}
Await TestDiagnosticsAsync(
"class C
sub M()
dim x = [|(1 + 2 +
(3 + 4) +
5 + 6)|]
end sub
end class", New TestParameters(options:=RemoveAllUnnecessaryParentheses), expectedDiagnostics)
End Function
End Class End Class
End Namespace End Namespace
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Options;
using Roslyn.Utilities; using Roslyn.Utilities;
using System.Collections.Immutable;
namespace Microsoft.CodeAnalysis.RemoveUnnecessaryParentheses namespace Microsoft.CodeAnalysis.RemoveUnnecessaryParentheses
{ {
...@@ -15,6 +16,11 @@ internal abstract class AbstractParenthesesDiagnosticAnalyzer : AbstractBuiltInC ...@@ -15,6 +16,11 @@ internal abstract class AbstractParenthesesDiagnosticAnalyzer : AbstractBuiltInC
{ {
} }
protected AbstractParenthesesDiagnosticAnalyzer(ImmutableArray<DiagnosticDescriptor> diagnosticDescriptors)
: base(diagnosticDescriptors)
{
}
protected PerLanguageOption<CodeStyleOption<ParenthesesPreference>> GetLanguageOption(PrecedenceKind precedenceKind) protected PerLanguageOption<CodeStyleOption<ParenthesesPreference>> GetLanguageOption(PrecedenceKind precedenceKind)
{ {
switch (precedenceKind) switch (precedenceKind)
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
using System.Threading;
using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.CodeAnalysis.RemoveUnnecessaryParentheses namespace Microsoft.CodeAnalysis.RemoveUnnecessaryParentheses
{ {
...@@ -15,10 +19,27 @@ internal abstract class AbstractRemoveUnnecessaryParenthesesDiagnosticAnalyzer< ...@@ -15,10 +19,27 @@ internal abstract class AbstractRemoveUnnecessaryParenthesesDiagnosticAnalyzer<
where TLanguageKindEnum : struct where TLanguageKindEnum : struct
where TParenthesizedExpressionSyntax : SyntaxNode where TParenthesizedExpressionSyntax : SyntaxNode
{ {
/// <summary>
/// A diagnostic descriptor that will fade the span (but not put a message or squiggle).
/// </summary>
private static readonly DiagnosticDescriptor s_diagnosticWithFade = CreateDescriptorWithId(
IDEDiagnosticIds.RemoveUnnecessaryParenthesesDiagnosticId,
new LocalizableResourceString(nameof(FeaturesResources.Remove_unnecessary_parentheses), FeaturesResources.ResourceManager, typeof(FeaturesResources)),
string.Empty,
isUnneccessary: true);
/// <summary>
/// A diagnostic descriptor used to squiggle and message the span, but will not fade.
/// </summary>
private static readonly DiagnosticDescriptor s_diagnosticWithoutFade = CreateDescriptorWithId(
IDEDiagnosticIds.RemoveUnnecessaryParenthesesDiagnosticId,
new LocalizableResourceString(nameof(FeaturesResources.Remove_unnecessary_parentheses), FeaturesResources.ResourceManager, typeof(FeaturesResources)),
new LocalizableResourceString(nameof(FeaturesResources.Parentheses_can_be_removed), FeaturesResources.ResourceManager, typeof(FeaturesResources)),
isUnneccessary: false);
protected AbstractRemoveUnnecessaryParenthesesDiagnosticAnalyzer() protected AbstractRemoveUnnecessaryParenthesesDiagnosticAnalyzer()
: base(IDEDiagnosticIds.RemoveUnnecessaryParenthesesDiagnosticId, : base(ImmutableArray.Create(s_diagnosticWithFade, s_diagnosticWithoutFade))
new LocalizableResourceString(nameof(FeaturesResources.Remove_unnecessary_parentheses), FeaturesResources.ResourceManager, typeof(FeaturesResources)),
new LocalizableResourceString(nameof(FeaturesResources.Parentheses_can_be_removed), FeaturesResources.ResourceManager, typeof(FeaturesResources)))
{ {
} }
...@@ -41,8 +62,8 @@ protected sealed override void InitializeWorker(AnalysisContext context) ...@@ -41,8 +62,8 @@ protected sealed override void InitializeWorker(AnalysisContext context)
private void AnalyzeSyntax(SyntaxNodeAnalysisContext context) private void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
{ {
var syntaxTree = context.SemanticModel.SyntaxTree; var syntaxTree = context.SemanticModel.SyntaxTree;
var cancellationTokan = context.CancellationToken; var cancellationToken = context.CancellationToken;
var optionSet = context.Options.GetDocumentOptionSetAsync(syntaxTree, cancellationTokan).GetAwaiter().GetResult(); var optionSet = context.Options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult();
if (optionSet == null) if (optionSet == null)
{ {
return; return;
...@@ -109,16 +130,35 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context) ...@@ -109,16 +130,35 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
var additionalLocations = ImmutableArray.Create(parenthesizedExpression.GetLocation()); var additionalLocations = ImmutableArray.Create(parenthesizedExpression.GetLocation());
// Fades the open parentheses character and reports the suggestion.
context.ReportDiagnostic(Diagnostic.Create(s_diagnosticWithFade, parenthesizedExpression.GetFirstToken().GetLocation(), additionalLocations));
// Generates diagnostic used to squiggle the parenthetical expression.
context.ReportDiagnostic(DiagnosticHelper.Create( context.ReportDiagnostic(DiagnosticHelper.Create(
UnnecessaryWithSuggestionDescriptor, s_diagnosticWithoutFade,
parenthesizedExpression.GetFirstToken().GetLocation(), GetDiagnosticSquiggleLocation(parenthesizedExpression, cancellationToken),
severity, severity,
additionalLocations, additionalLocations,
properties: null)); properties: null));
context.ReportDiagnostic(Diagnostic.Create( // Fades the close parentheses character.
UnnecessaryWithoutSuggestionDescriptor, context.ReportDiagnostic(Diagnostic.Create(s_diagnosticWithFade, parenthesizedExpression.GetLastToken().GetLocation(), additionalLocations));
parenthesizedExpression.GetLastToken().GetLocation(), additionalLocations)); }
/// <summary>
/// Gets the span of text to squiggle underline.
/// If the expression is contained within a single line, the entire expression span is returned.
/// Otherwise it will return the span from the expression start to the end of the same line.
/// </summary>
private Location GetDiagnosticSquiggleLocation(TParenthesizedExpressionSyntax parenthesizedExpression, CancellationToken cancellationToken)
{
var parenthesizedExpressionLocation = parenthesizedExpression.GetLocation();
var lines = parenthesizedExpression.SyntaxTree.GetText(cancellationToken).Lines;
var expressionFirstLine = lines.GetLineFromPosition(parenthesizedExpressionLocation.SourceSpan.Start);
var textSpanEndPosition = Math.Min(parenthesizedExpressionLocation.SourceSpan.End, expressionFirstLine.Span.End);
return Location.Create(parenthesizedExpression.SyntaxTree, TextSpan.FromBounds(parenthesizedExpressionLocation.SourceSpan.Start, textSpanEndPosition));
} }
} }
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册