Single vsix for both VB and C# Diagnostic Analysis

Topics: APIs, General
Jun 23, 2014 at 6:14 PM
I wanted to know if it is possible to have both C# and VB rules be implemented in the same solution and be made into a single vsix extension.
I tried the same but got some compilation isssues.

If this is not actually feasible, Is there any workarounds to achieve the same (Both the rulesets being implemented using a single vsix extension)
Developer
Jun 27, 2014 at 6:06 PM
It is possible to do this - what compilation issues did you run into?

Note that we try to avoid such an extension because once such an extension is installed, opening a C# projects loads the VB compiler in memory as well and leads to more memory consumption. If your analysis doesn't depend on the syntax itself but is just analyzing symbols, then you could simply have a single language-agnostic analyzer. If you need to be language specific then in that case having two language-specific analyzers doesn't sound very weird. We've been thinking more about how we can have more language-agnostic analyzers written and if you have a case that falls in the middle, that'll be good to know as well.
Marked as answer by jerriclynsjohn on 8/12/2014 at 8:47 AM
Aug 12, 2014 at 3:46 PM
This issue was not reproducible after this reporting. But since you mentioned about the possible memory consumption if we include both the C#/VB Diagnostic Analyzer together into a package, how is that possible when we filter the diagnostic analyzer based on the attribute
[ExportDiagnosticAnalyzer(DiagnosticId, LanguageNames.VisualBasic)]
[ExportDiagnosticAnalyzer(DiagnosticId, LanguageNames.CSharp)]
What is the processes of invoking the respective compilers based on the these attribute/Is the compilers invoked based on these attributes? How does this affect the performance, since only one compiler is working.

Also is it possible to package this project as part of a menu command extension for Visual Studio.
Developer
Aug 12, 2014 at 5:41 PM
The compiler is not the problem since the process goes away after the compilation is done. In visual studio if one opens a C# project and adds an analyzer that brings in the VB dlls then VB will be loaded in memory. True, the Export attribute will determine which analyzers are instantiated but you have to be very careful about not using any method which can refer to both kinds of syntax nodes (say) such that the JIT doesn't feel the need to compile some methods (and hence cause a dll load). At that point, you're depending on the JIT's implementation details and it's very easy to make a mistake. So it's easier to just split the analyzers into separate dlls for C# and VB.

You can use VS's menu extensibility to invoke these analyzers if you wish but that would be VS specific. If you add an analyzer to the project then it participates in build as well (outside of VS through msbuild too).
Aug 12, 2014 at 6:57 PM
I'm gonna outline my doubts based on your explanation:
In visual studio if one opens a C# project and adds an analyzer that brings in the VB dlls then VB will be loaded in memory
  1. If I'm loading a diagnostic analyzer that was written for both C# and VB (written in C# itself using their specific APIs) of the same assembly, I'm guessing its the same assembly that's getting loaded too. But as a matter of fact that doesn't matter (correct me!) since the MEF export only initializes the required analyzer according to the mentioned attribute.
  2. What is VB? Did you mean the VB compiler, because if that's the case then it will also seize into existence once the compilation is done I guess. Also how will a VB compiler be called when all I require is to load the correct analyzer according to the mentioned attribute
