我正在使用roslyn开发一个代码修复程序,以修复无效的throw表达式。一般而言,该代码修补程序会执行它应该做的事情-但是它使我的代码格式错误,将在下一个屏幕截图中显示
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(DoNotRethrowCodeFixProvider)), Shared]
public class DoNotRethrowCodeFixProvider : CodeFixProvider
{
public sealed override ImmutableArray<string> FixableDiagnosticIds
{
get { return ImmutableArray.Create(DoNotRethrowAnalyzer.DiagnosticId); }
}
public sealed override FixAllProvider GetFixAllProvider()
{
return WellKnownFixAllProviders.BatchFixer;
}
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
foreach (var diagnostic in context.Diagnostics)
{
context.RegisterCodeFix(
CodeAction.Create(
Resources.DoNotRethrowTitle, c => FixDoNotRethrowRule(context, diagnostic, c), Resources.DoNotRethrowTitle),
diagnostic);
}
}
private async Task<Document> FixDoNotRethrowRule(CodeFixContext context, Diagnostic diagnostic, CancellationToken cancellationToken)
{
var root = await context.Document.GetSyntaxRootAsync(cancellationToken);
if (root.FindNode(diagnostic.Location.SourceSpan) is ThrowStatementSyntax throwSyntax)
{
var newThrowStatement = SyntaxFactory.ThrowStatement()
.WithLeadingTrivia(throwSyntax.ThrowKeyword.LeadingTrivia)
.WithTrailingTrivia(throwSyntax.SemicolonToken.TrailingTrivia);
var rewritten = root.ReplaceNode(throwSyntax, newThrowStatement);
return context.Document.WithSyntaxRoot(rewritten);
}
return context.Document;
}
}
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class DoNotRethrowAnalyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = DiagnosticIds.DoNotRethrowAnalyzer.DoNotRethrowRule;
// You can change these strings in the Resources.resx file. If you do not want your analyzer to be localize-able, you can use regular strings for Title and MessageFormat.
// See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/Localizing%20Analyzers.md for more on localization
private static readonly LocalizableString Title = new LocalizableResourceString(nameof(Resources.DoNotRethrowTitle), Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableString MessageFormat = new LocalizableResourceString(nameof(Resources.DoNotRethrowMessageFormat), Resources.ResourceManager, typeof(Resources));
private static readonly LocalizableString Description = new LocalizableResourceString(nameof(Resources.DoNotRethrowDescription), Resources.ResourceManager, typeof(Resources));
private const string Category = "Debuggability";
private static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Warning,
isEnabledByDefault: true,
description: Description);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzeThrowExpression, SyntaxKind.ThrowStatement);
}
private void AnalyzeThrowExpression(SyntaxNodeAnalysisContext context)
{
if (context.Node is ThrowStatementSyntax throwSyntax)
{
if (throwSyntax.Expression == null)
return;
if(throwSyntax.Expression is IdentifierNameSyntax throwIdentifier
&& ThrowIdentifierIsIdentifierOfParentClause(throwIdentifier))
context.ReportDiagnostic(Diagnostic.Create(DoNotRethrowAnalyzer.Rule, throwSyntax.ThrowKeyword.GetLocation()));
}
}
private bool ThrowIdentifierIsIdentifierOfParentClause(IdentifierNameSyntax throwIdentifier)
{
var parentCatch = throwIdentifier.NextParentOfType<CatchClauseSyntax>();
if (parentCatch == null || parentCatch.Declaration == null)
return false;
return parentCatch.Declaration.Identifier.ValueText == throwIdentifier.Identifier.ValueText;
}
}
[TestClass]
public class DoNotRethrowTests : CodeFixVerifier
{
//No diagnostics expected to show up
[TestMethod]
public void TestEmptyFile()
{
var test = @"";
VerifyCSharpDiagnostic(test);
}
[TestMethod]
public void InvalidRethrow()
{
var test = @"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
namespace ConsoleApplication1
{
class TYPENAME
{
void Offender(){
throw new Exception(""testa"");
}
void Process(Exception e){
throw new Exception(""testa"");
}
void Source()
{
try
{
Offender();
}
catch (Exception e)
{
Process(e);
throw e;
}
}
}
}";
var expected = new[]
{
new DiagnosticResult
{
Id = DoNotRethrowAnalyzer.DiagnosticId,
Message = Resources.DoNotRethrowMessageFormat,
Severity = DiagnosticSeverity.Warning,
Locations =
new[]
{
new DiagnosticResultLocation("Test0.cs", 30, 6)
}
}
};
VerifyCSharpDiagnostic(test, expected);
var expectedFix = @"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
namespace ConsoleApplication1
{
class TYPENAME
{
void Offender(){
throw new Exception(""testa"");
}
void Process(Exception e){
throw new Exception(""testa"");
}
void Source()
{
try
{
Offender();
}
catch (Exception e)
{
Process(e);
throw;
}
}
}
}";
VerifyCSharpFix(test, expectedFix);
}
class TYPENAME
{
void Offender()
{
throw new Exception("testa");
}
void Process(Exception e)
{
throw new Exception("testa");
}
void Source()
{
try
{
Offender();
}
catch (Exception e)
{
Process(e);
throw;
}
}
}
//Diagnostic and CodeFix both triggered and checked for
[TestMethod]
public void ValidRethrow()
{
var test = @"
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
namespace ConsoleApplication1
{
class TYPENAME
{
void Offender(){
throw new Exception(""testa"");
}
void Source()
{
try
{
Offender();
}
catch (Exception e)
{
throw new Exception(""test"", e);
}
}
}
}";
var expected = new DiagnosticResult[0];
VerifyCSharpDiagnostic(test, expected);
}
protected override CodeFixProvider GetCSharpCodeFixProvider()
{
return new DoNotRethrowCodeFixProvider();
}
protected override DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer()
{
return new DoNotRethrowAnalyzer();
}
}
有人知道我在做什么错吗?我曾尝试使用DocumentEditor进行此代码修复(通常不会像这样给我带来麻烦),但是在那里我遇到了同样的问题。
答案 0 :(得分:0)
更改一些类似于以下代码的方法,但是我不喜欢它。似乎奇怪的是,正确保存琐事只会这样工作。
if (root.FindNode(diagnostic.Location.SourceSpan) is ThrowStatementSyntax throwSyntax)
{
var newThrowStatement = throwSyntax
.WithExpression(SyntaxFactory.ParseExpression(""));
var editor = await DocumentEditor.CreateAsync(context.Document, context.CancellationToken);
editor.ReplaceNode(throwSyntax, newThrowStatement);
return editor.GetChangedDocument();
}