未验证 提交 ba9ee62a 编写于 作者: S Sam Harwell 提交者: GitHub

Merge pull request #26224 from jcouv/implicit-type

Add refactoring for UseImplicitType
......@@ -13,7 +13,7 @@
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings.UseExplicitType
{
[Trait(Traits.Feature, Traits.Features.CodeActionsUseExplicitType)]
public class UseExplicitTypeTests : AbstractCSharpCodeActionTest
public class UseExplicitTypeRefactoringTests : AbstractCSharpCodeActionTest
{
protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace, TestParameters parameters)
=> new UseExplicitTypeCodeRefactoringProvider();
......@@ -38,7 +38,6 @@ protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspa
SingleOption(CSharpCodeStyleOptions.UseImplicitTypeWhereApparent, onWithNone),
SingleOption(CSharpCodeStyleOptions.UseImplicitTypeForIntrinsicTypes, onWithNone));
[Fact]
public async Task TestIntLocalDeclaration()
{
......
// 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.Generic;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.CodeRefactorings.UseImplicitType;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings.UseExplicitType
{
[Trait(Traits.Feature, Traits.Features.CodeActionsUseImplicitType)]
public class UseImplicitTypeRefactoringTests : AbstractCSharpCodeActionTest
{
protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace, TestParameters parameters)
=> new UseImplicitTypeCodeRefactoringProvider();
private readonly CodeStyleOption<bool> onWithNone = new CodeStyleOption<bool>(true, NotificationOption.None);
private readonly CodeStyleOption<bool> offWithNone = new CodeStyleOption<bool>(false, NotificationOption.None);
private readonly CodeStyleOption<bool> onWithInfo = new CodeStyleOption<bool>(true, NotificationOption.Suggestion);
private readonly CodeStyleOption<bool> offWithInfo = new CodeStyleOption<bool>(false, NotificationOption.Suggestion);
private IDictionary<OptionKey, object> PreferExplicitTypeWithInfo() => OptionsSet(
SingleOption(CSharpCodeStyleOptions.UseImplicitTypeWherePossible, offWithInfo),
SingleOption(CSharpCodeStyleOptions.UseImplicitTypeWhereApparent, offWithInfo),
SingleOption(CSharpCodeStyleOptions.UseImplicitTypeForIntrinsicTypes, offWithInfo));
private IDictionary<OptionKey, object> PreferImplicitTypeWithInfo() => OptionsSet(
SingleOption(CSharpCodeStyleOptions.UseImplicitTypeWherePossible, onWithInfo),
SingleOption(CSharpCodeStyleOptions.UseImplicitTypeWhereApparent, onWithInfo),
SingleOption(CSharpCodeStyleOptions.UseImplicitTypeForIntrinsicTypes, onWithInfo));
private IDictionary<OptionKey, object> PreferExplicitTypeWithNone() => OptionsSet(
SingleOption(CSharpCodeStyleOptions.UseImplicitTypeWherePossible, offWithNone),
SingleOption(CSharpCodeStyleOptions.UseImplicitTypeWhereApparent, offWithNone),
SingleOption(CSharpCodeStyleOptions.UseImplicitTypeForIntrinsicTypes, offWithNone));
private IDictionary<OptionKey, object> PreferImplicitTypeWithNone() => OptionsSet(
SingleOption(CSharpCodeStyleOptions.UseImplicitTypeWherePossible, onWithNone),
SingleOption(CSharpCodeStyleOptions.UseImplicitTypeWhereApparent, onWithNone),
SingleOption(CSharpCodeStyleOptions.UseImplicitTypeForIntrinsicTypes, onWithNone));
[Fact]
public async Task TestIntLocalDeclaration()
{
var code = @"
class C
{
static void Main()
{
int[||] i = 0;
}
}";
var expected = @"
class C
{
static void Main()
{
var i = 0;
}
}";
await TestInRegularAndScriptAsync(code, expected, options: PreferImplicitTypeWithNone());
await TestInRegularAndScriptAsync(code, expected, options: PreferExplicitTypeWithNone());
await TestInRegularAndScriptAsync(code, expected, options: PreferExplicitTypeWithInfo());
await TestMissingInRegularAndScriptAsync(code, PreferImplicitTypeWithInfo());
}
[Fact]
public async Task TestForeachInsideLocalDeclaration()
{
var code = @"
class C
{
static void Main()
{
System.Action notThisLocal = () => { foreach (int[||] i in new int[0]) { } };
}
}";
var expected = @"
class C
{
static void Main()
{
System.Action notThisLocal = () => { foreach (var[||] i in new int[0]) { } };
}
}";
await TestInRegularAndScriptAsync(code, expected, options: PreferImplicitTypeWithNone());
}
[Fact]
public async Task TestInIntPattern()
{
var code = @"
class C
{
static void Main()
{
_ = 0 is int[||] i;
}
}";
await TestMissingInRegularAndScriptAsync(code, PreferImplicitTypeWithNone());
await TestMissingInRegularAndScriptAsync(code, PreferExplicitTypeWithNone());
await TestMissingInRegularAndScriptAsync(code, PreferExplicitTypeWithInfo());
await TestMissingInRegularAndScriptAsync(code, PreferImplicitTypeWithInfo());
}
[Fact]
public async Task TestIntLocalDeclaration_Multiple()
{
var code = @"
class C
{
static void Main()
{
int[||] i = 0, j = j;
}
}";
await TestMissingInRegularAndScriptAsync(code, PreferImplicitTypeWithNone());
await TestMissingInRegularAndScriptAsync(code, PreferExplicitTypeWithNone());
await TestMissingInRegularAndScriptAsync(code, PreferExplicitTypeWithInfo());
await TestMissingInRegularAndScriptAsync(code, PreferImplicitTypeWithInfo());
}
[Fact]
public async Task TestIntLocalDeclaration_NoInitializer()
{
var code = @"
class C
{
static void Main()
{
int[||] i;
}
}";
await TestMissingInRegularAndScriptAsync(code);
}
[Fact]
public async Task TestIntForLoop()
{
var code = @"
class C
{
static void Main()
{
for (int[||] i = 0;;) { }
}
}";
var expected = @"
class C
{
static void Main()
{
for (var i = 0;;) { }
}
}";
await TestInRegularAndScriptAsync(code, expected, options: PreferImplicitTypeWithNone());
}
[Fact]
public async Task TestInDispose()
{
var code = @"
class C : System.IDisposable
{
static void Main()
{
using (C[||] c = new C()) { }
}
}";
var expected = @"
class C : System.IDisposable
{
static void Main()
{
using (var c = new C()) { }
}
}";
await TestInRegularAndScriptAsync(code, expected, options: PreferImplicitTypeWithNone());
}
[Fact]
public async Task TestIntForeachLoop()
{
var code = @"
class C
{
static void Main()
{
foreach (int[||] i in new[] { 0 }) { }
}
}";
var expected = @"
class C
{
static void Main()
{
foreach (var i in new[] { 0 }) { }
}
}";
await TestInRegularAndScriptAsync(code, expected, options: PreferImplicitTypeWithNone());
}
[Fact]
public async Task TestIntDeconstruction()
{
var code = @"
class C
{
static void Main()
{
(int[||] i, var j) = (0, 1);
}
}";
var expected = @"
class C
{
static void Main()
{
(var i, var j) = (0, 1);
}
}";
await TestInRegularAndScriptAsync(code, expected, options: PreferImplicitTypeWithNone());
}
[Fact]
public async Task TestIntDeconstruction2()
{
var code = @"
class C
{
static void Main()
{
(int[||] i, var j) = (0, 1);
}
}";
var expected = @"
class C
{
static void Main()
{
(var i, var j) = (0, 1);
}
}";
await TestInRegularAndScriptAsync(code, expected, options: PreferImplicitTypeWithNone());
await TestInRegularAndScriptAsync(code, expected, options: PreferExplicitTypeWithNone());
await TestMissingInRegularAndScriptAsync(code, PreferImplicitTypeWithInfo());
}
private Task TestMissingInRegularAndScriptAsync(string initialMarkup, IDictionary<OptionKey, object> options)
{
return TestMissingInRegularAndScriptAsync(initialMarkup, parameters: new TestParameters(options: options));
}
}
}
// 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.Composition;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.TypeStyle;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Utilities;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings.UseExplicitType
namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings.UseType
{
[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.UseExplicitType), Shared]
internal partial class UseExplicitTypeCodeRefactoringProvider : CodeRefactoringProvider
internal abstract class AbstractUseTypeCodeRefactoringProvider : CodeRefactoringProvider
{
protected abstract string Title { get; }
protected abstract Task HandleDeclarationAsync(Document document, SyntaxEditor editor, SyntaxNode node, CancellationToken cancellationToken);
protected abstract TypeSyntax FindAnalyzableType(SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken);
protected abstract TypeStyleResult AnalyzeTypeName(TypeSyntax typeName, SemanticModel semanticModel, OptionSet optionSet, CancellationToken cancellationToken);
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
{
var document = context.Document;
......@@ -47,7 +51,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
Debug.Assert(declaration.IsKind(SyntaxKind.VariableDeclaration, SyntaxKind.ForEachStatement, SyntaxKind.DeclarationExpression));
var declaredType = CSharpUseExplicitTypeHelper.Instance.FindAnalyzableType(declaration, semanticModel, cancellationToken);
var declaredType = FindAnalyzableType(declaration, semanticModel, cancellationToken);
if (declaredType == null)
{
return;
......@@ -63,8 +67,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
return;
}
var typeStyle = CSharpUseExplicitTypeHelper.Instance.AnalyzeTypeName(
declaredType, semanticModel, optionSet, cancellationToken);
var typeStyle = AnalyzeTypeName(declaredType, semanticModel, optionSet, cancellationToken);
if (typeStyle.IsStylePreferred && typeStyle.Severity != DiagnosticSeverity.Hidden)
{
// the analyzer would handle this. So we do not.
......@@ -78,7 +81,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
context.RegisterRefactoring(
new MyCodeAction(
CSharpFeaturesResources.Use_explicit_type,
Title,
c => UpdateDocumentAsync(document, declaredType, c)));
}
......@@ -89,12 +92,12 @@ private static SyntaxNode GetDeclaration(SyntaxNode root, TextSpan textSpan)
a => a.IsKind(SyntaxKind.DeclarationExpression, SyntaxKind.VariableDeclaration, SyntaxKind.ForEachStatement));
}
private static async Task<Document> UpdateDocumentAsync(Document document, SyntaxNode node, CancellationToken cancellationToken)
private async Task<Document> UpdateDocumentAsync(Document document, SyntaxNode node, CancellationToken cancellationToken)
{
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var editor = new SyntaxEditor(root, document.Project.Solution.Workspace);
await UseExplicitTypeCodeFixProvider.HandleDeclarationAsync(document, editor, node, cancellationToken).ConfigureAwait(false);
await HandleDeclarationAsync(document, editor, node, cancellationToken).ConfigureAwait(false);
var newRoot = editor.GetChangedRoot();
return document.WithSyntaxRoot(newRoot);
......
// 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.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp.CodeRefactorings.UseType;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.TypeStyle;
using Microsoft.CodeAnalysis.CSharp.Utilities;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Options;
namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings.UseExplicitType
{
[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.UseExplicitType), Shared]
internal class UseExplicitTypeCodeRefactoringProvider : AbstractUseTypeCodeRefactoringProvider
{
protected override string Title
=> CSharpFeaturesResources.Use_explicit_type;
protected override TypeSyntax FindAnalyzableType(SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken)
=> CSharpUseExplicitTypeHelper.Instance.FindAnalyzableType(node, semanticModel, cancellationToken);
protected override TypeStyleResult AnalyzeTypeName(TypeSyntax typeName, SemanticModel semanticModel, OptionSet optionSet, CancellationToken cancellationToken)
=> CSharpUseExplicitTypeHelper.Instance.AnalyzeTypeName(typeName, semanticModel, optionSet, cancellationToken);
protected override Task HandleDeclarationAsync(Document document, SyntaxEditor editor, SyntaxNode node, CancellationToken cancellationToken)
=> UseExplicitTypeCodeFixProvider.HandleDeclarationAsync(document, editor, node, cancellationToken);
}
}
// 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.Composition;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp.CodeRefactorings.UseType;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.TypeStyle;
using Microsoft.CodeAnalysis.CSharp.Utilities;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Options;
namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings.UseImplicitType
{
[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.UseImplicitType), Shared]
internal partial class UseImplicitTypeCodeRefactoringProvider : AbstractUseTypeCodeRefactoringProvider
{
protected override string Title
=> CSharpFeaturesResources.Use_implicit_type;
protected override TypeSyntax FindAnalyzableType(SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken)
=> CSharpUseImplicitTypeHelper.Instance.FindAnalyzableType(node, semanticModel, cancellationToken);
protected override TypeStyleResult AnalyzeTypeName(TypeSyntax typeName, SemanticModel semanticModel, OptionSet optionSet, CancellationToken cancellationToken)
=> CSharpUseImplicitTypeHelper.Instance.AnalyzeTypeName(typeName, semanticModel, optionSet, cancellationToken);
protected override Task HandleDeclarationAsync(Document document, SyntaxEditor editor, SyntaxNode node, CancellationToken cancellationToken)
{
UseImplicitTypeCodeFixProvider.ReplaceTypeWithVar(editor, node);
return Task.CompletedTask;
}
}
}
......@@ -38,16 +38,20 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
foreach (var diagnostic in diagnostics)
{
var node = root.FindNode(diagnostic.Location.SourceSpan, getInnermostNodeForTie: true);
var implicitType = SyntaxFactory.IdentifierName("var")
.WithTriviaFrom(node);
editor.ReplaceNode(node, implicitType);
ReplaceTypeWithVar(editor, node);
}
return SpecializedTasks.EmptyTask;
}
internal static void ReplaceTypeWithVar(SyntaxEditor editor, SyntaxNode node)
{
var implicitType = SyntaxFactory.IdentifierName("var")
.WithTriviaFrom(node);
editor.ReplaceNode(node, implicitType);
}
private class MyCodeAction : CodeAction.DocumentChangeAction
{
public MyCodeAction(Func<CancellationToken, Task<Document>> createChangedDocument) :
......
......@@ -23,6 +23,7 @@ internal static class PredefinedCodeRefactoringProviderNames
public const string MoveTypeToFile = "Move Type To File Code Action Provider";
public const string ReplaceDocCommentTextWithTag = "Replace Documentation Comment Text With Tag Code Action Provider";
public const string UseExplicitType = "Use Explicit Type Code Action Provider";
public const string UseImplicitType = "Use Implicit Type Code Action Provider";
public const string UseExpressionBody = "Use Expression Body Code Action Provider";
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册