The Export attribute will determine which analyzers are instantiated but you have to be very careful about not using any method which can refer to both kinds of syntax nodes (say) such that the JIT doesn't feel the need to compile some methods (and hence cause a dll load). At that point, you're depending on the JIT's implementation details and it's very easy to make a mistake.
Could you please explain a little bit more, I'm kindda confused now since I'm not aware of methods that can refer to both kinds of syntax nodes. JIT must compile every methods right, or are you mentioning about some highly rare exception that might be caused because of complex API usages, I'm not aware of such a complexity in all honesty. It will be really appreciated if you could explain a bit more on this.
You can use VS's menu extensibility to invoke these analyzers
So if I go right ahead and design a menu command functionality other than analyzer capabilities and alongside if I create diagnostic analyzers in the normal way, they all should coexist right? Hopefully I can package up everything in a single vsix.
Developer
Aug 12, 2014 at 7:27 PM
Let me try to clarify my original statement: If your analyzer references Microsoft.CodeAnalysis.CSharp and Microsoft.CodeAnalysis.VisualBasic, then without extra care, there's a high likelihood that when the analyzer is loaded into VS process space both of the above mentioned dlls will get loaded into the VS process space as well. This is wasteful because Microsoft.CodeAnalysis.VisualBasic (which is the heart of the VB compiler) is not necessary if the solution contains only C# projects (and if you never open VB projects in the same VS session).

By methods dealing with multiple nodes all I was trying to say was a method where you say like: if (node.Language == "C#") do something with a C# syntax node else do something with a VB syntax node - to JIT that method, the VB syntax node types (which live in Microsoft.CodeAnalysis.VisualBasic,dll) is going to get loaded in memory. Does that help?
So if I go right ahead and design a menu command functionality other than analyzer capabilities and alongside if I create diagnostic analyzers in the normal way, they all should coexist right? Hopefully I can package up everything in a single vsix.
Are you asking if the menu command extensibility and analyzer extensibility are related in any way? If so, no they are not and one VSIX should be able to export multiple components for extending both of those extension points.
Marked as answer by jerriclynsjohn on 8/12/2014 at 12:51 PM
Aug 12, 2014 at 7:51 PM
That perfectly makes sense, thank you. So I'm going with the concept of separate projects for C# and VB Diagnostic Analyzers.
if (node.Language == "C#") do something with a C# syntax node else do something with a VB syntax node - to JIT that method, the VB syntax node types (which live in Microsoft.CodeAnalysis.VisualBasic,dll) is going to get loaded in memory. Does that help?
That just clarified a huge cloud of confusion. So you are telling me if I try to make a generic diagnostic analyzer it might wreak havoc, or in a lighter sense cause performance issues since the other is getting loaded. Or does it really wreak havoc if such generic analyzers were implemented? :)

And no I was not asking if those extensions were related somehow, but I was curious if these extensions could coexist. Good to know that we can export multiple components to those extension points and not cause any coexistence problem.
Developer
Aug 12, 2014 at 7:54 PM
No there's no havoc. It'll work fine but VS will just be consuming more memory. For the trivial cases that might not matter much but it'll likely matter for large solutions.
Aug 12, 2014 at 8:25 PM
This is perfect and I've decided on to use two separate projects and package it under the same VSIX. In that way I will have separate dlls for C# and VB, thereby zeroing in on the possibility of invoking the CodeAnalysis dll of the other language. I hope that should work perfectly fine and remove those performance issue.

As a matter of fact about the other menu command, I was talking about a command that will update the installed analyzer extension if there is a new one available in my remote server. This will be based on the VS SDK and has some OLE Commands and Interop invocations to do a version check and others prior to download and installation.

