Skip to content

Commit 5ffee2c

Browse files
committed
Enable forwarding support for attribute arguments
1 parent 5798dbc commit 5ffee2c

File tree

2 files changed

+43
-13
lines changed

2 files changed

+43
-13
lines changed

CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/Models/AttributeInfo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public static AttributeInfo From(INamedTypeSymbol typeSymbol, SemanticModel sema
7575
continue;
7676
}
7777

78-
TypedConstantInfo argumentInfo = TypedConstantInfo.From(operation);
78+
TypedConstantInfo argumentInfo = TypedConstantInfo.From(operation, semanticModel, argument.Expression, token);
7979

8080
// Try to get the identifier name if the current expression is a named argument expression. If it
8181
// isn't, then the expression is a normal attribute constructor argument, so no extra work is needed.

CommunityToolkit.Mvvm.SourceGenerators/ComponentModel/Models/TypedConstantInfo.Factory.cs

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
using System;
66
using System.Collections.Immutable;
77
using System.Linq;
8+
using System.Threading;
89
using Microsoft.CodeAnalysis;
10+
using Microsoft.CodeAnalysis.CSharp.Syntax;
911
using Microsoft.CodeAnalysis.Operations;
1012

1113
namespace CommunityToolkit.Mvvm.SourceGenerators.ComponentModel.Models;
@@ -62,21 +64,28 @@ public static TypedConstantInfo From(TypedConstant arg)
6264
/// <summary>
6365
/// Creates a new <see cref="TypedConstantInfo"/> instance from a given <see cref="IOperation"/> value.
6466
/// </summary>
65-
/// <param name="arg">The input <see cref="IOperation"/> value.</param>
66-
/// <returns>A <see cref="TypedConstantInfo"/> instance representing <paramref name="arg"/>.</returns>
67+
/// <param name="operation">The input <see cref="IOperation"/> value.</param>
68+
/// <param name="semanticModel">The <see cref="SemanticModel"/> that was used to retrieve <paramref name="operation"/>.</param>
69+
/// <param name="expression">The <see cref="ExpressionSyntax"/> that <paramref name="operation"/> was retrieved from.</param>
70+
/// <param name="token">The cancellation token for the current operation.</param>
71+
/// <returns>A <see cref="TypedConstantInfo"/> instance representing <paramref name="operation"/>.</returns>
6772
/// <exception cref="ArgumentException">Thrown if the input argument is not valid.</exception>
68-
public static TypedConstantInfo From(IOperation arg)
73+
public static TypedConstantInfo From(
74+
IOperation operation,
75+
SemanticModel semanticModel,
76+
ExpressionSyntax expression,
77+
CancellationToken token)
6978
{
70-
if (arg.ConstantValue.HasValue)
79+
if (operation.ConstantValue.HasValue)
7180
{
7281
// Enum values are constant but need to be checked explicitly in this case
73-
if (arg.Type?.TypeKind is TypeKind.Enum)
82+
if (operation.Type?.TypeKind is TypeKind.Enum)
7483
{
75-
return new Enum(arg.Type!.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), arg.ConstantValue.Value!);
84+
return new Enum(operation.Type!.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat), operation.ConstantValue.Value!);
7685
}
7786

7887
// Handle all other constant literals normally
79-
return arg.ConstantValue.Value switch
88+
return operation.ConstantValue.Value switch
8089
{
8190
null => new Null(),
8291
string text => new Primitive.String(text),
@@ -96,21 +105,42 @@ public static TypedConstantInfo From(IOperation arg)
96105
};
97106
}
98107

99-
if (arg is ITypeOfOperation typeOfOperation)
108+
if (operation is ITypeOfOperation typeOfOperation)
100109
{
101110
return new Type(typeOfOperation.TypeOperand.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
102111
}
103112

104-
if (arg is IArrayCreationOperation arrayCreationOperation)
113+
if (operation is IArrayCreationOperation)
105114
{
106-
string? elementTypeName = ((IArrayTypeSymbol?)arg.Type)?.ElementType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
115+
string? elementTypeName = ((IArrayTypeSymbol?)operation.Type)?.ElementType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
107116

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

111-
ImmutableArray<TypedConstantInfo> items = ImmutableArray<TypedConstantInfo>.Empty; // TODO
120+
InitializerExpressionSyntax? initializerExpression =
121+
(expression as ImplicitArrayCreationExpressionSyntax)?.Initializer
122+
?? (expression as ArrayCreationExpressionSyntax)?.Initializer;
112123

113-
return new Array(elementTypeName, items);
124+
// No initializer found, just return an empty array
125+
if (initializerExpression is null)
126+
{
127+
return new Array(elementTypeName, ImmutableArray<TypedConstantInfo>.Empty);
128+
}
129+
130+
ImmutableArray<TypedConstantInfo>.Builder items = ImmutableArray.CreateBuilder<TypedConstantInfo>(initializerExpression.Expressions.Count);
131+
132+
// Enumerate all array elements and extract serialized info for them
133+
foreach (ExpressionSyntax initializationExpression in initializerExpression.Expressions)
134+
{
135+
if (semanticModel.GetOperation(initializationExpression, token) is not IOperation initializationOperation)
136+
{
137+
throw new ArgumentException("Failed to retrieve an operation for the current array element");
138+
}
139+
140+
items.Add(From(initializationOperation, semanticModel, initializationExpression, token));
141+
}
142+
143+
return new Array(elementTypeName, items.MoveToImmutable());
114144
}
115145

116146
throw new ArgumentException("Invalid attribute argument value");

0 commit comments

Comments
 (0)