Search code examples
c#roslynparse-tree

Modifying function declaration parameter by adding attribute and default value in Roslyn c#


I'm modifying my earlier code analyser in C# using Roslyn and again I'm stuck with some changes that I don't exactly know how to apply.

Basing on: https://github.com/dotnet/roslyn/wiki/Getting-Started-C%23-Syntax-Analysis I've created some base to work with in this question: Finding all not inheriting C# classes with Roslyn and changing to inheriting from base object (java-like)

I've parsed and traversed tree to find all method declarations, and their parameters. Basing on VS Syntax Visualiser I've constructed this:

foreach (var c in root1.DescendantNodesAndSelf())
{
    var methodDeclaration = c as MethodDeclarationSyntax;

    if (methodDeclaration == null)
        continue;

    if (methodDeclaration.ParameterList != null) //Have parameters
    {
        foreach (var p in methodDeclaration.ParameterList.Parameters)
        {
            var parameter = p as ParameterSyntax;
            String name, type;
            name = parameter.GetLastToken().Value.ToString();
            type = parameter.GetFirstToken().Value.ToString();

            if (parameter == null)
                continue;

            if (name == "caller" && type == "string")
            {
                AttributeSyntax ats = SyntaxFactory.Attribute(SyntaxFactory.QualifiedName(SyntaxFactory.IdentifierName("System.Runtime.CompilerServices"),SyntaxFactory.IdentifierName("CallerMemberName")));
                SeparatedSyntaxList<AttributeSyntax> ssl = new SeparatedSyntaxList<AttributeSyntax>();
                ssl = ssl.Add(ats);
                AttributeListSyntax als = SyntaxFactory.AttributeList(ssl);
                var par1 = parameter.AddAttributeLists(als);
                //ExpressionSyntax es = SyntaxFactory.AssignmentExpression(SyntaxKind.EqualsValueClause,null,SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression))
                //SyntaxFactory.EqualsValueClause(es);
                par1 = par1.AddModifiers();
                root2 = root2.ReplaceNode(parameter, par1);
            }
        }
    }
    else //Don't have parameters
        continue;

}

I'm trying to convert method declared like this:

private void testM3(string caller)

into

private void testM3([System.Runtime.CompilerServices.CallerMemberName] string caller = "")

And this part:

//ExpressionSyntax es = SyntaxFactory.AssignmentExpression(SyntaxKind.EqualsValueClause,null,SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression))
//SyntaxFactory.EqualsValueClause(es);

is my failed attempt to achieve creation of equals node.

From what I understand, this part:

AttributeSyntax ats = SyntaxFactory.Attribute(SyntaxFactory.QualifiedName(SyntaxFactory.IdentifierName("System.Runtime.CompilerServices"),SyntaxFactory.IdentifierName("CallerMemberName")));
SeparatedSyntaxList<AttributeSyntax> ssl = new SeparatedSyntaxList<AttributeSyntax>();
ssl = ssl.Add(ats);
AttributeListSyntax als = SyntaxFactory.AttributeList(ssl);
var par1 = parameter.AddAttributeLists(als);

will give me new parameter node in var par1 that includes attribute already, so I need to add default value setting. Please correct me if I'm wrong about this attribute, and I'd like to know how to build this equals expression node properly.


Solution

  • You have a two mistakes:

    • System.Runtime.CompilerServices.CallerMemberName is QualifiedName that contains System.Runtime.CompilerServices as QualifiedName and CallerMemberName as IdentifierName. System.Runtime.CompilerServices contains System.Runtime as QualifiedName and CompilerServices as IdentifierName. And finally System.Runtime contains two IdentifierName.

      So you need to fix creation of AttributeSyntax as are shown in the code below:

          AttributeSyntax ats = SyntaxFactory.Attribute(
                              SyntaxFactory.QualifiedName(
                                  SyntaxFactory.QualifiedName(
                                      SyntaxFactory.QualifiedName(SyntaxFactory.IdentifierName("System"), SyntaxFactory.IdentifierName("Runtime")), 
                                      SyntaxFactory.IdentifierName("CompilerServices")),
                              SyntaxFactory.IdentifierName("CallerMemberName")));
      
    • EqualsValueClause shouldn't contain AssignmentExpression, but should contains a some kind of LiteralExpression directly. In your case it's StringLiteralExpression:

      var par1 = parameter
                          .AddAttributeLists(als)
                          .WithDefault(SyntaxFactory.EqualsValueClause(SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(""))));
      

    By the way, you can use a some helpful method of Nodes, as example ParameterSyntax.WithDefault, to create a copy of Node (SyntaxTree is immutable in Roslyn) when you want to applay a small changes to existing node and then repace it.