提交 88f5f2ee 编写于 作者: C Cyrus Najmabadi

Add feature to make applicable local functions static.

上级 a9bb375a
// 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.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.CodeAnalysis.CSharp.MakeLocalFunctionStatic
{
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(MakeLocalFunctionStaticCodeFixProvider)), Shared]
internal class MakeLocalFunctionStaticCodeFixProvider : SyntaxEditorBasedCodeFixProvider
{
public override ImmutableArray<string> FixableDiagnosticIds { get; } =
ImmutableArray.Create(IDEDiagnosticIds.MakeLocalFunctionStaticDiagnosticId);
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
context.RegisterCodeFix(new MyCodeAction(
c => FixAsync(context.Document, context.Diagnostics[0], c)),
context.Diagnostics);
return Task.CompletedTask;
}
protected override Task FixAllAsync(
Document document, ImmutableArray<Diagnostic> diagnostics,
SyntaxEditor editor, CancellationToken cancellationToken)
{
var localFunctions = diagnostics.SelectAsArray(d => d.AdditionalLocations[0].FindNode(cancellationToken));
foreach (var localFunction in localFunctions)
{
editor.ReplaceNode(
localFunction,
(current, generator) => generator.WithModifiers(
current,
generator.GetModifiers(current).WithIsStatic(true)));
}
return Task.CompletedTask;
}
private class MyCodeAction : CodeAction.DocumentChangeAction
{
public MyCodeAction(Func<CancellationToken, Task<Document>> createChangedDocument)
: base(FeaturesResources.Make_local_function_static, createChangedDocument, FeaturesResources.Make_local_function_static)
{
}
}
}
}
// 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.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
namespace Microsoft.CodeAnalysis.CSharp.MakeLocalFunctionStatic
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal class MakeLocalFunctionStaticDiagnosticAnalyzer : AbstractBuiltInCodeStyleDiagnosticAnalyzer
{
public MakeLocalFunctionStaticDiagnosticAnalyzer()
: base(IDEDiagnosticIds.MakeLocalFunctionStaticDiagnosticId,
new LocalizableResourceString(nameof(FeaturesResources.Make_local_function_static), FeaturesResources.ResourceManager, typeof(FeaturesResources)),
new LocalizableResourceString(nameof(FeaturesResources.Local_function_can_be_made_static), FeaturesResources.ResourceManager, typeof(FeaturesResources)))
{
}
public override bool OpenFileOnly(Workspace workspace) => false;
public override DiagnosticAnalyzerCategory GetAnalyzerCategory()
=> DiagnosticAnalyzerCategory.SemanticSpanAnalysis;
protected override void InitializeWorker(AnalysisContext context)
=> context.RegisterSyntaxNodeAction(AnalyzeSyntax, SyntaxKind.LocalFunctionStatement);
private void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
{
var localFunction = (LocalFunctionStatementSyntax)context.Node;
if (localFunction.Modifiers.Any(SyntaxKind.StaticKeyword))
{
return;
}
var syntaxTree = context.Node.SyntaxTree;
var cancellationToken = context.CancellationToken;
var optionSet = context.Options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult();
if (optionSet == null)
{
return;
}
var option = optionSet.GetOption(CSharpCodeStyleOptions.PreferStaticLocalFunction);
if (!option.Value)
{
return;
}
var semanticModel = context.SemanticModel;
var analysis = semanticModel.AnalyzeDataFlow(localFunction);
var captures = analysis.CapturedInside;
if (analysis.Succeeded && captures.Length == 0)
{
context.ReportDiagnostic(DiagnosticHelper.Create(
Descriptor,
localFunction.Identifier.GetLocation(),
option.Notification.Severity,
additionalLocations: ImmutableArray.Create(localFunction.GetLocation()),
properties: null));
}
}
}
}
......@@ -102,6 +102,8 @@ internal static class IDEDiagnosticIds
// Conceptually belongs with IDE0021-IDE0027 & IDE0053, but is here because it was added later
public const string UseExpressionBodyForLocalFunctionsDiagnosticId = "IDE0061";
public const string MakeLocalFunctionStaticDiagnosticId = "IDE0062";
// Analyzer error Ids
public const string AnalyzerChangedId = "IDE1001";
public const string AnalyzerDependencyConflictId = "IDE1002";
......
......@@ -19,7 +19,7 @@ namespace Microsoft.CodeAnalysis {
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class FeaturesResources {
......@@ -2259,6 +2259,15 @@ internal class FeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Local function can be made static.
/// </summary>
internal static string Local_function_can_be_made_static {
get {
return ResourceManager.GetString("Local_function_can_be_made_static", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to local variable.
/// </summary>
......@@ -2322,6 +2331,15 @@ internal class FeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Make local function &apos;static&apos;.
/// </summary>
internal static string Make_local_function_static {
get {
return ResourceManager.GetString("Make_local_function_static", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Make method synchronous.
/// </summary>
......
......@@ -1625,4 +1625,10 @@ This version used in: {2}</value>
<data name="Pull_0_up_to_1" xml:space="preserve">
<value>Pull '{0}' up to '{1}'</value>
</data>
<data name="Local_function_can_be_made_static" xml:space="preserve">
<value>Local function can be made static</value>
</data>
<data name="Make_local_function_static" xml:space="preserve">
<value>Make local function 'static'</value>
</data>
</root>
\ No newline at end of file
......@@ -1534,6 +1534,7 @@ public override SyntaxNode WithAccessibility(SyntaxNode declaration, Accessibili
private static readonly DeclarationModifiers s_structModifiers = DeclarationModifiers.New | DeclarationModifiers.Partial | DeclarationModifiers.ReadOnly | DeclarationModifiers.Ref;
private static readonly DeclarationModifiers s_interfaceModifiers = DeclarationModifiers.New | DeclarationModifiers.Partial;
private static readonly DeclarationModifiers s_accessorModifiers = DeclarationModifiers.Abstract | DeclarationModifiers.New | DeclarationModifiers.Override | DeclarationModifiers.Virtual;
private static readonly DeclarationModifiers s_localFunctionModifiers = DeclarationModifiers.Static;
private static DeclarationModifiers GetAllowedModifiers(SyntaxKind kind)
{
......@@ -1583,6 +1584,9 @@ private static DeclarationModifiers GetAllowedModifiers(SyntaxKind kind)
case SyntaxKind.RemoveAccessorDeclaration:
return s_accessorModifiers;
case SyntaxKind.LocalFunctionStatement:
return s_localFunctionModifiers;
case SyntaxKind.EnumMemberDeclaration:
case SyntaxKind.Parameter:
case SyntaxKind.LocalDeclarationStatement:
......@@ -1676,6 +1680,8 @@ private static SyntaxTokenList GetModifierTokens(SyntaxNode declaration)
case SyntaxKind.AddAccessorDeclaration:
case SyntaxKind.RemoveAccessorDeclaration:
return ((AccessorDeclarationSyntax)declaration).Modifiers;
case SyntaxKind.LocalFunctionStatement:
return ((LocalFunctionStatementSyntax)declaration).Modifiers;
}
return default;
......@@ -1724,6 +1730,8 @@ private static SyntaxNode SetModifierTokens(SyntaxNode declaration, SyntaxTokenL
case SyntaxKind.AddAccessorDeclaration:
case SyntaxKind.RemoveAccessorDeclaration:
return ((AccessorDeclarationSyntax)declaration).WithModifiers(modifiers);
case SyntaxKind.LocalFunctionStatement:
return ((LocalFunctionStatementSyntax)declaration).WithModifiers(modifiers);
default:
return declaration;
}
......
......@@ -206,6 +206,13 @@ private static Option<T> CreateOption<T>(OptionGroup group, string name, T defau
EditorConfigStorageLocation.ForStringCodeStyleOption("csharp_preferred_modifier_order"),
new RoamingProfileStorageLocation($"TextEditor.CSharp.Specific.{nameof(PreferredModifierOrder)}")});
public static readonly Option<CodeStyleOption<bool>> PreferStaticLocalFunction = CreateOption(
CSharpCodeStyleOptionGroups.Modifier, nameof(PreferStaticLocalFunction),
defaultValue: CodeStyleOptions.TrueWithSuggestionEnforcement,
storageLocations: new OptionStorageLocation[] {
EditorConfigStorageLocation.ForBoolCodeStyleOption("csharp_prefer_static_local_function"),
new RoamingProfileStorageLocation($"TextEditor.CSharp.Specific.{nameof(PreferStaticLocalFunction)}")});
public static readonly Option<CodeStyleOption<bool>> PreferLocalOverAnonymousFunction = CreateOption(
CSharpCodeStyleOptionGroups.ExpressionLevelPreferences, nameof(PreferLocalOverAnonymousFunction),
defaultValue: CodeStyleOptions.TrueWithSuggestionEnforcement,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册