Notify Visual Studio of code called through reflection

Viewed 201

Context:

The environment I work in has a lot of 'magic' methods and fields that are called or set through reflection from external code. Something may have an attribute that means it will be set to a non-default value, but Visual Studio still doesn't see that aspect and "helpfully" provides the warning.

Since these are handled using attributes and specifically named methods, ideally I'd like to provide VS with additional information so that it knows that it's called or set, without me having to manually suppress each warning.

I've looked into writing a Roslyn analyzer, but from what I can tell I can only add additional warnings, not modify existing warnings/reference count.

Examples:

[MyCmpGet] private Component comp

"Field is never assigned to and will always have its default value null"

However the field is assigned through reflection due to the annotation.

[HarmonyPatch]
class Patch
{
     static void Postfix() {}
}

"Private member is unused"
"0 references"

However the method is called through reflection, due to the annotation on the class, and the method having a specific name.

Question:

What is the best way to let visual studio know that these fields are being set, and that these methods are being referenced? Preferably without requiring me to take manual actions for each one, and without adding anything additional to the example code.

1 Answers

You can implement a DiagnosticSuppressor analyzer. The default analyzer templates should produce a nuget package that you can include in all projects where you want to suppress this warning.

here is and example of what a diagnostic suppressor looks like at its most basic form:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using System.Collections.Immutable;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class DiagnosticSuppressorForAssignmentWarnings : DiagnosticSuppressor {
    public SuppressionDescriptor SuppressionDescriptor => new SuppressionDescriptor(
        id: "SPR0001", // Id for this analyzer suppressor (You should come up with a unique name for yours)
        suppressedDiagnosticId: "CS0649", // The warning that we may want to suppress
        justification: "This is ok because it is assigned via reflection");

    public override ImmutableArray<SuppressionDescriptor> SupportedSuppressions
        // You can pass in multiple suppression descriptors to have this suppress multiple types of warnings
        => ImmutableArray.Create(SuppressionDescriptor);

    public override void ReportSuppressions(SuppressionAnalysisContext context) {
        foreach (var diagnostic in context.ReportedDiagnostics) {
            // The parsed syntax tree of the file that this warning comes from
            var syntaxTree = diagnostic.Location.SourceTree;
            // The syntax Node that the warning was reported for
            var nodeWithWarning = syntaxTree.GetRoot().FindNode(diagnostic.Location.SourceSpan);
            // A semantic model that can answer questions like 'does this attribute inherit from this type' etc.
            var semanticModel = context.GetSemanticModel(syntaxTree);

            // You can do additional analysis of the source to ensure this is something 
            // that is semantically safe to suppress (like check for an attribute)

            context.ReportSuppression(Suppression.Create(SuppressionDescriptor, diagnostic));
        }
    }
}

For more documentation on how to write analyzers I would take a look at this blog post or this documentation

Related