Search code examples
c#roslyn-code-analysissourcegenerators

Using Microsoft.CodeAnalysis, how do I get the value of an Attribute property that is of type Type?


I have an Attribute class used to decorate an assembly for use in C# source code generators. I am trying to get the value of one of the arguments that is type System.Type. I can get the values of the other properties just fine.

Here's the attribute class:

    [AttributeUsage(AttributeTargets.Assembly)]
    public class MigrationFunctionAttribute : Attribute
    {
        public Type MigrationFunction { get; set; }

        public string MigrationMethod { get; set; }
        public string DependsOn { get; set; }
        public string SqlScriptBucket { get; set; }
        // ATTRIBUTE:  ADD HERE
        public string Branch { get; set; }

    }

Here's my usage of the attribute:

[assembly: MigrationFunction(MigrationFunction = typeof(MigrationFunctions), MigrationMethod = nameof(MigrationFunctions.MyMigrator), Branch = "@Branch", DependsOn = "Restore")]

After receiving the attribute in the SyntaxReceiver, I try to build a model from the information:

    public static AttributeModel2<IMigrationFunctionAttributeModel> Build(AttributeSyntax receiverMigrationFunctionAttribute, GeneratorExecutionContext context)
    {

        SemanticModel semanticModel = context.Compilation.GetSemanticModel(receiverMigrationFunctionAttribute.SyntaxTree);

        foreach (var attributeArgumentSyntax in receiverMigrationFunctionAttribute.ArgumentList.Arguments)
        {
            switch (attributeArgumentSyntax.NameEquals.Name.Identifier.ValueText)
            {
                case nameof(MigrationFunctionAttribute.MigrationFunction):


                    Debug.WriteLine(attributeArgumentSyntax.Expression);
                    // ^^^ outputs 'typeof(MigrationFunctions)'

                    TypeInfo t = semanticModel.GetTypeInfo(attributeArgumentSyntax.Expression);
                    Debug.WriteLine(t.Type.ToDisplayString());
                    // ^^^ outputs 'System.Type'


                    /// HOW DO I GET THE TYPE HERE, which should be "MigrationFunctions", not "System.Type"???
                    break;
                case nameof(MigrationFunctionAttribute.Branch):

                    var value = semanticModel.GetConstantValue(attributeArgumentSyntax.Expression);
                    Debug.WriteLine(value.Value);
                    // ^^^ outputs '@Branch just fine
                    break;

            }
        }
        
        return null;

    }

The model builder method can pick through the arguments as expected, but I cannot figure out how to get the correct type info for the property:

public Type MigrationFunction { get; set; }

I get System.Type, whereas I am expecting to get MigrationFunctions.

When I output from, I get typeof(MigrationFunctions):

Debug.WriteLine(attributeArgumentSyntax.Expression);

As documented in the code, HOW DO I GET THE TYPE HERE, which should be "MigrationFunctions", not "System.Type"???


Solution

  • You're definitely on the right track. Let's home in on this part:

    TypeInfo t = semanticModel.GetTypeInfo(attributeArgumentSyntax.Expression);
    Debug.WriteLine(t.Type.ToDisplayString());
    // ^^^ outputs 'System.Type'
    

    As you note, GetTypeInfo when invoked with attributeArgumentSyntax.Expression returns a TypeInfo representing System.Type. This makes sense because the declared type of that attribute property is System.Type.

    You can still get the type you're after (MigrationFunctions) but you have to dig a little deeper. First cast the expression to TypeOfExpressionSyntax:

    var typeOfExpression = (TypeOfExpressionSyntax)attributeArgumentSyntax.Expression;
    

    With that you can get the actual TypeSyntax representing typeof(MigrationFunctions):

    var typeSyntax = typeOfExpression.Type;
    

    Finally, you can get the ITypeSymbol for MigrationFunctions:

    var type = semanticModel.GetTypeInfo(typeSyntax);