Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting method arguments with Roslyn

Tags:

c#

.net

roslyn

I can get a list from the solution of all calls to a particuliar method using the following code:

var createCommandList = new List<MethodSymbol>();
INamedTypeSymbol interfaceSymbol = 
   (from p
    in solution.Projects
    select p.GetCompilation().GetTypeByMetadataName(
        "BuySeasons.BsiServices.DataResource.IBsiDataConnection")
    ).FirstOrDefault();
foreach (ISymbol symbol in interfaceSymbol.GetMembers("CreateCommand"))
{
    if (symbol.Kind == CommonSymbolKind.Method
        && symbol is MethodSymbol)
    {
        createCommandList.Add(symbol as MethodSymbol);
    }
}
foreach (MethodSymbol methodSymbol in createCommandList)
{
    foreach (ReferencedSymbol referenceSymbol
        in methodSymbol.FindReferences(solution))
    {
        foreach (ReferenceLocation referenceLocation
            in from l
               in referenceSymbol.Locations
               orderby l.Document.FilePath
               select l)
        {
            if (referenceLocation.Location.GetLineSpan(false)
                    .StartLinePosition.Line ==
                referenceLocation.Location.GetLineSpan(false)
                    .EndLinePosition.Line)
            {
                Debug.WriteLine("{0} {1} at {2} {3}/{4} - {5}",
                    methodSymbol.Name,
                    "(" + String.Join(",",
                       (from p
                        in methodSymbol.Parameters
                        select p.Type.Name + " " + p.Name).ToArray()
                       ) + ")",
                Path.GetFileName(referenceLocation.Location.GetLineSpan(false)
                    .Path),
                referenceLocation.Location.GetLineSpan(false)
                    .StartLinePosition.Line,
                referenceLocation.Location.GetLineSpan(false)
                    .StartLinePosition.Character,
                referenceLocation.Location.GetLineSpan(false)
                    .EndLinePosition.Character));
            }
            else
            {
                throw new ApplicationException("Call spans multiple lines");
            }
        }
    }
}

But this gives me a list of ReferencedSymbol. Although this gives me the file and line number that the method is called from I would also like to get the specific arguments that the method is called with. How can I either convert what I have or get the same information with Roslyn? (notice the I first load the solution with the Solution.Load method and then loop through to find out where the method is defined/declared (createCommandList)).

like image 894
Kevin Burton Avatar asked Jun 29 '12 14:06

Kevin Burton


2 Answers

You are already using Roslyn here. When you have a referenceSymbol, you can get at the Method Declaration Syntax and then look down into the tree to get the Parameter list.

I've inserted a arguments variable that uses your referenceSymbol:

// Snip start
foreach (MethodSymbol methodSymbol in createCommandList)
{
    foreach (ReferencedSymbol referenceSymbol
        in methodSymbol.FindReferences(solution))
    {
        var arguments = referenceSymbol.Definition.DeclaringSyntaxNodes.First()
            .DescendantNodes().OfType<ParameterSyntax>().ToList();

        foreach (ReferenceLocation referenceLocation in
            from l
            in referenceSymbol.Locations
            orderby l.Document.FilePath
            select l)
        {
// Snip end

When you perform a Debug output, you can then use that list of arguments to get the names.

My solution requires getting the First() of the DeclaringSyntaxNodes, which I don't like very much but cannot find another way to get at the Descendant Nodes of the Syntax Tree.

like image 167
Dr Rob Lang Avatar answered Sep 27 '22 15:09

Dr Rob Lang


I have discovered another way of getting the Parameter list from a method that might work for others as well.

Say I have the following method that has two parameters:

public string DebugPage(string enabled, string show){
  //do stuff 
}

Then you get your nodes however you wish. For example this gives me a list of public methods:

IEnumerable<MethodDeclarationSyntax> methods = from m in root.DescendantNodes().OfType<MethodDeclarationSyntax>() where m.Modifiers.ToString().Contains("public") select m;

Then I can iterate through that list of methods to get at the method's ParameterList property which is exposed to make this operation really easy. By the end of this loop the list parameters will hold the names of each parameter in the method (in the example of the Debug method above it will hold the values enabled and show as expected):

var parameters = new List<string>();

foreach (var method in methods)
{
   foreach (var n in method.ParameterList.Parameters)
   {
     var parameterName = n.Identifier.Text;
     parameters.Add(parameterName);
   }
}
like image 22
Max Worg Avatar answered Sep 27 '22 15:09

Max Worg