代码修复:更改成员和 class 用于方法调用
Code fix : change members and class being used for a method call
我正在做一个代码分析器,但我找不到一种方法来更改 class 和调用方法时正在访问的成员。例如,如果我有
public class FirstClass
{
public static void DoSomething(object SomeParameter)
{
//method body
}
}
public class SecondClass
{
public static void ReplaceDoSomething(object SomeParameter)
{
//other method's body
}
}
我怎样才能让我的代码修复更改 FirstClass.DoSomething(new object());
到 SecondClass.ReplaceDoSomething(new object());
?
我尝试使用 SyntaxFactory
但我无法真正弄清楚大多数方法需要哪些参数,MSDN 几乎没有提供有关它们的详细信息...
代码分析器
public override void Initialize(AnalysisContext context)
{
context.RegisterOperationAction(Analyze, OperationKind.Invocation);
}
private void Analyze(OperationAnalysisContext context)
{
IInvocationOperation operation = (IInvocationOperation)context.Operation;
if (operation.TargetMethod.Name == "DoSomething" && operation.TargetMethod.ContainingNamespace.Name == "FirstNamespace" && operation.TargetMethod.ContainingType.Inherits("FirstNamespace.FirstClass"))
{
//Rule is an automaticaly generated variable by visual studio when you create the analyzer
Diagnostic diagnostic = Diagnostic.Create(Rule, operation.Syntax.GetLocation());
context.ReportDiagnostic(diagnostic);
}
}
public static class INamedTypeSymbolExtensions
{
public static bool Inherits(this INamedTypeSymbol first, string otherFullName)
{
if (first.GetFullName() == otherFullName)
{
return true;
}
else if (typeof(object).FullName == first.GetFullName())
{
return false;
}
else
{
return first.BaseType.Inherits(otherFullName);
}
}
public static string GetFullName(this INamedTypeSymbol element)
{
StringBuilder ret = new StringBuilder(element.Name);
INamespaceSymbol ContainingNamespace = element.ContainingNamespace;
while (ContainingNamespace.ContainingNamespace != null)
{
ret.Insert(0, ContainingNamespace.Name + ".");
ContainingNamespace = ContainingNamespace.ContainingNamespace;
}
return ret.ToString();
}
}
编辑:我得到的最好结果是
private async Task<Document> ProvideCodeFix(Document document, InvocationExpressionSyntax invocation, CancellationToken cancellationToken)
{
SyntaxToken replaced = invocation.GetFirstToken();
SyntaxToken replacing = SyntaxFactory.IdentifierName("SecondClass").GetFirstToken().WithLeadingTrivia(replaced.LeadingTrivia));
return document.WithSyntaxRoot((await document.GetSyntaxRootAsync()).ReplaceToken(replaced, replacing);
}
有了这个,它会将 FirstClass.DoSomething(new object());
更改为 SecondClass.DoSomething(new object());
但这种方法的主要问题是,如果我必须将 FirstNamespace.FirstClass.DoSomething(new object());
更改为 SecondClass.ReplaceDoSomething(new object());
,那么它将不起作用
PS : 我用 .WithLeadingTrivia(...)
来保留评论和在 FirstClass
之前写的东西
此代码有效。我们得到 InvocationExpressionSyntax.Expression
代表 ()
参数列表之前的整个部分。然后我们替换整个表达式。
private async Task<Document> ProvideCodeFix(Document document, InvocationExpressionSyntax invocationExpression, CancellationToken cancellationToken)
{
// Get the expression that represents the entity that the invocation happens on
// (In this case this is the method name including possible class name, namespace, etc)
var invokedExpression = invocationExpression.Expression;
// Construct an expression for the new classname and methodname
var classNameSyntax = SyntaxFactory.IdentifierName("SecondClass");
var methodNameSyntax = SyntaxFactory.IdentifierName("ReplaceDoSomething");
var classNameAndMethodNameSyntax = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, classNameSyntax, methodNameSyntax);
// Add the surrounding trivia from the original expression
var replaced = classNameAndMethodNameSyntax;
replaced = replaced.WithLeadingTrivia(invokedExpression.GetLeadingTrivia());
replaced = replaced.WithTrailingTrivia(invokedExpression.GetTrailingTrivia());
// Replace the whole original expression with the new expression
return document.WithSyntaxRoot((await document.GetSyntaxRootAsync()).ReplaceNode(invokedExpression, replaced));
}
我正在做一个代码分析器,但我找不到一种方法来更改 class 和调用方法时正在访问的成员。例如,如果我有
public class FirstClass
{
public static void DoSomething(object SomeParameter)
{
//method body
}
}
public class SecondClass
{
public static void ReplaceDoSomething(object SomeParameter)
{
//other method's body
}
}
我怎样才能让我的代码修复更改 FirstClass.DoSomething(new object());
到 SecondClass.ReplaceDoSomething(new object());
?
我尝试使用 SyntaxFactory
但我无法真正弄清楚大多数方法需要哪些参数,MSDN 几乎没有提供有关它们的详细信息...
代码分析器
public override void Initialize(AnalysisContext context)
{
context.RegisterOperationAction(Analyze, OperationKind.Invocation);
}
private void Analyze(OperationAnalysisContext context)
{
IInvocationOperation operation = (IInvocationOperation)context.Operation;
if (operation.TargetMethod.Name == "DoSomething" && operation.TargetMethod.ContainingNamespace.Name == "FirstNamespace" && operation.TargetMethod.ContainingType.Inherits("FirstNamespace.FirstClass"))
{
//Rule is an automaticaly generated variable by visual studio when you create the analyzer
Diagnostic diagnostic = Diagnostic.Create(Rule, operation.Syntax.GetLocation());
context.ReportDiagnostic(diagnostic);
}
}
public static class INamedTypeSymbolExtensions
{
public static bool Inherits(this INamedTypeSymbol first, string otherFullName)
{
if (first.GetFullName() == otherFullName)
{
return true;
}
else if (typeof(object).FullName == first.GetFullName())
{
return false;
}
else
{
return first.BaseType.Inherits(otherFullName);
}
}
public static string GetFullName(this INamedTypeSymbol element)
{
StringBuilder ret = new StringBuilder(element.Name);
INamespaceSymbol ContainingNamespace = element.ContainingNamespace;
while (ContainingNamespace.ContainingNamespace != null)
{
ret.Insert(0, ContainingNamespace.Name + ".");
ContainingNamespace = ContainingNamespace.ContainingNamespace;
}
return ret.ToString();
}
}
编辑:我得到的最好结果是
private async Task<Document> ProvideCodeFix(Document document, InvocationExpressionSyntax invocation, CancellationToken cancellationToken)
{
SyntaxToken replaced = invocation.GetFirstToken();
SyntaxToken replacing = SyntaxFactory.IdentifierName("SecondClass").GetFirstToken().WithLeadingTrivia(replaced.LeadingTrivia));
return document.WithSyntaxRoot((await document.GetSyntaxRootAsync()).ReplaceToken(replaced, replacing);
}
有了这个,它会将 FirstClass.DoSomething(new object());
更改为 SecondClass.DoSomething(new object());
但这种方法的主要问题是,如果我必须将 FirstNamespace.FirstClass.DoSomething(new object());
更改为 SecondClass.ReplaceDoSomething(new object());
,那么它将不起作用
PS : 我用 .WithLeadingTrivia(...)
来保留评论和在 FirstClass
此代码有效。我们得到 InvocationExpressionSyntax.Expression
代表 ()
参数列表之前的整个部分。然后我们替换整个表达式。
private async Task<Document> ProvideCodeFix(Document document, InvocationExpressionSyntax invocationExpression, CancellationToken cancellationToken)
{
// Get the expression that represents the entity that the invocation happens on
// (In this case this is the method name including possible class name, namespace, etc)
var invokedExpression = invocationExpression.Expression;
// Construct an expression for the new classname and methodname
var classNameSyntax = SyntaxFactory.IdentifierName("SecondClass");
var methodNameSyntax = SyntaxFactory.IdentifierName("ReplaceDoSomething");
var classNameAndMethodNameSyntax = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, classNameSyntax, methodNameSyntax);
// Add the surrounding trivia from the original expression
var replaced = classNameAndMethodNameSyntax;
replaced = replaced.WithLeadingTrivia(invokedExpression.GetLeadingTrivia());
replaced = replaced.WithTrailingTrivia(invokedExpression.GetTrailingTrivia());
// Replace the whole original expression with the new expression
return document.WithSyntaxRoot((await document.GetSyntaxRootAsync()).ReplaceNode(invokedExpression, replaced));
}