How can I make my code diagnostic syntax node action work on closed files?
Asked Answered
P

1

73

I'm building a set of code diagnostics using Roslyn (in VS2015 Preview). Ideally, I'd like any errors they produce to act as persistent errors, just as if I were violating a normal language rule.

There are a bunch of options, but I'm having a hard time getting any of them to work consistently. I've managed to implement a rudimentary syntax node action, i.e. one registered with

context.RegisterSyntaxNodeAction(AnalyzeSyntaxNode, SyntaxKind.InvocationExpression);

in the Initialize method of my diagnostic class. Lo and behold, when I open up a file which violates this diagnostic (while running the VSIX project), VS2015 shows an error:

  • Red squiggle under the right bit of code
  • Red block in the margin
  • Error in the error list

However, the error goes away when I close the file.

I've tried using context.RegisterCompilationEndAction as well, but this has two problems:

  • It seems to fire inconsistently. Usually when I open the solution it fires, but not always. It doesn't fire on a clean/rebuild, which seems odd.
  • Although diagnostics created directly in the analysis method fire, in order to implement the diagnostics I'm using a visitor, like this - which may be inept:

    private static void AnalyzeEndCompilation(CompilationEndAnalysisContext context)
    {
        foreach (var tree in context.Compilation.SyntaxTrees)
        {
            var visitor = new ReportingVisitor(context.Compilation.GetSemanticModel(tree));
            visitor.Visit(tree.GetRoot());
            foreach (var diagnostic in visitor.Diagnostics)
            {
                context.ReportDiagnostic(diagnostic);
            }
        }
    }
    

    I know that the diagnostics are being created - a breakpoint on the ReportDiagnostic line is hit several times - but I'm not seeing anything in the error list. (Whereas a similar ReportDiagnostic call at the start of the method, or one per syntax tree with the file path, does get shown.)

What am I doing wrong here? The first approach (a syntax node action) would be ideal if feasible - it gives me exactly the context I need. Is there some setting in the project properties that I need to make the compiler use that for "full project" compilation as well as just interactive "in the IDE" handling? Is this perhaps just a bit of Roslyn integration which isn't quite finished yet?

(I can include the full code for the class if it would be useful - in this case I suspect it would be more noise than signal though.)

Phlogistic answered 8/12, 2014 at 19:58 Comment(1)
From what I understood at a talk RegisterCompilationEndAction is indeed what you need and the fact it fires inconsistently is a bug. I'll email the speaker and ask.Touchstone
B
46

For the closed file issues, it's our intent that all diagnostics will be reported, from either open or closed files. There is a user option for it in the preview at Tools\Options\Text Editor\C#\Advanced that you can toggle to include diagnostics in closed files. We hope to make this the default before VS 2015 is released. However, note that the option only applies to analysis within VS. If your analyzer is passed to the compiler (by adding an analyzer in Solution Explorer, or adding a NuGet package reference to a package with an analyzer, as opposed to installing a VSIX into Visual Studio), then the compiler will report all diagnostics when the user builds, regardless of whether the files are open or not.

For the second issue with RegisterCompilationEndedAnalyzer, it isn't reliably called inside Visual Studio in the VS 2015 Preview. This is because we do some optimizations to avoid re-analyzing everything for "local" changes inside method bodies. For similar reasons, we currently don't report errors that are reported with locations in method bodies. We've just recently changed that so that VS will kick off a full re-analysis after a longer delay and so RegisterCompilationEndedAnalyzer should be called reliably in future builds, and we will report errors regardless of location.

However, for your case the correct thing to do is stay with a SyntaxNodeAnalyzer, switch the VS option to enable diagnostics in closed files, and attach your diagnostic to the project compilation options.

Hope this helps!

Bemis answered 8/12, 2014 at 20:37 Comment(4)
Aha - the location part would definitely explain the oddities I was seeing. My "just check that I can get something working" diagnostics didn't have a location, so those ones showed up... and the option for diagnostics in closed files worked perfectly. Thanks! Back to making the diagnostic itself do the right thing now :)Phlogistic
Did you see the note about using the project attached diagnostics to get them into command line build too?Bemis
Yep. So long as I'm on the right track to do that later, I can punt on it for now :)Phlogistic
@KevinPilch This appears to still default to being off for VS2017.4 and was very surprising to me. Any word on whether this will be changed?Prostrate

© 2022 - 2024 — McMap. All rights reserved.