用私有 const 语句替换字符串文字
Replacing string literals with private const statements
我正在为 C# 代码构建一个分析器,当对某些函数的某些参数使用字符串文字而不是 const 字符串时,它会生成错误。即
class MyClass
{
private void MyMethod(IWriter writer)
{
writer.WriteInteger("NamedValue", 4);
}
}
应该变成:
class MyClass
{
private const string IoNamedValueKey = "NamedValue";
private void MyMethod(IWriter writer)
{
writer.WriteInteger(IoNamedValueKey , 4);
}
}
我已经在显示错误的地方工作了,但我也想提供一个 CodeFixProvider。我 运行 遇到了两个问题:
- 我需要添加
private const string IoNamedValueKey = "NamedValue";
语句,最好就在有问题的方法之上。
- 但前提是它不存在。
我不完全确定 CodeFixProvider 的模板方法是否针对我的目的使用了适当的重载(它只是将类型名称替换为大写变体),那么从 RegisterCodeFixesAsync 方法内部前进的最佳方法是什么?
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
// ... now what?
}
根据 roslynquoter 的说法,所需的节点可以按如下方式构建,但我仍然对如何将其注入到上下文中有些不知所措。
CompilationUnit()
.WithMembers(
SingletonList<MemberDeclarationSyntax>(
FieldDeclaration(
VariableDeclaration(
PredefinedType(
Token(SyntaxKind.StringKeyword)))
.WithVariables(
SingletonSeparatedList<VariableDeclaratorSyntax>(
VariableDeclarator(
Identifier("IoNamedValueKey"))
.WithInitializer(
EqualsValueClause(
LiteralExpression(
SyntaxKind.StringLiteralExpression,
Literal("NamedValue")))))))
.WithModifiers(
TokenList(
new []{
Token(SyntaxKind.PrivateKeyword),
Token(SyntaxKind.ConstKeyword)}))))
.NormalizeWhitespace()
您应该注册一个CodeAction
,通过context
引入更改的文档。对于
- 正在生成
SyntaxNodes
- 您可以使用 CSharp
SyntaxFactory
- 为您的 consant 获取唯一的名称 - 看看 Roslyn 的 UniqueNameGenerator and NameGenerator,它们没有被 API 公开,但是重新实现它们的一些简化版本会很容易。
这是您的代码可能看起来的示例(已更新):
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var diagnostic = context.Diagnostics.First();
var diagnosticSpan = diagnostic.Location.SourceSpan;
var argument = root.FindNode(diagnosticSpan);
if (!IsBadStringLiteralArgument(argument))
{
return;
}
// Register a code action that will invoke the fix.
context.RegisterCodeFix(
CodeAction.Create(
title: title,
createChangedDocument: (ct) => InlineConstField(context.Document, root, argument, ct),
equivalenceKey: title),
diagnostic);
}
private async Task<Document> InlineConstField(Document document, SyntaxNode root, SyntaxNode argument, CancellationToken cancellationToken)
{
var stringLiteral = (argument as ArgumentSyntax).Expression as LiteralExpressionSyntax;
string suggestdName = this.GetSuggestedName(stringLiteral);
var containingMember = argument.FirstAncestorOrSelf<MemberDeclarationSyntax>();
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var containingMemberSymbol = semanticModel.GetDeclaredSymbol(containingMember);
var takenNames = containingMemberSymbol.ContainingType.MemberNames;
string uniqueName = this.GetUniqueName(suggestdName, takenNames);
FieldDeclarationSyntax constField = CreateConstFieldDeclaration(uniqueName, stringLiteral).WithAdditionalAnnotations(Formatter.Annotation);
var newRoot = root.ReplaceNode(containingMember, new[] { constField, containingMember });
newRoot = Formatter.Format(newRoot, Formatter.Annotation, document.Project.Solution.Workspace);
return document.WithSyntaxRoot(newRoot);
}
private FieldDeclarationSyntax CreateConstFieldDeclaration(string uniqueName, LiteralExpressionSyntax stringLiteral)
{
return SyntaxFactory.FieldDeclaration(
SyntaxFactory.List<AttributeListSyntax>(),
SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.ConstKeyword)),
SyntaxFactory.VariableDeclaration(
SyntaxFactory.ParseTypeName("string"),
SyntaxFactory.SingletonSeparatedList(
SyntaxFactory.VariableDeclarator(
SyntaxFactory.Identifier(uniqueName),
argumentList: null,
initializer: SyntaxFactory.EqualsValueClause(stringLiteral)))));
}
我正在为 C# 代码构建一个分析器,当对某些函数的某些参数使用字符串文字而不是 const 字符串时,它会生成错误。即
class MyClass
{
private void MyMethod(IWriter writer)
{
writer.WriteInteger("NamedValue", 4);
}
}
应该变成:
class MyClass
{
private const string IoNamedValueKey = "NamedValue";
private void MyMethod(IWriter writer)
{
writer.WriteInteger(IoNamedValueKey , 4);
}
}
我已经在显示错误的地方工作了,但我也想提供一个 CodeFixProvider。我 运行 遇到了两个问题:
- 我需要添加
private const string IoNamedValueKey = "NamedValue";
语句,最好就在有问题的方法之上。 - 但前提是它不存在。
我不完全确定 CodeFixProvider 的模板方法是否针对我的目的使用了适当的重载(它只是将类型名称替换为大写变体),那么从 RegisterCodeFixesAsync 方法内部前进的最佳方法是什么?
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
// ... now what?
}
根据 roslynquoter 的说法,所需的节点可以按如下方式构建,但我仍然对如何将其注入到上下文中有些不知所措。
CompilationUnit()
.WithMembers(
SingletonList<MemberDeclarationSyntax>(
FieldDeclaration(
VariableDeclaration(
PredefinedType(
Token(SyntaxKind.StringKeyword)))
.WithVariables(
SingletonSeparatedList<VariableDeclaratorSyntax>(
VariableDeclarator(
Identifier("IoNamedValueKey"))
.WithInitializer(
EqualsValueClause(
LiteralExpression(
SyntaxKind.StringLiteralExpression,
Literal("NamedValue")))))))
.WithModifiers(
TokenList(
new []{
Token(SyntaxKind.PrivateKeyword),
Token(SyntaxKind.ConstKeyword)}))))
.NormalizeWhitespace()
您应该注册一个CodeAction
,通过context
引入更改的文档。对于
- 正在生成
SyntaxNodes
- 您可以使用CSharp
SyntaxFactory - 为您的 consant 获取唯一的名称 - 看看 Roslyn 的 UniqueNameGenerator and NameGenerator,它们没有被 API 公开,但是重新实现它们的一些简化版本会很容易。
这是您的代码可能看起来的示例(已更新):
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var diagnostic = context.Diagnostics.First();
var diagnosticSpan = diagnostic.Location.SourceSpan;
var argument = root.FindNode(diagnosticSpan);
if (!IsBadStringLiteralArgument(argument))
{
return;
}
// Register a code action that will invoke the fix.
context.RegisterCodeFix(
CodeAction.Create(
title: title,
createChangedDocument: (ct) => InlineConstField(context.Document, root, argument, ct),
equivalenceKey: title),
diagnostic);
}
private async Task<Document> InlineConstField(Document document, SyntaxNode root, SyntaxNode argument, CancellationToken cancellationToken)
{
var stringLiteral = (argument as ArgumentSyntax).Expression as LiteralExpressionSyntax;
string suggestdName = this.GetSuggestedName(stringLiteral);
var containingMember = argument.FirstAncestorOrSelf<MemberDeclarationSyntax>();
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var containingMemberSymbol = semanticModel.GetDeclaredSymbol(containingMember);
var takenNames = containingMemberSymbol.ContainingType.MemberNames;
string uniqueName = this.GetUniqueName(suggestdName, takenNames);
FieldDeclarationSyntax constField = CreateConstFieldDeclaration(uniqueName, stringLiteral).WithAdditionalAnnotations(Formatter.Annotation);
var newRoot = root.ReplaceNode(containingMember, new[] { constField, containingMember });
newRoot = Formatter.Format(newRoot, Formatter.Annotation, document.Project.Solution.Workspace);
return document.WithSyntaxRoot(newRoot);
}
private FieldDeclarationSyntax CreateConstFieldDeclaration(string uniqueName, LiteralExpressionSyntax stringLiteral)
{
return SyntaxFactory.FieldDeclaration(
SyntaxFactory.List<AttributeListSyntax>(),
SyntaxFactory.TokenList(SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.ConstKeyword)),
SyntaxFactory.VariableDeclaration(
SyntaxFactory.ParseTypeName("string"),
SyntaxFactory.SingletonSeparatedList(
SyntaxFactory.VariableDeclarator(
SyntaxFactory.Identifier(uniqueName),
argumentList: null,
initializer: SyntaxFactory.EqualsValueClause(stringLiteral)))));
}