gpt4 book ai didi

c# - 使用代码修复提供程序时如何删除 SyntaxToken

转载 作者:行者123 更新时间:2023-11-30 16:52:58 26 4
gpt4 key购买 nike

我正在开发一个用于格式化代码的代码库。在某些情况下,我必须删除语法标记,例如参数列表中的关键字“params”,如果我发现它是多余的,或者删除一些不需要的大括号(“{”“}”)。

问题是,从语法树中删除语法标记是如此困难和不安。我已经尝试了许多不同的解决方案,您可以在下面找到这些解决方案,但对于我来说,我无法成功。我想要一个适用于你可能遇到的任何语法标记的解决方案,并且我可能需要从语法树中删除它。

在下面的案例中,我构建了一个分析器,该分析器当前可以正常运行,但代码修复提供程序无法正常工作...(这是一个使用冗余参数关键字的案例)。

//更新 - 我提供了分析器/代码修复提供程序/NUnit 测试的完整代码

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Formatting;

namespace RefactoringEssentials.CSharp.Diagnostics
{
[ExportCodeFixProvider(LanguageNames.CSharp), System.Composition.Shared]
public class RedundantParamsCodeFixProvider : CodeFixProvider
{
public override ImmutableArray<string> FixableDiagnosticIds
{
get
{
return ImmutableArray.Create(CSharpDiagnosticIDs.RedundantParamsAnalyzerID);
}
}

public override FixAllProvider GetFixAllProvider()
{
return WellKnownFixAllProviders.BatchFixer;
}

public async override Task RegisterCodeFixesAsync(CodeFixContext context)
{
var document = context.Document;
var cancellationToken = context.CancellationToken;
var span = context.Span;
var diagnostics = context.Diagnostics;
var root = await document.GetSyntaxRootAsync(cancellationToken);
var diagnostic = diagnostics.First();
var node = root.FindNode(context.Span) as ParameterSyntax;
if (node == null)
return;

if (!node.Modifiers.Any(x => x.IsKind(SyntaxKind.ParamsKeyword)))
return;

var oldParameterNode = node;
var paramList = node.Parent as ParameterListSyntax;
if (paramList == null)
return;

//var newRoot = root.ReplaceNode(
// oldParameterNode.Parent as ParameterListSyntax,
// paramList.WithParameters
// (SyntaxFactory.SeparatedList(paramList.Parameters.ToArray()))
// .WithLeadingTrivia(node.GetLeadingTrivia())
// .WithTrailingTrivia(node.GetTrailingTrivia()))
// .WithAdditionalAnnotations(Formatter.Annotation);

//var paramsKeyword = (node.Modifiers.FirstOrDefault(x => x.IsKind(SyntaxKind.ParamsKeyword)));
//var indexParams = node.Modifiers.IndexOf(paramsKeyword);
//var syntaxListWithoutParams = node.Modifiers.RemoveAt(indexParams);
//node.ReplaceToken(paramsKeyword, syntaxListWithoutParams.AsEnumerable());
context.RegisterCodeFix(CodeActionFactory.Create(node.Span, diagnostic.Severity, "Remove 'params' modifier", token =>
{
var newNode = SyntaxFactory.Parameter(node.AttributeLists,node.Modifiers.Remove(SyntaxFactory.Token(SyntaxKind.ParamsKeyword)),node.Type,node.Identifier,node.Default);
var newRoot = root.ReplaceNode(node, newNode);
return Task.FromResult(document.WithSyntaxRoot(newRoot));
}), diagnostic);
//context.RegisterCodeFix(CodeActionFactory.Create(node.SKCpan, diagnostic.Severity, , document.WithSyntaxRoot(newRoot)), diagnostic);
}
}
}

这是我的情况下的用例。

using System;
using NUnit.Framework;
using RefactoringEssentials.CSharp.Diagnostics;

