提交 f44441e5 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #20448 from CyrusNajmabadi/removeUnusedFunction

	Add support for removing unused local functions.
......@@ -2,7 +2,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
......@@ -63,4 +62,4 @@ public override SourceText GetText(CancellationToken cancellationToken = default
/// </summary>
internal IList<DiagnosticInfo> Diagnostics => _diagnostics;
}
}
}
\ No newline at end of file
......@@ -252,7 +252,8 @@
<Compile Include="Diagnostics\NamingStyles\NamingStylesTests_OptionSets.cs" />
<Compile Include="Diagnostics\PreferFrameworkType\PreferFrameworkTypeTests.cs" />
<Compile Include="Diagnostics\PreferFrameworkType\PreferFrameworkTypeTests_FixAllTests.cs" />
<Compile Include="Diagnostics\RemoveUnusedVariable\RemoveUnusedVariableTest.cs" />
<Compile Include="RemoveUnusedLocalFunction\RemoveUnusedLocalFunctionTest.cs" />
<Compile Include="RemoveUnusedVariable\RemoveUnusedVariableTest.cs" />
<Compile Include="Extensions\ContextQuery\IsPossibleDeconstructionDesignationTests.cs" />
<Compile Include="Extensions\ContextQuery\PossibleTupleContextTests.cs" />
<Compile Include="GenerateFromMembers\GenerateEqualsAndGetHashCodeFromMembers\GenerateEqualsAndGetHashCodeFromMembersTests.cs" />
......
// 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.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.RemoveUnusedLocalFunction;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.RemoveUnusedLocalFunction
{
public partial class RemoveUnusedVariableTest : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
{
internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace)
=> (null, new CSharpRemoveUnusedLocalFunctionCodeFixProvider());
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnusedVariable)]
public async Task RemoveUnusedLocalFunction()
{
await TestInRegularAndScriptAsync(
@"class Class
{
void Method()
{
void [|Foo|]() { }
}
}",
@"class Class
{
void Method()
{
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnusedVariable)]
public async Task RemoveUnusedLocalFunctionFixAll1()
{
await TestInRegularAndScriptAsync(
@"class Class
{
void Method()
{
void {|FixAllInDocument:F|}() { }
void G() { }
}
}",
@"class Class
{
void Method()
{
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnusedVariable)]
public async Task RemoveUnusedLocalFunctionFixAll2()
{
await TestInRegularAndScriptAsync(
@"class Class
{
void Method()
{
void G() { }
void {|FixAllInDocument:F|}() { }
}
}",
@"class Class
{
void Method()
{
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnusedVariable)]
public async Task RemoveUnusedLocalFunctionFixAll3()
{
await TestInRegularAndScriptAsync(
@"class Class
{
void Method()
{
void {|FixAllInDocument:F|}() { void G() { } }
}
}",
@"class Class
{
void Method()
{
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnusedVariable)]
public async Task RemoveUnusedLocalFunctionFixAll4()
{
await TestInRegularAndScriptAsync(
@"class Class
{
void Method()
{
void G() { void {|FixAllInDocument:F|}() { } }
}
}",
@"class Class
{
void Method()
{
}
}");
}
}
}
\ No newline at end of file
// 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.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.CodeFixes.RemoveUnusedVariable;
using Microsoft.CodeAnalysis.CSharp.RemoveUnusedVariable;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.RemoveUnusedVar
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.RemoveUnusedVariable
{
public partial class RemoveUnusedVariableTest : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
{
......@@ -172,7 +172,38 @@ void Method()
void Method()
{
}
}");
}
[WorkItem(20466, "https://github.com/dotnet/roslyn/issues/20466")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnusedVariable)]
public async Task RemoveUnusedCatchVariable()
{
await TestInRegularAndScriptAsync(
@"class Class
{
void Method()
{
try
{
}
catch (System.Exception [|e|])
{
}
}
}",
@"class Class
{
void Method()
{
try
{
}
catch (System.Exception)
{
}
}
}");
}
}
}
}
\ No newline at end of file
......@@ -88,6 +88,7 @@ public static class Features
public const string CodeActionsRemoveByVal = "CodeActions.RemoveByVal";
public const string CodeActionsRemoveDocCommentNode = "CodeActions.RemoveDocCommentNode";
public const string CodeActionsRemoveUnnecessaryCast = "CodeActions.RemoveUnnecessaryCast";
public const string CodeActionsRemoveUnusedLocalFunction = "CodeActions.RemoveUnusedLocalFunction";
public const string CodeActionsRemoveUnusedVariable = "CodeActions.RemoveUnusedVariable";
public const string CodeActionsRemoveUnnecessaryImports = "CodeActions.RemoveUnnecessaryImports";
public const string CodeActionsRemoveUnreachableCode = "CodeActions.RemoveUnreachableCode";
......
......@@ -195,7 +195,7 @@
<Compile Include="CodeActions\ConvertIfToSwitch\ConvertIfToSwitchTests.vb" />
<Compile Include="CodeActions\ConvertNumericLiteral\ConvertNumericLiteralTests.vb" />
<Compile Include="CodeActions\UseNamedArguments\UseNamedArgumentsTests.vb" />
<Compile Include="Diagnostics\RemoveUnusedVariable\RemoveUnusedVariableTest.vb" />
<Compile Include="RemoveUnusedVariable\RemoveUnusedVariableTest.vb" />
<Compile Include="InitializeParameter\AddParameterCheckTests.vb" />
<Compile Include="OrderModifiers\OrderModifiersTests.vb" />
<Compile Include="Structure\CollectionInitializerStructureProviderTests.vb" />
......
......@@ -2,9 +2,10 @@
Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.VisualBasic.CodeFixes.RemoveUnusedVariable
Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics
Imports Microsoft.CodeAnalysis.VisualBasic.RemoveUnusedVariable
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.RemoveUnusedVariableTest
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.RemoveUnusedVariable
Partial Public Class RemoveUnusedVariableTest
Inherits AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest
......
......@@ -61,7 +61,8 @@
<Compile Include="AddImport\CSharpAddImportFeatureService.cs" />
<Compile Include="AddPackage\CSharpAddSpecificPackageCodeFixProvider.cs" />
<Compile Include="AddParameter\CSharpAddParameterCodeFixProvider.cs" />
<Compile Include="CodeFixes\RemoveUnusedVariable\CSharpRemoveUnusedVariableCodeFixProvider.cs" />
<Compile Include="RemoveUnusedLocalFunction\CSharpRemoveUnusedLocalFunctionCodeFixProvider.cs" />
<Compile Include="RemoveUnusedVariable\CSharpRemoveUnusedVariableCodeFixProvider.cs" />
<Compile Include="CommentSelection\CSharpCommentSelectionService.cs" />
<Compile Include="Completion\CompletionProviders\DeclarationNameCompletionProvider.NameGenerator.cs" />
<Compile Include="Completion\CompletionProviders\DeclarationNameCompletionProvider.DeclarationInfo.cs" />
......
......@@ -845,6 +845,15 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Remove unused function.
/// </summary>
internal static string Remove_unused_function {
get {
return ResourceManager.GetString("Remove_unused_function", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Replace return with yield return.
/// </summary>
......
......@@ -524,4 +524,7 @@
<data name="Suggested_name" xml:space="preserve">
<value>(Suggested name)</value>
</data>
<data name="Remove_unused_function" xml:space="preserve">
<value>Remove unused function</value>
</data>
</root>
\ No newline at end of file
// 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.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.RemoveUnusedLocalFunction
{
[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.RemoveUnusedLocalFunction), Shared]
[ExtensionOrder(After = PredefinedCodeFixProviderNames.AddImport)]
internal class CSharpRemoveUnusedLocalFunctionCodeFixProvider : SyntaxEditorBasedCodeFixProvider
{
private const string CS8321 = nameof(CS8321); // The local function 'X' is declared but never used
public sealed override ImmutableArray<string> FixableDiagnosticIds
=> ImmutableArray.Create(CS8321);
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
context.RegisterCodeFix(
new MyCodeAction(c => FixAsync(context.Document, context.Diagnostics.First(), c)),
context.Diagnostics);
return SpecializedTasks.EmptyTask;
}
protected override Task FixAllAsync(Document document, ImmutableArray<Diagnostic> diagnostics, SyntaxEditor editor, CancellationToken cancellationToken)
{
var root = editor.OriginalRoot;
// Order diagnostics in reverse (from latest in file to earliest) so that we process
// all inner local functions before processing outer local functions. If we don't
// do this, then SyntaxEditor will fail if it tries to remove an inner local function
// after already removing the outer one.
var localFunctions = diagnostics.OrderBy((d1, d2) => d2.Location.SourceSpan.Start - d1.Location.SourceSpan.Start)
.Select(d => root.FindToken(d.Location.SourceSpan.Start))
.Select(t => t.GetAncestor<LocalFunctionStatementSyntax>());
foreach (var localFunction in localFunctions)
{
editor.RemoveNode(localFunction);
}
return SpecializedTasks.EmptyTask;
}
private class MyCodeAction : CodeAction.DocumentChangeAction
{
public MyCodeAction(Func<CancellationToken, Task<Document>> createChangedDocument) :
base(CSharpFeaturesResources.Remove_unused_function, createChangedDocument, CSharpFeaturesResources.Remove_unused_function)
{
}
}
}
}
\ No newline at end of file
......@@ -3,10 +3,10 @@
using System.Collections.Immutable;
using System.Composition;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeFixes.RemoveUnusedVariable;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.RemoveUnusedVariable;
namespace Microsoft.CodeAnalysis.CSharp.CodeFixes.RemoveUnusedVariable
namespace Microsoft.CodeAnalysis.CSharp.RemoveUnusedVariable
{
[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.RemoveUnusedVariable), Shared]
[ExtensionOrder(After = PredefinedCodeFixProviderNames.AddImport)]
......@@ -15,6 +15,10 @@ internal partial class CSharpRemoveUnusedVariableCodeFixProvider : AbstractRemov
private const string CS0168 = nameof(CS0168);
private const string CS0219 = nameof(CS0219);
public sealed override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(CS0168, CS0219);
public sealed override ImmutableArray<string> FixableDiagnosticIds
=> ImmutableArray.Create(CS0168, CS0219);
protected override bool IsCatchDeclarationIdentifier(SyntaxToken token)
=> token.Parent is CatchDeclarationSyntax catchDeclaration && catchDeclaration.Identifier == token;
}
}
}
\ No newline at end of file
......@@ -39,6 +39,7 @@ internal static class PredefinedCodeFixProviderNames
public const string RemoveUnnecessaryCast = nameof(RemoveUnnecessaryCast);
public const string RemoveUnnecessaryImports = nameof(RemoveUnnecessaryImports);
public const string RemoveUnreachableCode = nameof(RemoveUnreachableCode);
public const string RemoveUnusedLocalFunction = nameof(RemoveUnusedLocalFunction);
public const string RemoveUnusedVariable = nameof(RemoveUnusedVariable);
public const string RenameTracking = nameof(RenameTracking);
public const string SimplifyNames = nameof(SimplifyNames);
......
......@@ -119,7 +119,7 @@
<Compile Include="AddPackage\InstallWithPackageManagerCodeAction.cs" />
<Compile Include="AddPackage\InstallPackageParentCodeAction.cs" />
<Compile Include="AddParameter\AbstractAddParameterCodeFixProvider.cs" />
<Compile Include="CodeFixes\RemoveUnusedVariable\AbstractRemoveUnusedVariableCodeFixProvider.cs" />
<Compile Include="RemoveUnusedVariable\AbstractRemoveUnusedVariableCodeFixProvider.cs" />
<Compile Include="CodeRefactorings\WorkspaceServices\ISymbolRenamedCodeActionOperationFactoryWorkspaceService.cs" />
<Compile Include="CommentSelection\AbstractCommentSelectionService.cs" />
<Compile Include="CommentSelection\CommentSelectionInfo.cs" />
......
......@@ -6,28 +6,35 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CodeFixes.RemoveUnusedVariable
namespace Microsoft.CodeAnalysis.RemoveUnusedVariable
{
internal abstract class AbstractRemoveUnusedVariableCodeFixProvider<TLocalDeclarationStatement, TVariableDeclarator, TVariableDeclaration> : SyntaxEditorBasedCodeFixProvider
where TLocalDeclarationStatement: SyntaxNode
where TVariableDeclarator: SyntaxNode
where TVariableDeclaration: SyntaxNode
where TLocalDeclarationStatement : SyntaxNode
where TVariableDeclarator : SyntaxNode
where TVariableDeclaration : SyntaxNode
{
protected abstract bool IsCatchDeclarationIdentifier(SyntaxToken token);
public async override Task RegisterCodeFixesAsync(CodeFixContext context)
{
foreach (var diagnostic in context.Diagnostics)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var token = root.FindToken(diagnostic.Location.SourceSpan.Start);
var ancestor = token.GetAncestor<TLocalDeclarationStatement>();
if (ancestor == null)
if (!IsCatchDeclarationIdentifier(token))
{
return;
var ancestor = token.GetAncestor<TLocalDeclarationStatement>();
if (ancestor == null)
{
return;
}
}
}
......@@ -41,20 +48,30 @@ protected override Task FixAllAsync(Document document, ImmutableArray<Diagnostic
var root = editor.OriginalRoot;
foreach (var diagnostic in diagnostics)
{
var token = root.FindToken(diagnostic.Location.SourceSpan.Start);
var variableDeclarator = token.GetAncestor<TVariableDeclarator>();
var variableDeclarators = token.GetAncestor<TVariableDeclaration>().ChildNodes().Where(x => x is TVariableDeclarator);
if (variableDeclarators.Count() == 1)
var token = diagnostic.Location.FindToken(cancellationToken);
if (IsCatchDeclarationIdentifier(token))
{
editor.RemoveNode(token.GetAncestor<TLocalDeclarationStatement>());
editor.ReplaceNode(
token.Parent,
token.Parent.ReplaceToken(token, default(SyntaxToken)).WithAdditionalAnnotations(Formatter.Annotation));
}
else if (variableDeclarators.Count() > 1)
else
{
editor.RemoveNode(variableDeclarator);
var variableDeclarator = token.GetAncestor<TVariableDeclarator>();
var variableDeclarators = token.GetAncestor<TVariableDeclaration>().ChildNodes().Where(x => x is TVariableDeclarator);
if (variableDeclarators.Count() == 1)
{
editor.RemoveNode(token.GetAncestor<TLocalDeclarationStatement>());
}
else if (variableDeclarators.Count() > 1)
{
editor.RemoveNode(variableDeclarator);
}
}
}
return SpecializedTasks.EmptyTask;
}
......@@ -66,4 +83,4 @@ private class MyCodeAction : CodeAction.DocumentChangeAction
}
}
}
}
}
\ No newline at end of file
......@@ -92,7 +92,7 @@
<Compile Include="CodeFixes\GenerateParameterizedMember\GenerateParameterizedMemberCodeFixProvider.vb" />
<Compile Include="CodeFixes\GenerateType\GenerateTypeCodeFixProvider.vb" />
<Compile Include="GenerateVariable\VisualBasicGenerateVariableCodeFixProvider.vb" />
<Compile Include="CodeFixes\RemoveUnusedVariable\VisualBasicRemoveUnusedVariableCodeFixProvider.vb" />
<Compile Include="RemoveUnusedVariable\VisualBasicRemoveUnusedVariableCodeFixProvider.vb" />
<Compile Include="ConvertIfToSwitch\VisualBasicConvertIfToSwitchCodeRefactoringProvider.Pattern.vb" />
<Compile Include="ConvertIfToSwitch\VisualBasicConvertIfToSwitchCodeRefactoringProvider.vb" />
<Compile Include="ConvertNumericLiteral\VisualBasicConvertNumericLiteralCodeRefactoringProvider.vb" />
......
......@@ -3,24 +3,24 @@
Imports System.Collections.Immutable
Imports System.Composition
Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.CodeFixes.RemoveUnusedVariable
Imports Microsoft.CodeAnalysis.RemoveUnusedVariable
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.RemoveUnusedVariable
<ExportCodeFixProviderAttribute(LanguageNames.VisualBasic, Name:=PredefinedCodeFixProviderNames.RemoveUnusedVariable), [Shared]>
Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnusedVariable
<ExportCodeFixProvider(LanguageNames.VisualBasic, Name:=PredefinedCodeFixProviderNames.RemoveUnusedVariable), [Shared]>
<ExtensionOrder(After:=PredefinedCodeFixProviderNames.AddImport)>
Friend Class VisualBasicRemoveUnusedVariableCodeFixProvider
Inherits AbstractRemoveUnusedVariableCodeFixProvider(Of
LocalDeclarationStatementSyntax, ModifiedIdentifierSyntax, VariableDeclaratorSyntax)
Private Const BC42024 As String = "BC42024"
Private ReadOnly Ids As ImmutableArray(Of String) = ImmutableArray.Create(BC42024)
Private Const BC42024 As String = NameOf(BC42024)
Public Overrides ReadOnly Property FixableDiagnosticIds As ImmutableArray(Of String) =
ImmutableArray.Create(BC42024)
Public Overrides ReadOnly Property FixableDiagnosticIds As ImmutableArray(Of String)
Get
Return Ids
End Get
End Property
Protected Overrides Function IsCatchDeclarationIdentifier(token As SyntaxToken) As Boolean
' VB does not support catch declarations without an identifier in them
Return False
End Function
End Class
End Namespace
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册