提交 5885a17e 编写于 作者: B Brett V. Forsgren

update analyzer and add fix-all code action

上级 db62c6f7
......@@ -165,6 +165,7 @@
<Compile Include="CodeActions\ReplaceMethodWithProperty\ReplaceMethodWithPropertyTests.cs" />
<Compile Include="Diagnostics\InvokeDelegateWithConditionalAccess\InvokeDelegateWithConditionalAccessTests.cs" />
<Compile Include="Diagnostics\QualifyMemberAccess\QualifyMemberAccessTests.cs" />
<Compile Include="Diagnostics\QualifyMemberAccess\QualifyMemberAccessTests_FixAllTests.cs" />
<Compile Include="Diagnostics\SpellCheck\SpellCheckTests.cs" />
<Compile Include="Diagnostics\Suppression\RemoveSuppressionTests.cs" />
<Compile Include="Diagnostics\Suppression\SuppressionTest_FixMultipleTests.cs" />
......
......@@ -13,7 +13,7 @@
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.QualifyMemberAccess
{
public class QualifyMemberAccessTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
public partial class QualifyMemberAccessTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
{
internal override Tuple<DiagnosticAnalyzer, CodeFixProvider> CreateDiagnosticProviderAndFixer(Workspace workspace)
{
......@@ -22,12 +22,22 @@ public class QualifyMemberAccessTests : AbstractCSharpDiagnosticProviderBasedUse
private Task TestAsyncWithOption(string code, string expected, PerLanguageOption<CodeStyleOption<bool>> option)
{
return TestAsync(code, expected, options: Option(option, true, NotificationOption.Error));
return TestAsyncWithOptionAndNotificationOption(code, expected, option, NotificationOption.Error);
}
private Task TestAsyncWithOptionAndNotificationOption(string code, string expected, PerLanguageOption<CodeStyleOption<bool>> option, NotificationOption notification)
{
return TestAsync(code, expected, options: Option(option, true, notification));
}
private Task TestMissingAsyncWithOption(string code, PerLanguageOption<CodeStyleOption<bool>> option)
{
return TestMissingAsync(code, options: Option(option, true, NotificationOption.Error));
return TestMissingAsyncWithOptionAndNotificationOption(code, option, NotificationOption.Error);
}
private Task TestMissingAsyncWithOptionAndNotificationOption(string code, PerLanguageOption<CodeStyleOption<bool>> option, NotificationOption notification)
{
return TestMissingAsync(code, options: Option(option, true, notification));
}
[WorkItem(7065, "https://github.com/dotnet/roslyn/issues/7065")]
......@@ -351,5 +361,40 @@ public async Task QualifyEventAccess_NotSuggestedOnStatic()
@"using System; class C { static event EventHandler e; } void Handler(object sender, EventArgs args) { [|e|] += Handler; } }",
CodeStyleOptions.QualifyEventAccess);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsQualifyMemberAccess)]
public async Task QualifyMemberAccessNotPresentOnNotificationOptionNone()
{
await TestMissingAsyncWithOptionAndNotificationOption(
@"class Class { int Property { get; set; }; void M() { [|Property|] = 1; } }",
CodeStyleOptions.QualifyPropertyAccess, NotificationOption.None);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsQualifyMemberAccess)]
public async Task QualifyMemberAccessOnNotificationOptionInfo()
{
await TestAsyncWithOptionAndNotificationOption(
@"class Class { int Property { get; set; }; void M() { [|Property|] = 1; } }",
@"class Class { int Property { get; set; }; void M() { this.Property = 1; } }",
CodeStyleOptions.QualifyPropertyAccess, NotificationOption.Info);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsQualifyMemberAccess)]
public async Task QualifyMemberAccessOnNotificationOptionWarning()
{
await TestAsyncWithOptionAndNotificationOption(
@"class Class { int Property { get; set; }; void M() { [|Property|] = 1; } }",
@"class Class { int Property { get; set; }; void M() { this.Property = 1; } }",
CodeStyleOptions.QualifyPropertyAccess, NotificationOption.Warning);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsQualifyMemberAccess)]
public async Task QualifyMemberAccessOnNotificationOptionError()
{
await TestAsyncWithOptionAndNotificationOption(
@"class Class { int Property { get; set; }; void M() { [|Property|] = 1; } }",
@"class Class { int Property { get; set; }; void M() { this.Property = 1; } }",
CodeStyleOptions.QualifyPropertyAccess, NotificationOption.Error);
}
}
}
// 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.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeStyle;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.QualifyMemberAccess
{
public partial class QualifyMemberAccessTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
{
[Fact]
[Trait(Traits.Feature, Traits.Features.CodeActionsQualifyMemberAccess)]
[Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)]
public async Task TestFixAllInSolution_QualifyMemberAccess()
{
var input = @"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" CommonReferences=""true"">
<Document>
using System;
class C
{
int Property { get; set; }
int OtherProperty { get; set; }
void M()
{
{|FixAllInSolution:Property|} = 1;
var x = OtherProperty;
}
}
</Document>
<Document>
using System;
class D
{
string StringProperty { get; set; }
int field;
void N()
{
StringProperty = string.Empty;
field = 0; // ensure this doesn't get qualified
}
}
</Document>
</Project>
</Workspace>";
var expected = @"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" CommonReferences=""true"">
<Document>
using System;
class C
{
int Property { get; set; }
int OtherProperty { get; set; }
void M()
{
this.Property = 1;
var x = this.OtherProperty;
}
}
</Document>
<Document>
using System;
class D
{
string StringProperty { get; set; }
int field;
void N()
{
this.StringProperty = string.Empty;
field = 0; // ensure this doesn't get qualified
}
}
</Document>
</Project>
</Workspace>";
await TestAsync(
initialMarkup: input,
expectedMarkup: expected,
options: Option(CodeStyleOptions.QualifyPropertyAccess, true, NotificationOption.Info),
compareTokens: false,
fixAllActionEquivalenceKey: FeaturesResources.AddQualification);
}
}
}
// 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.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.CodeFixes.SimplifyTypeNames;
using Microsoft.CodeAnalysis.CSharp.Diagnostics.SimplifyTypeNames;
using Microsoft.CodeAnalysis.Diagnostics;
using Roslyn.Test.Utilities;
using Xunit;
......@@ -851,6 +853,94 @@ private System.Int32 F(System.Int32 p1, System.Int16 p2)
await TestAsync(input, expected, compareTokens: false, fixAllActionEquivalenceKey: fixAllActionId);
}
[Fact]
[Trait(Traits.Feature, Traits.Features.CodeActionsQualifyMemberAccess)]
[Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)]
public async Task TestFixAllInSolution_RemoveMemberAccessQualification()
{
var input = @"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" CommonReferences=""true"">
<Document>
using System;
class C
{
int Property { get; set; }
int OtherProperty { get; set; }
void M()
{
{|FixAllInSolution:this.Property|} = 1;
var x = this.OtherProperty;
}
}
</Document>
<Document>
using System;
class D
{
string StringProperty { get; set; }
int field;
void N()
{
this.StringProperty = string.Empty;
this.field = 0; // ensure qualification isn't removed
}
}
</Document>
</Project>
</Workspace>";
var expected = @"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" CommonReferences=""true"">
<Document>
using System;
class C
{
int Property { get; set; }
int OtherProperty { get; set; }
void M()
{
Property = 1;
var x = OtherProperty;
}
}
</Document>
<Document>
using System;
class D
{
string StringProperty { get; set; }
int field;
void N()
{
StringProperty = string.Empty;
this.field = 0; // ensure qualification isn't removed
}
}
</Document>
</Project>
</Workspace>";
var options = OptionsSet(
Tuple.Create(CodeStyleOptions.QualifyPropertyAccess, false, NotificationOption.Info),
Tuple.Create(CodeStyleOptions.QualifyFieldAccess, true, NotificationOption.Info));
await TestAsync(
initialMarkup: input,
expectedMarkup: expected,
options: options,
compareTokens: false,
fixAllActionEquivalenceKey: CSharpFeaturesResources.RemoveThisQualification);
}
#endregion
}
}
......@@ -938,7 +938,7 @@ void M()
MyEvent +$$
}
}";
using (var testState = EventHookupTestState.CreateTestState(markup, QualifyMethodAccess))
using (var testState = EventHookupTestState.CreateTestState(markup, QualifyMethodAccessWithNotification(NotificationOption.Error)))
{
testState.SendTypeChar('=');
testState.SendTab();
......@@ -962,6 +962,46 @@ private void C_MyEvent()
}
}
private IDictionary<OptionKey, object> QualifyMethodAccess => new Dictionary<OptionKey, object>() { { new OptionKey(CodeStyleOptions.QualifyMethodAccess, LanguageNames.CSharp), new CodeStyleOption<bool>(true, NotificationOption.Error) } };
[WpfFact, Trait(Traits.Feature, Traits.Features.EventHookup)]
public async Task EventHookupWithQualifiedMethodAccessAndNotificationOptionNone()
{
// This validates the scenario where the user has stated that they prefer `this.` qualification but the
// notification level is `None`, which means existing violations of the rule won't be flagged but newly
// generated code will conform appropriately.
var markup = @"
class C
{
event System.Action MyEvent;
void M()
{
MyEvent +$$
}
}";
using (var testState = EventHookupTestState.CreateTestState(markup, QualifyMethodAccessWithNotification(NotificationOption.None)))
{
testState.SendTypeChar('=');
testState.SendTab();
await testState.WaitForAsynchronousOperationsAsync();
var expectedCode = @"
class C
{
event System.Action MyEvent;
void M()
{
MyEvent += this.C_MyEvent;
}
private void C_MyEvent()
{
throw new System.NotImplementedException();
}
}";
testState.AssertCodeIs(expectedCode);
}
}
private IDictionary<OptionKey, object> QualifyMethodAccessWithNotification(NotificationOption notification)
=> new Dictionary<OptionKey, object>() { { new OptionKey(CodeStyleOptions.QualifyMethodAccess, LanguageNames.CSharp), new CodeStyleOption<bool>(true, notification) } };
}
}
......@@ -369,7 +369,21 @@ protected static IList<CodeAction> FlattenActions(IEnumerable<CodeAction> codeAc
protected IDictionary<OptionKey, object> Option(PerLanguageOption<CodeStyle.CodeStyleOption<bool>> option, bool value, CodeStyle.NotificationOption notification)
{
return new Dictionary<OptionKey, object>() { { new OptionKey(option, GetLanguage()), new CodeStyle.CodeStyleOption<bool>(value, notification) } };
return OptionsSet(Tuple.Create(option, value, notification));
}
protected IDictionary<OptionKey, object> OptionsSet(params Tuple<PerLanguageOption<CodeStyle.CodeStyleOption<bool>>, bool, CodeStyle.NotificationOption>[] optionsToSet)
{
var options = new Dictionary<OptionKey, object>();
foreach (var triple in optionsToSet)
{
var option = triple.Item1;
var value = triple.Item2;
var notification = triple.Item3;
options.Add(new OptionKey(option, GetLanguage()), new CodeStyle.CodeStyleOption<bool>(value, notification));
}
return options;
}
}
}
......@@ -3046,6 +3046,150 @@ class C
</text>
Await TestAsync(input, expected, QualifyEventAccessOption(LanguageNames.CSharp))
End Function
<Fact, Trait(Traits.Feature, Traits.Features.Simplification)>
Public Async Function QualifyMemberAccessNotPresentOnNotificationOptionNone_CSharp() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
<![CDATA[
class C
{
int Property { get; set; }
void M()
{
{|Simplify:Property|} = 1;
}
}
]]>
</Document>
</Project>
</Workspace>
Dim expected =
<text>
<![CDATA[
class C
{
int Property { get; set; }
void M()
{
Property = 1;
}
}
]]>
</text>
Await TestAsync(input, expected, QualifyPropertyAccessOptionWithNotification(LanguageNames.CSharp, NotificationOption.None))
End Function
<Fact, Trait(Traits.Feature, Traits.Features.Simplification)>
Public Async Function QualifyMemberAccessOnNotificationOptionInfo_CSharp() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
<![CDATA[
class C
{
int Property { get; set; }
void M()
{
{|Simplify:this.Property|} = 1;
}
}
]]>
</Document>
</Project>
</Workspace>
Dim expected =
<text>
<![CDATA[
class C
{
int Property { get; set; }
void M()
{
this.Property = 1;
}
}
]]>
</text>
Await TestAsync(input, expected, QualifyPropertyAccessOptionWithNotification(LanguageNames.CSharp, NotificationOption.Info))
End Function
<Fact, Trait(Traits.Feature, Traits.Features.Simplification)>
Public Async Function QualifyMemberAccessOnNotificationOptionWarning_CSharp() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
<![CDATA[
class C
{
int Property { get; set; }
void M()
{
{|Simplify:this.Property|} = 1;
}
}
]]>
</Document>
</Project>
</Workspace>
Dim expected =
<text>
<![CDATA[
class C
{
int Property { get; set; }
void M()
{
this.Property = 1;
}
}
]]>
</text>
Await TestAsync(input, expected, QualifyPropertyAccessOptionWithNotification(LanguageNames.CSharp, NotificationOption.Warning))
End Function
<Fact, Trait(Traits.Feature, Traits.Features.Simplification)>
Public Async Function QualifyMemberAccessOnNotificationOptionError_CSharp() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
<![CDATA[
class C
{
int Property { get; set; }
void M()
{
{|Simplify:this.Property|} = 1;
}
}
]]>
</Document>
</Project>
</Workspace>
Dim expected =
<text>
<![CDATA[
class C
{
int Property { get; set; }
void M()
{
this.Property = 1;
}
}
]]>
</text>
Await TestAsync(input, expected, QualifyPropertyAccessOptionWithNotification(LanguageNames.CSharp, NotificationOption.Error))
End Function
#End Region
#Region "Normal Visual Basic Tests"
......@@ -5484,6 +5628,134 @@ End Class
Await TestAsync(input, expected, QualifyEventAccessOption(LanguageNames.VisualBasic))
End Function
<Fact, Trait(Traits.Feature, Traits.Features.Simplification)>
Public Async Function QualifyMemberAccessNotPresentOnNotificationOptionNone_VisualBasic() As Task
Dim input =
<Workspace>
<Project Language="Visual Basic" CommonReferences="true">
<Document>
<![CDATA[
Class C
Property I As Integer
Sub M()
{|Simplify:I|} = 1
End Sub
End Class
]]>
</Document>
</Project>
</Workspace>
Dim expected =
<text>
<![CDATA[
Class C
Property I As Integer
Sub M()
I = 1
End Sub
End Class
]]>
</text>
Await TestAsync(input, expected, QualifyPropertyAccessOptionWithNotification(LanguageNames.VisualBasic, NotificationOption.None))
End Function
<Fact, Trait(Traits.Feature, Traits.Features.Simplification)>
Public Async Function QualifyMemberAccessOnNotificationOptionInfo_VisualBasic() As Task
Dim input =
<Workspace>
<Project Language="Visual Basic" CommonReferences="true">
<Document>
<![CDATA[
Class C
Property I As Integer
Sub M()
{|Simplify:Me.I|} = 1
End Sub
End Class
]]>
</Document>
</Project>
</Workspace>
Dim expected =
<text>
<![CDATA[
Class C
Property I As Integer
Sub M()
Me.I = 1
End Sub
End Class
]]>
</text>
Await TestAsync(input, expected, QualifyPropertyAccessOptionWithNotification(LanguageNames.VisualBasic, NotificationOption.Info))
End Function
<Fact, Trait(Traits.Feature, Traits.Features.Simplification)>
Public Async Function QualifyMemberAccessOnNotificationOptionWarning_VisualBasic() As Task
Dim input =
<Workspace>
<Project Language="Visual Basic" CommonReferences="true">
<Document>
<![CDATA[
Class C
Property I As Integer
Sub M()
{|Simplify:Me.I|} = 1
End Sub
End Class
]]>
</Document>
</Project>
</Workspace>
Dim expected =
<text>
<![CDATA[
Class C
Property I As Integer
Sub M()
Me.I = 1
End Sub
End Class
]]>
</text>
Await TestAsync(input, expected, QualifyPropertyAccessOptionWithNotification(LanguageNames.VisualBasic, NotificationOption.Warning))
End Function
<Fact, Trait(Traits.Feature, Traits.Features.Simplification)>
Public Async Function QualifyMemberAccessOnNotificationOptionError_VisualBasic() As Task
Dim input =
<Workspace>
<Project Language="Visual Basic" CommonReferences="true">
<Document>
<![CDATA[
Class C
Property I As Integer
Sub M()
{|Simplify:Me.I|} = 1
End Sub
End Class
]]>
</Document>
</Project>
</Workspace>
Dim expected =
<text>
<![CDATA[
Class C
Property I As Integer
Sub M()
Me.I = 1
End Sub
End Class
]]>
</text>
Await TestAsync(input, expected, QualifyPropertyAccessOptionWithNotification(LanguageNames.VisualBasic, NotificationOption.Error))
End Function
<WorkItem(7955, "https://github.com/dotnet/roslyn/issues/7955")>
<Fact, Trait(Traits.Feature, Traits.Features.Simplification)>
Public Async Function UsePredefinedTypeKeywordIfTextIsTheSame() As Task
......@@ -5559,7 +5831,7 @@ End Class
End Function
Protected Function QualifyPropertyAccessOption(languageName As String) As Dictionary(Of OptionKey, Object)
Return New Dictionary(Of OptionKey, Object) From {{New OptionKey(CodeStyleOptions.QualifyPropertyAccess, languageName), New CodeStyleOption(Of Boolean)(True, NotificationOption.Error)}}
Return QualifyPropertyAccessOptionWithNotification(languageName, NotificationOption.Error)
End Function
Protected Function QualifyMethodAccessOption(languageName As String) As Dictionary(Of OptionKey, Object)
......@@ -5570,6 +5842,10 @@ End Class
Return New Dictionary(Of OptionKey, Object) From {{New OptionKey(CodeStyleOptions.QualifyEventAccess, languageName), New CodeStyleOption(Of Boolean)(True, NotificationOption.Error)}}
End Function
Protected Function QualifyPropertyAccessOptionWithNotification(languageName As String, notification As NotificationOption) As Dictionary(Of OptionKey, Object)
Return New Dictionary(Of OptionKey, Object) From {{New OptionKey(CodeStyleOptions.QualifyPropertyAccess, languageName), New CodeStyleOption(Of Boolean)(True, notification)}}
End Function
Shared DontPreferIntrinsicPredefinedTypeKeywordInDeclaration As Dictionary(Of OptionKey, Object) = New Dictionary(Of OptionKey, Object) From {{New OptionKey(SimplificationOptions.PreferIntrinsicPredefinedTypeKeywordInDeclaration, LanguageNames.VisualBasic), False}}
#End Region
......
......@@ -186,6 +186,7 @@
<Compile Include="Diagnostics\MoveToTopOfFile\MoveToTopOfFileTests.vb" />
<Compile Include="Diagnostics\OverloadBase\OverloadBaseTests.vb" />
<Compile Include="Diagnostics\QualifyMemberAccess\QualifyMemberAccessTests.vb" />
<Compile Include="Diagnostics\QualifyMemberAccess\QualifyMemberAccessTests_FixAllTests.vb" />
<Compile Include="Diagnostics\RemoveUnnecessaryCast\RemoveUnnecessaryCastTests.vb" />
<Compile Include="Diagnostics\RemoveUnnecessaryCast\RemoveUnnecessaryCastTests_FixAllTests.vb" />
<Compile Include="Diagnostics\RemoveUnnecessaryImports\RemoveUnnecessaryImportsTests.vb" />
......
......@@ -8,7 +8,7 @@ Imports Microsoft.CodeAnalysis.Options
Imports Microsoft.CodeAnalysis.VisualBasic.CodeFixes.QualifyMemberAccess
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.QualifyMemberAccess
Public Class QualifyMemberAccessTests
Partial Public Class QualifyMemberAccessTests
Inherits AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest
Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace) As Tuple(Of DiagnosticAnalyzer, CodeFixProvider)
......@@ -16,11 +16,19 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Qualif
End Function
Private Function TestAsyncWithOption(code As String, expected As String, opt As PerLanguageOption(Of CodeStyleOption(Of Boolean))) As Task
Return TestAsync(code, expected, options:=[Option](opt, True, NotificationOption.Error))
Return TestAsyncWithOptionAndNotification(code, expected, opt, NotificationOption.Error)
End Function
Private Function TestAsyncWithOptionAndNotification(code As String, expected As String, opt As PerLanguageOption(Of CodeStyleOption(Of Boolean)), notification As NotificationOption) As Task
Return TestAsync(code, expected, options:=[Option](opt, True, notification))
End Function
Private Function TestMissingAsyncWithOption(code As String, opt As PerLanguageOption(Of CodeStyleOption(Of Boolean))) As Task
Return TestMissingAsync(code, options:=[Option](opt, True, NotificationOption.Error))
Return TestMissingAsyncWithOptionAndNotification(code, opt, NotificationOption.Error)
End Function
Private Function TestMissingAsyncWithOptionAndNotification(code As String, opt As PerLanguageOption(Of CodeStyleOption(Of Boolean)), notification As NotificationOption) As Task
Return TestMissingAsync(code, options:=[Option](opt, True, notification))
End Function
<WorkItem(7065, "https://github.com/dotnet/roslyn/issues/7065")>
......@@ -423,5 +431,36 @@ End Class",
CodeStyleOptions.QualifyEventAccess)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsQualifyMemberAccess)>
Public Async Function QualifyMemberAccessNotPresentOnNotificationOptionNone() As Task
Await TestMissingAsyncWithOptionAndNotification(
"Class C : Property I As Integer : Sub M() : [|I|] = 1 : End Sub : End Class",
CodeStyleOptions.QualifyPropertyAccess, NotificationOption.None)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsQualifyMemberAccess)>
Public Async Function QualifyMemberAccessOnNotificationOptionInfo() As Task
Await TestAsyncWithOptionAndNotification(
"Class C : Property I As Integer : Sub M() : [|I|] = 1 : End Sub : End Class",
"Class C : Property I As Integer : Sub M() : Me.I = 1 : End Sub : End Class",
CodeStyleOptions.QualifyPropertyAccess, NotificationOption.Info)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsQualifyMemberAccess)>
Public Async Function QualifyMemberAccessOnNotificationOptionWarning() As Task
Await TestAsyncWithOptionAndNotification(
"Class C : Property I As Integer : Sub M() : [|I|] = 1 : End Sub : End Class",
"Class C : Property I As Integer : Sub M() : Me.I = 1 : End Sub : End Class",
CodeStyleOptions.QualifyPropertyAccess, NotificationOption.Warning)
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsQualifyMemberAccess)>
Public Async Function QualifyMemberAccessOnNotificationOptionError() As Task
Await TestAsyncWithOptionAndNotification(
"Class C : Property I As Integer : Sub M() : [|I|] = 1 : End Sub : End Class",
"Class C : Property I As Integer : Sub M() : Me.I = 1 : End Sub : End Class",
CodeStyleOptions.QualifyPropertyAccess, NotificationOption.Error)
End Function
End Class
End Namespace
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports Microsoft.CodeAnalysis.CodeStyle
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.QualifyMemberAccess
Partial Public Class QualifyMemberAccessTests
Inherits AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest
<Fact>
<Trait(Traits.Feature, Traits.Features.CodeActionsQualifyMemberAccess)>
<Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)>
Public Async Function TestFixAllInSolution_QualifyMemberAccess() As Task
Dim input =
<Workspace>
<Project Language="Visual Basic" AssemblyName="Assembly1" CommonReferences="true">
<Document><![CDATA[
Imports System
Class C
Property SomeProperty As Integer
Property OtherProperty As Integer
Sub M()
{|FixAllInSolution:SomeProperty|} = 1
Dim x = OtherProperty
End Sub
End Class]]>
</Document>
<Document><![CDATA[
Imports System
Class D
Property StringProperty As String
field As Integer
Sub N()
StringProperty = String.Empty
field = 0 ' ensure this doesn't get qualified
End Sub
End Class]]>
</Document>
</Project>
</Workspace>.ToString()
Dim expected =
<Workspace>
<Project Language="Visual Basic" AssemblyName="Assembly1" CommonReferences="true">
<Document><![CDATA[
Imports System
Class C
Property SomeProperty As Integer
Property OtherProperty As Integer
Sub M()
Me.SomeProperty = 1
Dim x = Me.OtherProperty
End Sub
End Class]]>
</Document>
<Document><![CDATA[
Imports System
Class D
Property StringProperty As String
field As Integer
Sub N()
Me.StringProperty = String.Empty
field = 0 ' ensure this doesn't get qualified
End Sub
End Class]]>
</Document>
</Project>
</Workspace>.ToString()
Await TestAsync(
initialMarkup:=input,
expectedMarkup:=expected,
options:=[Option](CodeStyleOptions.QualifyPropertyAccess, True, NotificationOption.Info),
compareTokens:=False,
fixAllActionEquivalenceKey:=FeaturesResources.AddQualification)
End Function
End Class
End Namespace
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Option Strict Off
Imports System.Threading.Tasks
Imports Microsoft.CodeAnalysis.CodeStyle
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.Diagnostics.SimplifyTypeNames
Imports Microsoft.CodeAnalysis.VisualBasic.CodeFixes.SimplifyTypeNames
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.SimplifyTypeNames
......@@ -721,5 +719,85 @@ End Class]]>
Await TestAsync(input, expected, compareTokens:=False, fixAllActionEquivalenceKey:=fixAllActionId)
End Function
<Fact>
<Trait(Traits.Feature, Traits.Features.CodeActionsQualifyMemberAccess)>
<Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)>
Public Async Function TestFixAllInSolution_RemoveMemberAccessQualification() As Task
Dim input =
<Workspace>
<Project Language="Visual Basic" AssemblyName="Assembly1" CommonReferences="true">
<Document><![CDATA[
Imports System
Class C
Property SomeProperty As Integer
Property OtherProperty As Integer
Sub M()
{|FixAllInSolution:Me.SomeProperty|} = 1
Dim x = Me.OtherProperty
End Sub
End Class]]>
</Document>
<Document><![CDATA[
Imports System
Class D
Property StringProperty As String
field As Integer
Sub N()
Me.StringProperty = String.Empty
Me.field = 0 ' ensure qualification isn't removed
End Sub
End Class]]>
</Document>
</Project>
</Workspace>.ToString()
Dim expected =
<Workspace>
<Project Language="Visual Basic" AssemblyName="Assembly1" CommonReferences="true">
<Document><![CDATA[
Imports System
Class C
Property SomeProperty As Integer
Property OtherProperty As Integer
Sub M()
SomeProperty = 1
Dim x = OtherProperty
End Sub
End Class]]>
</Document>
<Document><![CDATA[
Imports System
Class D
Property StringProperty As String
field As Integer
Sub N()
StringProperty = String.Empty
Me.field = 0 ' ensure qualification isn't removed
End Sub
End Class]]>
</Document>
</Project>
</Workspace>.ToString()
Dim options = OptionsSet(
Tuple.Create(CodeStyleOptions.QualifyPropertyAccess, False, NotificationOption.Info),
Tuple.Create(CodeStyleOptions.QualifyFieldAccess, True, NotificationOption.Info))
Await TestAsync(
initialMarkup:=input,
expectedMarkup:=expected,
options:=options,
compareTokens:=False,
fixAllActionEquivalenceKey:=VBFeaturesResources.RemoveMeQualification)
End Function
End Class
End Namespace
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Diagnostics;
......@@ -37,13 +35,18 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
}
var generator = document.GetLanguageService<SyntaxGenerator>();
var codeAction = new QualifyMemberAccessCodeAction(
var codeAction = new CodeAction.DocumentChangeAction(
FeaturesResources.AddQualification,
c => document.ReplaceNodeAsync(node, GetReplacementSyntax(node, generator), c),
FeaturesResources.AddQualification);
context.RegisterCodeFix(codeAction, context.Diagnostics);
}
public override FixAllProvider GetFixAllProvider()
{
return BatchFixAllProvider.Instance;
}
private static SyntaxNode GetReplacementSyntax(SyntaxNode node, SyntaxGenerator generator)
{
var qualifiedAccess =
......@@ -53,13 +56,5 @@ private static SyntaxNode GetReplacementSyntax(SyntaxNode node, SyntaxGenerator
.WithLeadingTrivia(node.GetLeadingTrivia());
return qualifiedAccess;
}
private class QualifyMemberAccessCodeAction : CodeAction.DocumentChangeAction
{
public QualifyMemberAccessCodeAction(string title, Func<CancellationToken, Task<Document>> createChangedDocument, string id)
: base(title, createChangedDocument, id)
{
}
}
}
}
// 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.Collections.Immutable;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Semantics;
using Microsoft.CodeAnalysis.Simplification;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Diagnostics.QualifyMemberAccess
{
internal abstract class QualifyMemberAccessDiagnosticAnalyzerBase<TLanguageKindEnum> : DiagnosticAnalyzer, IBuiltInAnalyzer where TLanguageKindEnum : struct
{
private static readonly LocalizableString s_localizableMessage = new LocalizableResourceString(nameof(WorkspacesResources.MemberAccessShouldBeQualified), WorkspacesResources.ResourceManager, typeof(WorkspacesResources));
private static readonly LocalizableString s_shouldBeQualifiedMessage = new LocalizableResourceString(nameof(WorkspacesResources.MemberAccessShouldBeQualified), WorkspacesResources.ResourceManager, typeof(WorkspacesResources));
private static readonly LocalizableString s_localizableTitleQualifyMembers = new LocalizableResourceString(nameof(FeaturesResources.AddThisOrMeQualification), FeaturesResources.ResourceManager, typeof(FeaturesResources));
private static readonly DiagnosticDescriptor s_descriptorQualifyMemberAccess = new DiagnosticDescriptor(IDEDiagnosticIds.AddQualificationDiagnosticId,
s_localizableTitleQualifyMembers,
s_localizableMessage,
private static readonly LocalizableString s_qualifyMembersTitle = new LocalizableResourceString(nameof(FeaturesResources.AddThisOrMeQualification), FeaturesResources.ResourceManager, typeof(FeaturesResources));
private static readonly DiagnosticDescriptor s_descriptorQualifyMemberAccessInfo = new DiagnosticDescriptor(IDEDiagnosticIds.AddQualificationDiagnosticId,
s_qualifyMembersTitle,
s_shouldBeQualifiedMessage,
DiagnosticCategory.Style,
DiagnosticSeverity.Info,
isEnabledByDefault: true,
customTags: DiagnosticCustomTags.Unnecessary);
private static readonly DiagnosticDescriptor s_descriptorQualifyMemberAccessWarning = new DiagnosticDescriptor(IDEDiagnosticIds.AddQualificationDiagnosticId,
s_qualifyMembersTitle,
s_shouldBeQualifiedMessage,
DiagnosticCategory.Style,
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
customTags: DiagnosticCustomTags.Unnecessary);
private static readonly DiagnosticDescriptor s_descriptorQualifyMemberAccessError = new DiagnosticDescriptor(IDEDiagnosticIds.AddQualificationDiagnosticId,
s_qualifyMembersTitle,
s_shouldBeQualifiedMessage,
DiagnosticCategory.Style,
DiagnosticSeverity.Hidden,
DiagnosticSeverity.Error,
isEnabledByDefault: true,
customTags: DiagnosticCustomTags.Unnecessary);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(s_descriptorQualifyMemberAccess);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(
s_descriptorQualifyMemberAccessInfo,
s_descriptorQualifyMemberAccessWarning,
s_descriptorQualifyMemberAccessError);
protected abstract bool IsAlreadyQualifiedMemberAccess(SyntaxNode node);
......@@ -45,27 +64,50 @@ private void AnalyzeOperation(OperationAnalysisContext context)
return;
}
// if there's already a `this.` or `Me.` qualification
if (IsAlreadyQualifiedMemberAccess(memberReference.Instance.Syntax))
// if we can't find a member then we can't do anything
if (memberReference.Member == null)
{
return;
}
// get the option
var optionSet = context.Options.GetOptionSet();
if (optionSet == null)
{
return;
}
if (memberReference.Member == null)
{
return;
}
var language = context.Operation.Syntax.Language;
if (!SimplificationHelpers.ShouldSimplifyMemberAccessExpression(memberReference.Member, language, optionSet))
var applicableOption = GetApplicableOption(memberReference.Member);
var optionValue = optionSet.GetOption(applicableOption, language);
var shouldOptionBePresent = optionValue.Value;
var isQualificationPresent = IsAlreadyQualifiedMemberAccess(memberReference.Instance.Syntax);
if (shouldOptionBePresent && !isQualificationPresent)
{
context.ReportDiagnostic(Diagnostic.Create(s_descriptorQualifyMemberAccess, context.Operation.Syntax.GetLocation()));
DiagnosticDescriptor descriptor;
switch (optionValue.Notification.Value)
{
case DiagnosticSeverity.Hidden:
descriptor = null;
break;
case DiagnosticSeverity.Info:
descriptor = s_descriptorQualifyMemberAccessInfo;
break;
case DiagnosticSeverity.Warning:
descriptor = s_descriptorQualifyMemberAccessWarning;
break;
case DiagnosticSeverity.Error:
descriptor = s_descriptorQualifyMemberAccessError;
break;
default:
throw ExceptionUtilities.Unreachable;
}
if (descriptor != null)
{
context.ReportDiagnostic(Diagnostic.Create(descriptor, context.Operation.Syntax.GetLocation()));
}
}
}
......@@ -73,5 +115,22 @@ public DiagnosticAnalyzerCategory GetAnalyzerCategory()
{
return DiagnosticAnalyzerCategory.SemanticSpanAnalysis;
}
private PerLanguageOption<CodeStyleOption<bool>> GetApplicableOption(ISymbol symbol)
{
switch (symbol.Kind)
{
case SymbolKind.Field:
return CodeStyleOptions.QualifyFieldAccess;
case SymbolKind.Property:
return CodeStyleOptions.QualifyPropertyAccess;
case SymbolKind.Method:
return CodeStyleOptions.QualifyMethodAccess;
case SymbolKind.Event:
return CodeStyleOptions.QualifyEventAccess;
default:
throw ExceptionUtilities.Unreachable;
}
}
}
}
......@@ -103,17 +103,13 @@ internal static bool IsValidSymbolInfo(ISymbol symbol)
internal static bool ShouldSimplifyMemberAccessExpression(SemanticModel semanticModel, SyntaxNode expression, OptionSet optionSet)
{
var nameSymbol = GetOriginalSymbolInfo(semanticModel, expression);
return nameSymbol != null && ShouldSimplifyMemberAccessExpression(nameSymbol, semanticModel.Language, optionSet);
}
internal static bool ShouldSimplifyMemberAccessExpression(ISymbol symbol, string languageName, OptionSet optionSet)
{
if (!symbol.IsStatic &&
(symbol.IsKind(SymbolKind.Field) && optionSet.GetOption(CodeStyleOptions.QualifyFieldAccess, languageName).Value ||
(symbol.IsKind(SymbolKind.Property) && optionSet.GetOption(CodeStyleOptions.QualifyPropertyAccess, languageName).Value) ||
(symbol.IsKind(SymbolKind.Method) && optionSet.GetOption(CodeStyleOptions.QualifyMethodAccess, languageName).Value) ||
(symbol.IsKind(SymbolKind.Event) && optionSet.GetOption(CodeStyleOptions.QualifyEventAccess, languageName).Value)))
var symbol = GetOriginalSymbolInfo(semanticModel, expression);
if (symbol == null ||
(!symbol.IsStatic &&
(symbol.IsKind(SymbolKind.Field) && optionSet.GetOption(CodeStyleOptions.QualifyFieldAccess, semanticModel.Language).Value ||
(symbol.IsKind(SymbolKind.Property) && optionSet.GetOption(CodeStyleOptions.QualifyPropertyAccess, semanticModel.Language).Value) ||
(symbol.IsKind(SymbolKind.Method) && optionSet.GetOption(CodeStyleOptions.QualifyMethodAccess, semanticModel.Language).Value) ||
(symbol.IsKind(SymbolKind.Event) && optionSet.GetOption(CodeStyleOptions.QualifyEventAccess, semanticModel.Language).Value))))
{
return false;
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册