c# - Roslyn, can not get provider to update solution after codefix -
i have created analyzer detect if method not contain <createddate>
tag in xml , provider inject tag. works fine , inserts tag still returns build error though rule has been fixed. think need use semantic models possibly?
this have: 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 { { return immutablearray.create(newanalyzeranalyzer.diagnosticid); } } public sealed override fixallprovider getfixallprovider() { // see https://github.com/dotnet/roslyn/blob/master/docs/analyzers/fixallprovider.md more information on fix 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 api endpoint."; var equivalencekey = "empty string"; context.registercodefix(codeaction.create(description, cancellationtoken => createchangeddocument(context, methoddeclarationsyntax, cancellationtoken), equivalencekey), diagnostic); return task.fromresult(0); } /// <summary> /// create new method contain changes required insert createddate tag comments. /// </summary> /// <param name="context">context</param> /// <param name="methoddeclarationsyntax">method declaration syntax</param> /// <param name="cancellationtoken">cancellation token</param> /// <returns>new method contains createddate in 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 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; } } }
and 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"; // can change these strings in resources.resx file. if not want analyzer localize-able, can use regular strings title , messageformat. // see https://github.com/dotnet/roslyn/blob/master/docs/analyzers/localizing%20analyzers.md more on localization private static readonly localizablestring title = "title: createddate missing"; private static readonly localizablestring messageformat = "format: createddate missing"; private static readonly localizablestring description = "desc: createddate 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 { { return immutablearray.create(rule); } } public override void initialize(analysiscontext context) { // todo: consider registering other actions act on syntax instead of or in addition symbols // see https://github.com/dotnet/roslyn/blob/master/docs/analyzers/analyzer%20actions%20semantics.md more information //context.registersymbolaction(analyzesymbol, symbolkind.namedtype); context.registersymbolaction(analyzemethod, symbolkind.method); } /// <summary> /// analyze method see if it's 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); } } } } }
any ideas how let build pass when tag present in xml? seems not updating solution?
context.registersymbolaction(analyzemethod, symbolkind.method)
execute callback property getters , setters too. codefix failing on properties, because can't find methoddeclarationsyntax
reported location.
why did implement functionality symbol based analyzer? syntax node based one, , can subscribe methoddeclaration
s.
also, in codefix documentationcomment
can null
, , content
of it, can fail too.
other these codefix adds current time.
these come when start debugging extension. maybe have @ exception settings window break on clr exceptions.
Comments
Post a Comment