Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SymbolInfo of extension method

Tags:

c#

.net

roslyn

I need to analyze some extension method. For example Enumerable.ToList.

Code sample to analyze:

var test = @"
using System.Linq;

namespace Test
{
    public class TestType
    {
        void TestMethod()
        {
            var empty = new[] {0};
            var test = empty.ToList();
        }
    }
}";

Diagnostic:

public override void Initialize(AnalysisContext context)
{
    context.RegisterSyntaxNodeAction(AnalyzeSymbol, SyntaxKind.InvocationExpression);
}

private static void AnalyzeSymbol(SyntaxNodeAnalysisContext context)
{
    var symbolInfo = context.SemanticModel.GetSymbolInfo(context.Node);
}

However symbolInfo.Symbol is null and there are no any candidates. If I change the code sample like that:

var test = @"
using System.Linq;

namespace Test
{
    public class TestType
    {
        void TestMethod()
        {
            var empty = new[] {0};
            var test = Enumerable.ToList(empty);
        }
    }
}";

then symbolInfo has a candidate but still doesn't have Symbol. How to get a symbol info of extension method invocation?

like image 239
Serg046 Avatar asked Dec 24 '18 21:12

Serg046


People also ask

What is the correct definition of extension method?

Extension methods enable you to "add" methods to existing types without creating a new derived type, recompiling, or otherwise modifying the original type. Extension methods are static methods, but they're called as if they were instance methods on the extended type.

What is extension method in MVC?

What is extension method? Extension methods in C# are methods applied to some existing class and they look like regular instance methods. This way we can "extend" existing classes we cannot change. Perhaps the best example of extension methods are HtmlHelper extensions used in ASP.NET MVC.

What is extension method in C# with example?

In C#, the extension method concept allows you to add new methods in the existing class or in the structure without modifying the source code of the original type and you do not require any kind of special permission from the original type and there is no need to re-compile the original type.

Can extension methods access private members?

According to Microsoft, In fact, extension methods cannot access private variables in the type they are extending.


1 Answers

If you are using the default unit tests helper class that is automatically created from an 'Analyzer with Code Fix' project template, then you should be aware of the following.

The GetSortedDiagnosticsFromDocuments method tries to run the analyzers even if there are compilation errors when processing the code you provide as input. Of course, when there are compilation errors, the semantic model might be incomplete or missing.

You can change this method in this way:

// old version: no compilation errors detection
var compilationWithAnalyzers = project.GetCompilationAsync().Result.WithAnalyzers(ImmutableArray.Create(analyzer));


// new version: detect the compilation errors
var compilation = project.GetCompilationAsync().Result;             
var compilerErrors = compilation.GetDiagnostics().Where(i => i.Severity == DiagnosticSeverity.Error);

if (compilerErrors.Any())
{
    return compilerErrors.ToArray();
}

var compilationWithAnalyzers = compilation.WithAnalyzers(ImmutableArray.Create(analyzer));

If you then try to run the unit tests with this code and with your input string, you will notice at least 2 compilation errors:

error CS0012: The type 'List<>' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Collections, Version=4.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.

(the version number might be different)

and

error CS5001: Program does not contain a static 'Main' method suitable for an entry point

You need to solve these errors to get your code compiled, so that you can access a valid semantic model.

  • Change your void TestMethod() to a static void Main(). This is not really necessary, but it's better to have a valid code without compiler errors.
  • Add a reference to System.Collections and System.Runtime to your dynamically generated project. There is no convenient way to do this in unit tests when you use the helper code from the project template. But to test it, you could change the CreateProject method.

By default, 4 additional references are configured. Add the missing references:

var collectionsReference = MetadataReference.CreateFromFile(typeof(Stack<>).Assembly.Location);
var runtimeReference = MetadataReference.CreateFromFile(typeof(ISet<>).Assembly.Location);

var solution = new AdhocWorkspace()
    .CurrentSolution
    .AddProject(projectId, TestProjectName, TestProjectName, language)
    .AddMetadataReference() //...
    // ...extend the AddMetadataReference chain
    .AddMetadataReference(projectId, collectionsReference)
    .AddMetadataReference(projectId, runtimeReference);

Then, you should be able to compile the code in your unit tests and to get a valid semantic model.

You might be willing to implement that dynamic references functionality e.g. by exposing the project object being created on-the-fly to the unit test caller.

like image 193
dymanoid Avatar answered Oct 08 '22 23:10

dymanoid