Skip to content

Commit fbdd94c

Browse files
Add an option to control if unused members shoudl be faded out. (#76520)
2 parents 9a27ef7 + 2c922c6 commit fbdd94c

29 files changed

+152
-50
lines changed

src/Analyzers/Core/Analyzers/RemoveUnusedMembers/AbstractRemoveUnusedMembersDiagnosticAnalyzer.cs

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
using System.Linq;
1111
using System.Runtime.CompilerServices;
1212
using System.Threading;
13-
using Microsoft.CodeAnalysis.CodeQuality;
1413
using Microsoft.CodeAnalysis.CodeStyle;
1514
using Microsoft.CodeAnalysis.Diagnostics;
1615
using Microsoft.CodeAnalysis.LanguageService;
@@ -27,10 +26,9 @@ internal abstract class AbstractRemoveUnusedMembersDiagnosticAnalyzer<
2726
TIdentifierNameSyntax,
2827
TTypeDeclarationSyntax,
2928
TMemberDeclarationSyntax>()
30-
: AbstractCodeQualityDiagnosticAnalyzer(
29+
: AbstractBuiltInUnnecessaryCodeStyleDiagnosticAnalyzer(
3130
[s_removeUnusedMembersRule, s_removeUnreadMembersRule],
32-
// We want to analyze references in generated code, but not report unused members in generated code.
33-
GeneratedCodeAnalysisFlags.Analyze)
31+
FadingOptions.FadeOutUnusedMembers)
3432
where TDocumentationCommentTriviaSyntax : SyntaxNode
3533
where TIdentifierNameSyntax : SyntaxNode
3634
where TTypeDeclarationSyntax : TMemberDeclarationSyntax
@@ -44,35 +42,43 @@ internal abstract class AbstractRemoveUnusedMembersDiagnosticAnalyzer<
4442
memberOptions: SymbolDisplayMemberOptions.IncludeContainingType);
4543

4644
// IDE0051: "Remove unused members" (Symbol is declared but never referenced)
47-
private static readonly DiagnosticDescriptor s_removeUnusedMembersRule = CreateDescriptor(
45+
private static readonly DiagnosticDescriptor s_removeUnusedMembersRule = CreateDescriptorWithId(
4846
IDEDiagnosticIds.RemoveUnusedMembersDiagnosticId,
4947
EnforceOnBuildValues.RemoveUnusedMembers,
48+
hasAnyCodeStyleOption: false,
5049
new LocalizableResourceString(nameof(AnalyzersResources.Remove_unused_private_members), AnalyzersResources.ResourceManager, typeof(AnalyzersResources)),
5150
new LocalizableResourceString(nameof(AnalyzersResources.Private_member_0_is_unused), AnalyzersResources.ResourceManager, typeof(AnalyzersResources)),
52-
hasAnyCodeStyleOption: false, isUnnecessary: true);
51+
isUnnecessary: true);
5352

5453
// IDE0052: "Remove unread members" (Value is written and/or symbol is referenced, but the assigned value is never read)
5554
// Internal for testing
56-
internal static readonly DiagnosticDescriptor s_removeUnreadMembersRule = CreateDescriptor(
55+
internal static readonly DiagnosticDescriptor s_removeUnreadMembersRule = CreateDescriptorWithId(
5756
IDEDiagnosticIds.RemoveUnreadMembersDiagnosticId,
5857
EnforceOnBuildValues.RemoveUnreadMembers,
58+
hasAnyCodeStyleOption: false,
5959
new LocalizableResourceString(nameof(AnalyzersResources.Remove_unread_private_members), AnalyzersResources.ResourceManager, typeof(AnalyzersResources)),
6060
new LocalizableResourceString(nameof(AnalyzersResources.Private_member_0_can_be_removed_as_the_value_assigned_to_it_is_never_read), AnalyzersResources.ResourceManager, typeof(AnalyzersResources)),
61-
hasAnyCodeStyleOption: false, isUnnecessary: true);
61+
isUnnecessary: true);
6262

