Skip to content

Commit

Permalink
Enable forwarding support for attribute arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
Sergio0694 committed Sep 20, 2022
1 parent 2526eef commit 4bd9539
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public static AttributeInfo From(INamedTypeSymbol typeSymbol, SemanticModel sema
continue;
}

TypedConstantInfo argumentInfo = TypedConstantInfo.From(operation);
TypedConstantInfo argumentInfo = TypedConstantInfo.From(operation, semanticModel, argument.Expression, token);

// Try to get the identifier name if the current expression is a named argument expression. If it
// isn't, then the expression is a normal attribute constructor argument, so no extra work is needed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
using System;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Operations;

namespace CommunityToolkit.Mvvm.SourceGenerators.ComponentModel.Models;
Expand Down Expand Up @@ -62,21 +64,28 @@ public static TypedConstantInfo From(TypedConstant arg)
/// <summary>
/// Creates a new <see cref="TypedConstantInfo"/> instance from a given <see cref="IOperation"/> value.
/// </summary>
/// <param name="arg">The input <see cref="IOperation"/> value.</param>
/// <returns>A <see cref="TypedConstantInfo"/> instance representing <paramref name="arg"/>.</returns>
/// <param name="operation">The input <see cref="IOperation"/> value.</param>
/// <param name="semanticModel">The <see cref="SemanticModel"/> that was used to retrieve <paramref name="operation"/>.</param>
/// <param name="expression">The <see cref="ExpressionSyntax"/> that <paramref name="operation"/> was retrieved from.</param>
/// <param name="token">The cancellation token for the current operation.</param>
/// <returns>A <see cref="TypedConstantInfo"/> instance representing <paramref name="operation"/>.</returns>
/// <exception cref="ArgumentException">Thrown if the input argument is not valid.</exception>
public static TypedConstantInfo From(IOperation arg)
public static TypedConstantInfo From(
IOperation operation,
SemanticModel semanticModel,
ExpressionSyntax expression,
CancellationToken token)
{
if (arg.ConstantValue.HasValue)
if (operation.ConstantValue.HasValue)
{
// Enum values are constant but need to be checked explicitly in this case
if (arg.Type?.TypeKind is TypeKind.Enum)
if (operation.Type?.TypeKind is TypeKind.Enum)
{
return new Enum(arg.Type!.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), arg.ConstantValue.Value!);
return new Enum(operation.Type!.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), operation.ConstantValue.Value!);
}

// Handle all other constant literals normally
return arg.ConstantValue.Value switch
return operation.ConstantValue.Value switch
{
null => new Null(),
string text => new Primitive.String(text),
Expand All @@ -96,21 +105,42 @@ public static TypedConstantInfo From(IOperation arg)
};
}

if (arg is ITypeOfOperation typeOfOperation)
if (operation is ITypeOfOperation typeOfOperation)
{
return new Type(typeOfOperation.TypeOperand.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
}

if (arg is IArrayCreationOperation arrayCreationOperation)
if (operation is IArrayCreationOperation)
{
string? elementTypeName = ((IArrayTypeSymbol?)arg.Type)?.ElementType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
string? elementTypeName = ((IArrayTypeSymbol?)operation.Type)?.ElementType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);

// If the element type is not available (since the attribute wasn't checked), just default to object
elementTypeName ??= "object";

ImmutableArray<TypedConstantInfo> items = ImmutableArray<TypedConstantInfo>.Empty; // TODO
InitializerExpressionSyntax? initializerExpression =
(expression as ImplicitArrayCreationExpressionSyntax)?.Initializer
?? (expression as ArrayCreationExpressionSyntax)?.Initializer;

return new Array(elementTypeName, items);
// No initializer found, just return an empty array
if (initializerExpression is null)
{
return new Array(elementTypeName, ImmutableArray<TypedConstantInfo>.Empty);
}

ImmutableArray<TypedConstantInfo>.Builder items = ImmutableArray.CreateBuilder<TypedConstantInfo>(initializerExpression.Expressions.Count);

// Enumerate all array elements and extract serialized info for them
foreach (ExpressionSyntax initializationExpression in initializerExpression.Expressions)
{
if (semanticModel.GetOperation(initializationExpression, token) is not IOperation initializationOperation)
{
throw new ArgumentException("Failed to retrieve an operation for the current array element");
}

items.Add(From(initializationOperation, semanticModel, initializationExpression, token));
}

return new Array(elementTypeName, items.MoveToImmutable());
}

throw new ArgumentException("Invalid attribute argument value");
Expand Down

0 comments on commit 4bd9539

Please sign in to comment.