This should work I guess and must coexist in the same analyzer project of each.
Aug 13, 2014 at 6:16 AM
if (node.Language == "C#") do something with a C# syntax node else do something with a VB syntax node - to JIT that method, the VB syntax node types (which live in Microsoft.CodeAnalysis.VisualBasic,dll) is going to get loaded in memory.
Does this mean that the compiler is loaded into the memory only once the language specific methods are invoked or will both the compilers be loaded along with the assembly export since we refer to both the Microsoft.CodeAnalysis.CSharp and Microsoft.CodeAnalysis.VisualBasic in the project itself.
Aug 13, 2014 at 4:12 PM
you could simply have a single language-agnostic analyzer
How can we build a language-agnostic analyzer when every MEF export has a language specific attribute?
Developer
Aug 13, 2014 at 6:02 PM
Does this mean that the compiler is loaded into the memory only once the language specific methods are invoked or will both the compilers be loaded along with the assembly export since we refer to both the Microsoft.CodeAnalysis.CSharp and Microsoft.CodeAnalysis.VisualBasic in the project itself.
The assemblies are loaded when they are needed. If you are opening a C# project, even before an analyzer is loaded, the C# compiler will get loaded for other purposes like colorizing the file, intellisense etc. The VB dll will not be loaded since it's not needed. If your analyzer refers to a type from the VB dll, then that gets loaded into the process space by the CLR when it JITs that method - whenever that happens to be.
How can we build a language-agnostic analyzer when every MEF export has a language specific attribute?
If you build a ISymbolAnalyzer, where you are analyzing just the symbols and not the syntax nodes, then you don't need to reference the C# or VB dlls. In those cases, the analyzer is language-agnostic. You could put two export attributes - one for each language. The attribute is just metadata to tell us what language the analyzer is applicable to and so when we are analyzing a C# solution, we'll know that this symbol analyzer is eligible for C# and instantiate it and call it.
Marked as answer by jerriclynsjohn on 8/13/2014 at 2:21 PM
Aug 13, 2014 at 9:49 PM
This definitly means that
  1. if I'm using a single project that refers to both the VB and C# dll, and
  2. if I'm seperating my analyzers from C# and VB according to their functionality, and
  3. if i'm only using these individual analyzer to use individual language types (C# uses C# types, and VB uses VB types)
Then if I'm not wrong, the performance issue will not be an issue because:
  • When analyzing C# solution, only C# compiler will be loaded (eventhough VB is refered but not used in any of the C# MEF exports), vice-versa.
Do reply if I'm wrong
Nov 21, 2014 at 1:04 AM
I'm trying to create a shared analyzer with the duplicate extension attributes as mentioned above in VS 2015 beta, but am getting the error, "Duplicate 'ExportDiagnosticAnalyzer' attribute" (code CS0579). The analyzer is a named type symbol analyzer, so the additional overhead of the compiler load shouldn't apply in this case at least.
Developer
Nov 21, 2014 at 1:08 AM
When you say VS 2015 beta, do you mean the preview release? Are you using the nuget packages for the preview release? The ExportDiagnosticAnalyzerAttribute type has been renamed to DiagnosticAnalyzerAttribute and so I'm wondering if you're using some older version.
Nov 21, 2014 at 1:17 AM
Thanks for the catch. It was in a project that I upgraded from earlier CTP's and didn't have the refreshed DLL's.
Jan 17, 2015 at 12:43 PM
I have a similar requirement. If I am loading Diagnostic Analyzers through MEF , Is it enough to just initialize the composition container in a VSPackage MenuItemCallback method.
string path = @"DiagnosticAlalyzerDLLs**";
        catalog.Catalogs.Add(new DirectoryCatalog(path));


        //Create the CompositionContainer with the parts in the catalog
        _container = new CompositionContainer(catalog);

        //Fill the imports of this object
        try
        {
            this._container.ComposeParts(this);
        }
I am trying the above approach..Its not working .
Please suggest the solution.
Developer
Jan 21, 2015 at 12:50 AM
I am not sure I understand your scenario. Are you trying to get all the analyzers in an assembly and run them somehow? The DiagnosticAnalyzerAttribute is not a MEF attribute? The compiler is not a MEF container and we simply identify the types using a metadata reader and instantiate them.
Jan 21, 2015 at 4:43 AM
Edited Jan 21, 2015 at 4:52 AM
My reuirement is there will be several Refactoring DLLs (created without using Roslyn templates but same CodeAnalysis APIs ) in a directory and these have to be loaded into Visual Studio programmatically using MEF. My question is : Is it possible to load the refactoring DLLs programmatically e.g. on click of a VS Package Tools menu click so that refactoring works as usual and one can add new DLLs as desired. If possible please let me know how I can achive this. These can be deployed into VS using VSIX but I have to do it programmatically.