Search code examples
c#.netvisual-studioroslynroslyn-code-analysis

Roslyn C# semantic analyzer for naming correctness passes in build but errors in Intellisense for specific cases


I have a custom roslyn analyzer which enforces naming rules based on certain implementation details of a method in its parent class, i.e.

public abstract class ChildEntity : ParentEntity;

public abstract class ParentEntity
{
     protected void IsAwesome()
     {
          ...
     }
}

In this case, my analyzer would create a Diagnostic, something like "ChildEntity should be renamed to begin with "Awesome" because it inherits from an Awesome Parent". This rule and diagnostic function correctly, and create errors both in code analysis Intellisense and when trying to build the project which ChildEntity and ParentEntity belong to. However, if the ParentEntity lives in a different Project than the ChildEntity, and is referred to via PackageReference, only Intellisense is showing the error, but the project build passes. Why would the location of the ParentEntity affect this? I need it to fail the build regardless of which project ParentEntity lives in.

My semantic analyzer checks the NamedType symbol (like ChildEntity), finds its parent class, and from there evaluates its methods if it finds any. If it needs to create an error, it creates it at the ChildEntity class location, and only there.

At first I did not understand for which cases builds were passing/failing but Intellisense errors were always showing. I tried to force Analyzers to be run using various props in the .csproj file and Director.build.props but nothing produced any different results. Finally after I noticed that the location of the parent entity was the culprit, I created a small test project to see if the behavior was consistent in a simpler form, and it was. I'm not sure if the roslyn compilation differs between Intellisense and the actual build, it seems like it is. Verbose outputs of the build show CoreCompile being run for each case. Other analyzers such as StyleCop are also running for each case.

My Analyzer is installed as a nuget package, not a VSIX install. The Analyzer Target Framework Version is .Net Standard 2.0, and I'm using the analyzer in projects running the same, and .Net Framework 4.7.2


Solution

  • Roslyn analyzers only have access to the syntax tree in the Source Compilation, therefore my analyzer when checking the symbol of an external INamedTypeSymbol (the parent class from outside the project which the analyzer was running) could not call the DeclaringSyntaxReferenced property for method inspection. This property would yield null or an empty array, and thus no error would be thrown. Since the parent class symbols are coming in as "Metadata" through a referenced assembly or project reference, they don't have an syntax information exposed once they are packaged or compiled into a library or DLL. However Intellisense picks up the syntax information in the IDE, and is able to generate the desired diagnostic.

    So the work-around solution I implemented is to check for the symbol properties IsInMetadata and IsInSource and handle the Build case (when IsInMetadata is TRUE and IsInSource is FALSE) to check the name of the parent class that it starts with "Awesome" rather than trying to syntactically analyze the "IsAwesome" method. In the flip case, such as for Intellisense (when IsInMetadata is FALSE and IsInSource is TRUE) we do perform the syntactical analysis since we have access to the syntax tree in the Source Compilation.