6363
protected abstract ISemanticFacts SemanticFacts { get; }
6464

6565
protected abstract IEnumerable<TTypeDeclarationSyntax> GetTypeDeclarations(INamedTypeSymbol namedType, CancellationToken cancellationToken);
6666
protected abstract SyntaxList<TMemberDeclarationSyntax> GetMembers(TTypeDeclarationSyntax typeDeclaration);
6767
protected abstract SyntaxNode GetParentIfSoleDeclarator(SyntaxNode declaration);
6868

69-
// We need to analyze the whole document even for edits within a method body,
70-
// because we might add or remove references to members in executable code.
71-
// For example, if we had an unused field with no references, then editing any single method body
72-
// to reference this field should clear the unused field diagnostic.
73-
// Hence, we need to re-analyze the declarations in the whole file for any edits within the document.
69+
/// <summary>
70+
/// We need to analyze the whole document even for edits within a method body, because we might add or remove
71+
/// references to members in executable code. For example, if we had an unused field with no references, then
72+
/// editing any single method body to reference this field should clear the unused field diagnostic. Hence, we need
73+
/// to re-analyze the declarations in the whole file for any edits within the document.
74+
/// </summary>
7475
public override DiagnosticAnalyzerCategory GetAnalyzerCategory() => DiagnosticAnalyzerCategory.SemanticDocumentAnalysis;
7576

77+
/// <summary>
78+
/// We want to analyze references in generated code, but not report unused members in generated code.
79+
/// </summary>
80+
protected override GeneratedCodeAnalysisFlags GeneratedCodeAnalysisFlags => GeneratedCodeAnalysisFlags.Analyze;
81+
7682
protected sealed override void InitializeWorker(AnalysisContext context)
7783
=> context.RegisterCompilationStartAction(compilationStartContext
7884
=> CompilationAnalyzer.CreateAndRegisterActions(compilationStartContext, this));

src/Analyzers/VisualBasic/Tests/RemoveUnusedMembers/RemoveUnusedMembersTests.vb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1621,7 +1621,7 @@ parseOptions:=Nothing,
16211621
compilationOptions:=Nothing,
16221622
options:=Nothing,
16231623
"IDE0051",
1624-
DiagnosticSeverity.Info,
1624+
DiagnosticSeverity.Hidden,
16251625
diagnosticMessage:=String.Format(AnalyzersResources.Private_member_0_is_unused, "C.New"))
16261626
End Function
16271627
End Class

src/EditorFeatures/CSharpTest/QuickInfo/DiagnosticAnalyzerQuickInfoSourceTests.cs

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.QuickInfo;
2525

