I'm writing my codefixer with roslyn. I want it to work in such way:
Before:
IEnumerable<int> list = new List<int> {1, 2, 3, 4};
list = list.Where(x => x%2 == 0);
After:
...
IEnumerable<int> list = new List<int> {1, 2, 3, 4};
list = list.Where(lambdaMethod);
...
private static bool lamdaMethod(int x)
{
return x % 2 == 0;
}
The problem is that I can't find the proper way to get return type for the method and type of argument.
My method from analyzer template:
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
// TODO: Replace the following code with your own analysis, generating a CodeAction for each fix to suggest
var diagnostic = context.Diagnostics.FirstOrDefault();
var diagnosticSpan = diagnostic.Location.SourceSpan;
// Find the type declaration identified by the diagnostic.
var declaration = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf()
.OfType<LambdaExpressionSyntax>().FirstOrDefault();
if (declaration == null)
return;
// Register a code action that will invoke the fix.
context.RegisterCodeFix(
CodeAction.Create(
title: $"{"Just Title"}",
createChangedDocument: c => MakeLambdaOptimization(context.Document, declaration, c),
equivalenceKey: nameof(CodeFixResources.CodeFixTitle)),
diagnostic);
}
As you see, I'm working with LambdaExpressionSyntax
. There are also SimpleLambdaExpressionSyntax
and ParenthesizedLambdaExpressionSyntax
which help me to get ParameterSyntax
. In the ParameterSyntax
I can find Type
, but it's null and I get NullReferenceException.
I tried to get the whole type of lambda (e.g. Func<int, bool>
), I guess it would help me. How can I do it? I've heard about SemanticModel, but I don't know how to use it
You can use SemanticModel.GetSymbolInfo(ExpressionSyntax)
to get the lambda symbol. From the lambda symbol you can find the information you need (return type and parameter types). Here is a code snippet (written in the context of a Console Application instead of a codefix for simplicity - you'll be able to get it working for codefixes):
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
// You don't need to create or get a compilation yourself. I'm creating it manually since I'm in a console app.
CSharpCompilation comp = CSharpCompilation.Create(null, new[] { SyntaxFactory.ParseSyntaxTree("System.Func<int, bool> f = x => x % 2 == 0;") }, new[] { MetadataReference.CreateFromFile(typeof(Func<>).Assembly.Location) });
// You already have the LambdaExpressionSyntax, ignore the following two lines, which again because I'm in a console app.
var root = comp.SyntaxTrees[0].GetRoot();
var lambda = root.DescendantNodes().OfType<LambdaExpressionSyntax>().Single();
// Here you'll need to get a semantic model via 'await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);'
var model = comp.GetSemanticModel(lambda.SyntaxTree);
// That's the core of what you want to do.
if (model.GetSymbolInfo(lambda).Symbol is IMethodSymbol lambdaSymbol)
{
// prints 'bool'
Console.WriteLine(lambdaSymbol.ReturnType.ToDisplayString());
foreach (var parameter in lambdaSymbol.Parameters)
{
// prints 'int' (loop is entered once since my lambda has a single parameter).
Console.WriteLine(parameter.Type.ToDisplayString());
}
}