namespace RefactoringEssentials.Tests.CSharp.Diagnostics
{
[TestFixture]
public class RedundantParamsTests : CSharpDiagnosticTestBase
{
[Test]
public void TestBasicCase()
{
Analyze<RedundantParamsAnalyzer>(@"class FooBar
{
public virtual void Foo(string fmt, object[] args)
{
}
}

class FooBar2 : FooBar
{
public override void Foo(string fmt, $params object[] args$)
{
System.Console.WriteLine(fmt, args);
}
}", @"class FooBar
{
public virtual void Foo(string fmt, object[] args)
{
}
}

class FooBar2 : FooBar
{
public override void Foo(string fmt, object[] args)
{
System.Console.WriteLine(fmt, args);
}
}");
}

[Test]
public void TestValidCase()
{
Analyze<RedundantParamsAnalyzer>(@"class FooBar
{
public virtual void Foo(string fmt, object[] args)
{
}
}

class FooBar2 : FooBar
{
public override void Foo(string fmt, object[] args)
{
System.Console.WriteLine(fmt, args);
}
}");
}

[Test]
public void ValideParamsUsageTests()
{
Analyze<RedundantParamsAnalyzer>(@"class FooBar
{
public virtual void Foo(string fmt, params object[] args)
{
}
}

class FooBar2 : FooBar
{
public override void Foo(string fmt, params object[] args)
{
System.Console.WriteLine(fmt, args);
}
}");
}

[Test]
public void TestDisable()
{
Analyze<RedundantParamsAnalyzer>(@"class FooBar
{
public virtual void Foo(string fmt, object[] args)
{
}
}

class FooBar2 : FooBar
{
// ReSharper disable once RedundantParams
public override void Foo(string fmt, params object[] args)
{
System.Console.WriteLine(fmt, args);
}
}");
}
}
}

对于那些可能对我如何确定参数是多余的感兴趣的人,这里是逻辑:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using System.Collections.Immutable;
using System.Linq;

namespace RefactoringEssentials.CSharp.Diagnostics
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class RedundantParamsAnalyzer : DiagnosticAnalyzer
{
private static readonly DiagnosticDescriptor descriptor = new DiagnosticDescriptor(
CSharpDiagnosticIDs.RedundantParamsAnalyzerID,
GettextCatalog.GetString("'params' is ignored on overrides"),
GettextCatalog.GetString("'params' is always ignored in overrides"),
DiagnosticAnalyzerCategories.RedundanciesInDeclarations,
DiagnosticSeverity.Warning,
isEnabledByDefault: true,
helpLinkUri: HelpLink.CreateFor(CSharpDiagnosticIDs.RedundantParamsAnalyzerID),
customTags: DiagnosticCustomTags.Unnecessary
);

public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(descriptor);

public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(
(nodeContext) =>
{
Diagnostic diagnostic;
if (TryGetParamsDiagnostic(nodeContext, out diagnostic))
{
nodeContext.ReportDiagnostic(diagnostic);
}
},
SyntaxKind.ParameterList
);
}

//I think it's a better decision to head in this direction instead of MethodDeclaration.
private static bool TryGetParamsDiagnostic(SyntaxNodeAnalysisContext nodeContext, out Diagnostic diagnostic)
{
diagnostic = default(Diagnostic);
if (nodeContext.IsFromGeneratedCode())
return false;

var paramList = nodeContext.Node as ParameterListSyntax;
var declaration = paramList?.Parent as MethodDeclarationSyntax;

if (declaration == null)
return false;

if (declaration.Modifiers.Count == 0 || !declaration.Modifiers.Any(SyntaxKind.OverrideKeyword))
return false;

var lastParam = declaration.ParameterList.Parameters.LastOrDefault();
SyntaxToken? paramsModifierToken = null;
if (lastParam == null)
return false;

foreach (var x in lastParam.Modifiers)
{
if (x.IsKind(SyntaxKind.ParamsKeyword))
{
paramsModifierToken = x;
break;
}
}

if (!paramsModifierToken.HasValue ||
!paramsModifierToken.Value.IsKind(SyntaxKind.ParamsKeyword))
return false;

diagnostic = Diagnostic.Create(descriptor, lastParam.GetLocation());
return true;
}
}

最佳答案

在这种情况下,您不想删除整个节点,而只想删除 params 修饰符。由于 SyntaxNodes 是不可变的,因此您需要使用 WithModifiers 方法创建一个带有适当修饰符的新节点:

public async override Task RegisterCodeFixesAsync(CodeFixContext context)
{

var diagnostic = context.Diagnostics.First();
context.RegisterCodeFix(CodeAction.Create("Remove 'params' modifier", async token =>
{

var document = context.Document;
var root = await document.GetSyntaxRootAsync(token);

var fullParameterNode = root.FindNode(diagnostic.Location.SourceSpan, false) as ParameterSyntax;

// Keep all modifiers except the params
var newModifiers = fullParameterNode.Modifiers.Where(m => !m.IsKind(SyntaxKind.ParamsKeyword));
var syntaxModifiers = SyntaxTokenList.Create(new SyntaxToken());
syntaxModifiers.AddRange(newModifiers);

var updatedParameterNode = fullParameterNode.WithModifiers(syntaxModifiers);

var newDoc = document.WithSyntaxRoot(root.ReplaceNode(fullParameterNode, updatedParameterNode));
return newDoc;
}, "KEY"), diagnostic);
}

至于语法的通用删除选项,我不知道有哪个好用。 Node.ReplaceSyntax 是一个相当复杂的方法,我通常发现使用 ReplaceNodeRemoveNode 方法更容易。

关于c# - 使用代码修复提供程序时如何删除 SyntaxToken,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31931091/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com