2626
[UseExportProvider]
2727
[Trait(Traits.Feature, Traits.Features.QuickInfo)]
28-
public class DiagnosticAnalyzerQuickInfoSourceTests
28+
public sealed class DiagnosticAnalyzerQuickInfoSourceTests
2929
{
3030
[WpfFact, WorkItem("https://github.com/dotnet/roslyn/issues/46604")]
3131
public async Task ErrorTitleIsShownOnDisablePragma()
@@ -156,26 +156,26 @@ public async Task QuickInfoSuppressMessageAttributeUseCases(string suppressMessa
156156
? GetFormattedIDEAnalyzerTitle(51, nameof(AnalyzersResources.Remove_unused_private_members))
157157
: null;
158158
await TestAsync(
159-
@$"
160-
using System.Diagnostics.CodeAnalysis;
161-
using SM = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute;
162-
namespace T
163-
{{
164-
public static class DiagnosticIds
165-
{{
166-
public const string IDE0051 = ""IDE0051"";
167-
}}
159+
$$"""
160+
using System.Diagnostics.CodeAnalysis;
161+
using SM = System.Diagnostics.CodeAnalysis.SuppressMessageAttribute;
162+
namespace T
163+
{
164+
public static class DiagnosticIds
165+
{
166+
public const string IDE0051 = "IDE0051";
167+
}
168168
169-
{suppressMessageAttribute}
170-
public class C
171-
{{
172-
private int _i;
173-
}}
174-
}}
175-
", description, ImmutableArray<TextSpan>.Empty);
169+
{{suppressMessageAttribute}}
170+
public class C
171+
{
172+
private int _i;
173+
}
174+
}
175+
""", description, ImmutableArray<TextSpan>.Empty);
176176
}
177177

178-
protected static async Task AssertContentIsAsync(EditorTestWorkspace workspace, Document document, int position, string expectedDescription,
178+
private static async Task AssertContentIsAsync(EditorTestWorkspace workspace, Document document, int position, string expectedDescription,
179179
ImmutableArray<TextSpan> relatedSpans)
180180
{
181181
var info = await GetQuickinfo(workspace, document, position);
@@ -194,13 +194,13 @@ private static async Task<QuickInfoItem> GetQuickinfo(EditorTestWorkspace worksp
194194
return info;
195195
}
196196

197-
protected static async Task AssertNoContentAsync(EditorTestWorkspace workspace, Document document, int position)
197+
private static async Task AssertNoContentAsync(EditorTestWorkspace workspace, Document document, int position)
198198
{
199199
var info = await GetQuickinfo(workspace, document, position);
200200
Assert.Null(info);
201201
}
202202

203-
protected static async Task TestAsync(
203+
private static async Task TestAsync(
204204
string code,
205205
string expectedDescription,
206206
ImmutableArray<TextSpan> relatedSpans,
@@ -237,7 +237,7 @@ private static string GetFormattedIDEAnalyzerTitle(int ideDiagnosticId, string n
237237
return $"IDE{ideDiagnosticId:0000}: {localizable}";
238238
}
239239

240-
protected static Task TestInClassAsync(string code, string expectedDescription, params TextSpan[] relatedSpans)
240+
private static Task TestInClassAsync(string code, string expectedDescription, params TextSpan[] relatedSpans)
241241
=> TestAsync(
242242
$$"""
243243
class C
@@ -246,7 +246,7 @@ class C
246246
}
247247
""", expectedDescription, relatedSpans.ToImmutableArray());
248248

249-
protected static Task TestInMethodAsync(string code, string expectedDescription, params TextSpan[] relatedSpans)
249+
private static Task TestInMethodAsync(string code, string expectedDescription, params TextSpan[] relatedSpans)
250250
=> TestInClassAsync(
251251
$$"""
252252
void M()

src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,8 @@
194194
<StackPanel>
195195
<CheckBox x:Name="Fade_out_unused_usings"
196196
Content="{x:Static local:AdvancedOptionPageStrings.Option_Fade_out_unused_usings}" />
197+
<CheckBox x:Name="Fade_out_unused_members"
198+
Content="{x:Static local:AdvancedOptionPageStrings.Option_Fade_out_unused_members}" />
197199
<CheckBox x:Name="Fade_out_unreachable_code"
198200
Content="{x:Static local:AdvancedOptionPageStrings.Option_Fade_out_unreachable_code}" />
199201
</StackPanel>

src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ public AdvancedOptionPageControl(OptionStore optionStore) : base(optionStore)
129129

130130
// Fading
131131
BindToOption(Fade_out_unused_usings, FadingOptions.FadeOutUnusedImports, LanguageNames.CSharp);
132+
BindToOption(Fade_out_unused_members, FadingOptions.FadeOutUnusedMembers, LanguageNames.CSharp);
132133
BindToOption(Fade_out_unreachable_code, FadingOptions.FadeOutUnreachableCode, LanguageNames.CSharp);
133134

134135
// Block Structure Guides

src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageStrings.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,9 @@ public static string Option_Fading
268268
public static string Option_Fade_out_unused_usings
269269
=> CSharpVSResources.Fade_out_unused_usings;
270270

271+
public static string Option_Fade_out_unused_members
272+
=> ServicesVSResources.Fade_out_unused_members;
273+
271274
public static string Option_Fade_out_unreachable_code
272275
=> ServicesVSResources.Fade_out_unreachable_code;
273276

src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.Fading.cs

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,25 @@
44

55
using Microsoft.CodeAnalysis.CodeStyle;
66

7-
namespace Microsoft.VisualStudio.LanguageServices.CSharp.Options
7+
namespace Microsoft.VisualStudio.LanguageServices.CSharp.Options;
8+
9+
public partial class AutomationObject
810
{
9-
public partial class AutomationObject
11+
public int Fading_FadeOutUnreachableCode
12+
{
13+
get { return GetBooleanOption(FadingOptions.FadeOutUnreachableCode); }
14+
set { SetBooleanOption(FadingOptions.FadeOutUnreachableCode, value); }
15+
}
16+
17+
public int Fading_FadeOutUnusedImports
1018
{
11-
public int Fading_FadeOutUnreachableCode
12-
{
13-
get { return GetBooleanOption(FadingOptions.FadeOutUnreachableCode); }
14-
set { SetBooleanOption(FadingOptions.FadeOutUnreachableCode, value); }
15-
}
19+
get { return GetBooleanOption(FadingOptions.FadeOutUnusedImports); }
20+
set { SetBooleanOption(FadingOptions.FadeOutUnusedImports, value); }
21+
}
1622

17-
public int Fading_FadeOutUnusedImports
18-
{
19-
get { return GetBooleanOption(FadingOptions.FadeOutUnusedImports); }
20-
set { SetBooleanOption(FadingOptions.FadeOutUnusedImports, value); }
21-
}
23+
public int Fading_FadeOutUnusedMembers
24+
{
25+
get { return GetBooleanOption(FadingOptions.FadeOutUnusedMembers); }
26+
set { SetBooleanOption(FadingOptions.FadeOutUnusedMembers, value); }
2227
}
2328
}

src/VisualStudio/CSharp/Impl/Options/AutomationObject/AutomationObject.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
namespace Microsoft.VisualStudio.LanguageServices.CSharp.Options
1212
{
1313
[ComVisible(true)]
14-
public partial class AutomationObject : AbstractAutomationObject
14+
public sealed partial class AutomationObject : AbstractAutomationObject
1515
{
1616
internal AutomationObject(ILegacyGlobalOptionService legacyGlobalOptions)
1717
: base(legacyGlobalOptions, LanguageNames.CSharp)

src/VisualStudio/Core/Def/Options/VisualStudioOptionStorage.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ public bool TryFetch(LocalUserRegistryOptionPersister persister, OptionKey2 opti
280280
{"dotnet_allow_best_effort_when_extracting_method", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.Allow Best Effort")},
281281
{"dotnet_fade_out_unreachable_code", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.FadeOutUnreachableCode")},
282282
{"dotnet_fade_out_unused_imports", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.FadeOutUnusedImports")},
283+
{"dotnet_fade_out_unused_members", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.FadeOutUnusedMembers")},
283284
{"dotnet_add_imports_on_paste", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.AddImportsOnPaste2")},
284285
{"dotnet_always_use_default_symbol_servers", new RoamingProfileStorage("TextEditor.AlwaysUseDefaultSymbolServers")},
285286
{"csharp_insert_block_comment_start_string", new RoamingProfileStorage("TextEditor.%LANGUAGE%.Specific.Auto Insert Block Comment Start String")},

src/VisualStudio/Core/Def/ServicesVSResources.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,9 @@ Additional information: {1}</value>
763763
<data name="Analysis" xml:space="preserve">
764764
<value>Analysis</value>
765765
</data>
766+
<data name="Fade_out_unused_members" xml:space="preserve">
767+
<value>Fade out unused members</value>
768+
</data>
766769
<data name="Fade_out_unreachable_code" xml:space="preserve">
767770
<value>Fade out unreachable code</value>
768771
</data>

0 commit comments

Comments
 (0)