Ich habe einen Analysator erstellt, der erkennt, ob eine Methode kein <createddate>
-Tag im XML-Code und einen Provider enthält, der dieses Tag injiziert. Es funktioniert gut und fügt das Tag ein, aber es wird immer noch mit Erstellungsfehler zurückgegeben, obwohl die Regel behoben wurde. Ich denke, ich muss möglicherweise semantische Modelle verwenden?Roslyn, Provider kann keine Lösung nach Codefix aktualisieren
Das ist, was ich habe: CodeFixProvider.cs
using System;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Simplification;
using Microsoft.CodeAnalysis.Rename;
namespace NewAnalyzer
{
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(NewAnalyzerCodeFixProvider)), Shared]
public class NewAnalyzerCodeFixProvider : CodeFixProvider
{
private const string title = "Add Createddate";
public sealed override ImmutableArray<string> FixableDiagnosticIds
{
get { return ImmutableArray.Create(NewAnalyzerAnalyzer.DiagnosticId); }
}
public sealed override FixAllProvider GetFixAllProvider()
{
// See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/FixAllProvider.md for more information on Fix All Providers
return WellKnownFixAllProviders.BatchFixer;
}
public sealed override Task RegisterCodeFixesAsync(CodeFixContext context)
{
var diagnostic = context.Diagnostics.First();
var diagnosticSpan = diagnostic.Location.SourceSpan;
SyntaxNode root;
context.Document.TryGetSyntaxRoot(out root);
var syntax = root.FindNode(diagnostic.Location.SourceSpan);
var methodDeclarationSyntax = syntax.FirstAncestorOrSelf<MethodDeclarationSyntax>();
var description = "Add created datetime for API endpoint.";
var equivalenceKey = "empty string";
context.RegisterCodeFix(CodeAction.Create(description, cancellationToken => CreateChangedDocument(context, methodDeclarationSyntax, cancellationToken), equivalenceKey), diagnostic);
return Task.FromResult(0);
}
/// <summary>
/// Create a new method that will contain the changes required to insert a createddate tag into the comments.
/// </summary>
/// <param name="context">context</param>
/// <param name="methodDeclarationSyntax">method declaration syntax</param>
/// <param name="cancellationToken">cancellation token</param>
/// <returns>new method that contains createddate in the comments</returns>
private static async Task<Document> CreateChangedDocument(CodeFixContext context, MethodDeclarationSyntax methodDeclarationSyntax, CancellationToken cancellationToken)
{
var originalTree = await context.Document.GetSyntaxTreeAsync(cancellationToken);
var newTree = await context.Document.GetSyntaxTreeAsync(cancellationToken);
var root = await newTree.GetRootAsync(cancellationToken);
var documentationComment = methodDeclarationSyntax.GetLeadingTrivia().Select(i => i.GetStructure()).OfType<DocumentationCommentTriviaSyntax>().FirstOrDefault();
var summaryElement = (XmlElementSyntax)documentationComment.Content.FirstOrDefault(x => x is XmlElementSyntax); // works
if (documentationComment == null)
return context.Document;
var newLineText = SyntaxFactory.XmlTextNewLine(SyntaxFactory.TriviaList(), Environment.NewLine, Environment.NewLine, SyntaxFactory.TriviaList());
var createdDateText =
SyntaxFactory.XmlText(SyntaxFactory.TokenList(
SyntaxFactory.XmlTextLiteral(
SyntaxFactory.TriviaList(),
DateTime.UtcNow.ToString("d"),
DateTime.UtcNow.ToString("d"),
SyntaxFactory.TriviaList())
));
var textList = SyntaxFactory.List<XmlNodeSyntax>(new[] { createdDateText });
var createdDateNode = new XmlNodeSyntax[]
{
SyntaxFactory.XmlText().AddTextTokens(SyntaxFactory.XmlTextNewLine(SyntaxFactory.TriviaList(), Environment.NewLine, Environment.NewLine, SyntaxFactory.TriviaList())),
SyntaxFactory.XmlElement(SyntaxFactory.XmlElementStartTag(SyntaxFactory.XmlName("createddate")).WithLeadingTrivia(SyntaxFactory.DocumentationCommentExterior("/// ")),
textList,
SyntaxFactory.XmlElementEndTag(SyntaxFactory.XmlName("createddate"))).WithAdditionalAnnotations(Formatter.Annotation, Simplifier.Annotation)
};
var list = SyntaxFactory.List<XmlNodeSyntax>(createdDateNode);
SyntaxNode tempNode = documentationComment.InsertNodesAfter(summaryElement, list);
var newRoot = root.ReplaceNode(documentationComment, tempNode);
var semanticModel = await context.Document.GetSemanticModelAsync(cancellationToken);
var typeSymbol = semanticModel.GetDeclaredSymbol(methodDeclarationSyntax, cancellationToken);
var semModel = await context.Document.GetSemanticModelAsync();
var compilation = semModel.Compilation.ReplaceSyntaxTree(originalTree, newTree);
var oldSemModel = await context.Document.GetSemanticModelAsync();
oldSemModel = semanticModel;
return context.Document;
}
}
}
Und DiagnosticAnalyzer.cs
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
namespace NewAnalyzer
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class NewAnalyzerAnalyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = "NA001";
// 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 = "Title: createddate is missing";
private static readonly LocalizableString MessageFormat = "Format: createddate is missing";
private static readonly LocalizableString Description = "Desc: createddate is missing";
private const string Category = "Naming";
private static DiagnosticDescriptor Rule = new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }
public override void Initialize(AnalysisContext context)
{
// TODO: Consider registering other actions that act on syntax instead of or in addition to symbols
// See https://github.com/dotnet/roslyn/blob/master/docs/analyzers/Analyzer%20Actions%20Semantics.md for more information
//context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType);
context.RegisterSymbolAction(AnalyzeMethod, SymbolKind.Method);
}
/// <summary>
/// Analyze method to see if it's a REST endpoint method
/// </summary>
/// <param name="context">context</param>
private static void AnalyzeMethod(SymbolAnalysisContext context)
{
var methodDeclarationNode = context.Symbol.GetDocumentationCommentXml();
if (methodDeclarationNode != null)
{
if (!methodDeclarationNode.Contains("createddate"))
{
var diagnostic = Diagnostic.Create(Rule, context.Symbol.Locations[0], methodDeclarationNode);
context.ReportDiagnostic(diagnostic);
}
}
}
}
}
Irgendwelche Ideen, wie die Build verstreichen zu lassen, wenn der Tag in der XML vorhanden ist ? Scheint es nicht wirklich die Lösung zu aktualisieren?
das funktioniert nicht. Wenn Sie es als Methodendeklaration angeben, wird es nur als IDE-Fehler und nicht als Buildfehler zurückgegeben. Sollte dies ein semantischerModelAnalysis-Kontext sein? –
Sorry, ich verstehe nicht, was nicht funktioniert. Könnten Sie das Problem ein wenig mehr erklären? – Tamas
Wenn ich Ihnen den Code senden soll, verursacht die Verwendung von methoddeclarationsyntax keinen Build-Fehler. Gibt es eine Möglichkeit, den Code zu teilen? –