提交 c32b6e73 编写于 作者: Š Šimon Koníček 提交者: Joey Robichaud

Adding code fix for CS0822 (Implicitly-typed variables cannot be constant) (#30519)

Adding UseExplicitTypeForConstCodeFixProvider

- Not offering the fix for multiple declarators
- Not offering regular UseExplicitType code fix and refactoring for constants
上级 5a8dabf9
......@@ -279,6 +279,20 @@ void Method(List<int> var)
await TestMissingInRegularAndScriptAsync(code);
}
[Fact]
public async Task NotOnConstVar()
{
// This error case is handled by a separate code fix (UseExplicitTypeForConst).
await TestMissingInRegularAndScriptAsync(
@"class C
{
void M()
{
const [||]var v = 0;
}
}");
}
private async Task TestInRegularAndScriptWhenDiagnosticNotAppliedAsync(string initialMarkup, string expectedMarkup)
{
// Enabled because the diagnostic is disabled
......
......@@ -1901,6 +1901,20 @@ void Method(List<int> var)
Console.WriteLine(value.Value);
}
}
}", new TestParameters(options: ExplicitTypeEverywhere()));
}
[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExplicitType)]
public async Task NotOnConstVar()
{
// This error case is handled by a separate code fix (UseExplicitTypeForConst).
await TestMissingInRegularAndScriptAsync(
@"class C
{
void M()
{
const [|var|] v = 0;
}
}", new TestParameters(options: ExplicitTypeEverywhere()));
}
}
......
// 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.UseExplicitTypeForConst;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExplicitTypeForConst
{
[Trait(Traits.Feature, Traits.Features.CodeActionsUseExplicitTypeForConst)]
public sealed class UseExplicitTypeForConstTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
{
internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace)
=> (null, new UseExplicitTypeForConstCodeFixProvider());
[Fact]
public async Task TestWithIntLiteral()
{
await TestInRegularAndScriptAsync(
@"class C
{
void M()
{
const [|var|] v = 0;
}
}",
@"class C
{
void M()
{
const int v = 0;
}
}");
}
[Fact]
public async Task TestWithStringConstant()
{
await TestInRegularAndScriptAsync(
@"class C
{
const string s = null;
void M()
{
const [|var|] v = s;
}
}",
@"class C
{
const string s = null;
void M()
{
const string v = s;
}
}");
}
[Fact]
public async Task TestWithQualifiedType()
{
await TestInRegularAndScriptAsync(
@"class C
{
void M()
{
const [|var|] v = default(System.Action);
}
}",
@"class C
{
void M()
{
const System.Action v = default(System.Action);
}
}");
}
[Fact]
public async Task TestWithNonConstantInitializer()
{
await TestInRegularAndScriptAsync(
@"class C
{
void M()
{
const [|var|] v = System.Console.ReadLine();
}
}",
@"class C
{
void M()
{
const string v = System.Console.ReadLine();
}
}");
}
[Fact]
public async Task TestWithNonConstantTupleType()
{
await TestInRegularAndScriptAsync(
@"class C
{
void M()
{
const [|var|] v = (0, true);
}
}",
@"class C
{
void M()
{
const (int, bool) v = (0, true);
}
}");
}
[Fact]
public async Task TestNotWithNullLiteral()
{
await TestMissingInRegularAndScriptAsync(
@"class C
{
void M()
{
const [|var|] v = null;
}
}");
}
[Fact]
public async Task TestNotWithDefaultLiteral()
{
await TestMissingInRegularAndScriptAsync(
@"class C
{
void M()
{
const [|var|] v = default;
}
}");
}
[Fact]
public async Task TestNotWithLambda()
{
await TestMissingInRegularAndScriptAsync(
@"class C
{
void M()
{
const [|var|] v = () => { };
}
}");
}
[Fact]
public async Task TestNotWithAnonymousType()
{
await TestMissingInRegularAndScriptAsync(
@"class C
{
void M()
{
const [|var|] v = new { a = 0 };
}
}");
}
[Fact]
public async Task TestNotWithArrayInitializer()
{
await TestMissingInRegularAndScriptAsync(
@"class C
{
void M()
{
const [|var|] v = { 0, 1 };
}
}");
}
[Fact]
public async Task TestNotWithMissingInitializer()
{
await TestMissingInRegularAndScriptAsync(
@"class C
{
void M()
{
const [|var|] v =
}
}");
}
[Fact]
public async Task TestNotWithClassVar()
{
await TestMissingInRegularAndScriptAsync(
@"class C
{
class var { }
void M()
{
const [|var|] v = 0;
}
}");
}
[Fact]
public async Task TestNotOnField()
{
await TestMissingInRegularAndScriptAsync(
@"class C
{
const [|var|] v = 0;
}");
}
[Fact]
public async Task TestNotWithMultipleDeclarators()
{
await TestMissingInRegularAndScriptAsync(
@"class C
{
void M()
{
const [|var|] a = 0, b = 0;
}
}");
}
}
}
// 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.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.CSharp.UseExplicitTypeForConst
{
[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.UseExplicitTypeForConst), Shared]
internal sealed class UseExplicitTypeForConstCodeFixProvider : CodeFixProvider
{
private const string CS0822 = nameof(CS0822); // Implicitly-typed variables cannot be constant
public override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(CS0822);
public override FixAllProvider GetFixAllProvider()
{
// This code fix addresses a very specific compiler error. It's unlikely there will be more than 1 of them at a time.
return null;
}
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
if (root.FindNode(context.Span) is VariableDeclarationSyntax variableDeclaration &&
variableDeclaration.Variables.Count == 1)
{
var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
var type = semanticModel.GetTypeInfo(variableDeclaration.Type, context.CancellationToken).ConvertedType;
if (type == null || type.TypeKind == TypeKind.Error || type.IsAnonymousType)
{
return;
}
context.RegisterCodeFix(
new MyCodeAction(c => FixAsync(context.Document, context.Span, type, c)),
context.Diagnostics);
}
}
private static async Task<Document> FixAsync(
Document document, TextSpan span, ITypeSymbol type, CancellationToken cancellationToken)
{
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var variableDeclaration = (VariableDeclarationSyntax)root.FindNode(span);
var newRoot = root.ReplaceNode(variableDeclaration.Type, type.GenerateTypeSyntax(allowVar: false));
return document.WithSyntaxRoot(newRoot);
}
private sealed class MyCodeAction : CodeAction.DocumentChangeAction
{
public MyCodeAction(Func<CancellationToken, Task<Document>> createChangedDocument) :
base(CSharpFeaturesResources.Use_explicit_type_instead_of_var,
createChangedDocument)
{
}
}
}
}
......@@ -57,6 +57,7 @@ internal static class PredefinedCodeFixProviderNames
public const string AddNew = nameof(AddNew);
public const string UseImplicitType = nameof(UseImplicitType);
public const string UseExplicitType = nameof(UseExplicitType);
public const string UseExplicitTypeForConst = nameof(UseExplicitTypeForConst);
public const string UseCollectionInitializer = nameof(UseCollectionInitializer);
public const string UseObjectInitializer = nameof(UseObjectInitializer);
public const string UseThrowExpression = nameof(UseThrowExpression);
......
......@@ -134,6 +134,7 @@ public static class Features
public const string CodeActionsUseExpressionBody = "CodeActions.UseExpressionBody";
public const string CodeActionsUseImplicitType = "CodeActions.UseImplicitType";
public const string CodeActionsUseExplicitType = "CodeActions.UseExplicitType";
public const string CodeActionsUseExplicitTypeForConst = "CodeActions.UseExplicitTypeForConst";
public const string CodeActionsUseExplicitTupleName = "CodeActions.UseExplicitTupleName";
public const string CodeActionsUseFrameworkType = "CodeActions.UseFrameworkType";
public const string CodeActionsUseIsNullCheck = "CodeActions.UseIsNullCheck";
......
......@@ -88,13 +88,20 @@ protected override bool ShouldAnalyzeForEachStatement(ForEachStatementSyntax for
typeName.Parent.Parent.IsKind(SyntaxKind.LocalDeclarationStatement, SyntaxKind.ForStatement, SyntaxKind.UsingStatement))
{
// check assignment for variable declarations.
var variable = ((VariableDeclarationSyntax)typeName.Parent).Variables.First();
var variableDeclaration = (VariableDeclarationSyntax)typeName.Parent;
var variable = variableDeclaration.Variables.First();
if (!AssignmentSupportsStylePreference(
variable.Identifier, typeName, variable.Initializer.Value,
semanticModel, optionSet, cancellationToken))
{
return false;
}
// This error case is handled by a separate code fix (UseExplicitTypeForConst).
if ((variableDeclaration.Parent as LocalDeclarationStatementSyntax)?.IsConst == true)
{
return false;
}
}
else if (typeName.Parent is ForEachStatementSyntax foreachStatement &&
foreachStatement.Type == typeName)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册