Skip to content

Commit b0e815e

Browse files
committed
Fix attribute named argument issue. Add capture validation. Optimize widgets.
1 parent 34c07d1 commit b0e815e

File tree

16 files changed

+408
-254
lines changed

16 files changed

+408
-254
lines changed

src/UnityUxmlGenerator/Captures/UxmlFactoryCapture.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@ namespace UnityUxmlGenerator.Captures;
44

55
internal sealed class UxmlFactoryCapture : BaseCapture
66
{
7-
public UxmlFactoryCapture(ClassDeclarationSyntax @class) : base(@class)
7+
public UxmlFactoryCapture(ClassDeclarationSyntax @class, AttributeSyntax attribute) : base(@class)
88
{
9+
Attribute = attribute;
910
}
1011

1112
public override string ClassTag => "UxmlFactory";
13+
14+
public AttributeSyntax Attribute { get; }
1215
}

src/UnityUxmlGenerator/Captures/UxmlTraitsCapture.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ internal sealed class UxmlTraitsCapture : BaseCapture
77
public UxmlTraitsCapture(ClassDeclarationSyntax @class, TypeSyntax baseClassType) : base(@class)
88
{
99
BaseClassType = baseClassType;
10-
Properties = new List<(PropertyDeclarationSyntax property, string? DefaultValue)>();
10+
Properties = new List<(PropertyDeclarationSyntax, AttributeSyntax)>();
1111
}
1212

1313
public override string ClassTag => "UxmlTraits";
1414

1515
public TypeSyntax BaseClassType { get; }
16-
public List<(PropertyDeclarationSyntax property, string? DefaultValue)> Properties { get; }
16+
public List<(PropertyDeclarationSyntax Property, AttributeSyntax Attribute)> Properties { get; }
1717

1818
public string GetBaseClassName(out TypeSyntax? genericTypeSyntax)
1919
{

src/UnityUxmlGenerator/Diagnostics/DiagnosticDescriptors.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,20 @@ internal static class DiagnosticDescriptors
2727
category: typeof(UxmlGenerator).FullName,
2828
defaultSeverity: DiagnosticSeverity.Error,
2929
isEnabledByDefault: true);
30+
31+
public static readonly DiagnosticDescriptor PropertyTypeIsNotSupportedError = new(
32+
id: "UXMLG004",
33+
title: "Property type is not supported",
34+
messageFormat: "Property type '{0}' can not be used as an attribute.",
35+
category: typeof(UxmlGenerator).FullName,
36+
defaultSeverity: DiagnosticSeverity.Error,
37+
isEnabledByDefault: true);
38+
39+
public static readonly DiagnosticDescriptor IncorrectEnumDefaultValueTypeError = new(
40+
id: "UXMLG005",
41+
title: "Type cannot be the default value for an enum",
42+
messageFormat: "Type '{0}' cannot be the default value for an enum.",
43+
category: typeof(UxmlGenerator).FullName,
44+
defaultSeverity: DiagnosticSeverity.Error,
45+
isEnabledByDefault: true);
3046
}

src/UnityUxmlGenerator/Diagnostics/DiagnosticExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace UnityUxmlGenerator.Diagnostics;
44

55
internal static class DiagnosticExtensions
66
{
7-
public static Diagnostic CreateDiagnostic(this DiagnosticDescriptor descriptor, Location location, params object[] args)
7+
public static Diagnostic CreateDiagnostic(this DiagnosticDescriptor descriptor, Location location, params object?[]? args)
88
{
99
return Diagnostic.Create(descriptor, location, args);
1010
}

src/UnityUxmlGenerator/Extensions/SyntaxNodeExtensions.cs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,29 @@ namespace UnityUxmlGenerator.Extensions;
55

66
internal static class SyntaxNodeExtensions
77
{
8+
public static bool IsAttributeWithName(this SyntaxNode syntaxNode, string attributeName,
9+
out AttributeSyntax? attribute)
10+
{
11+
if (syntaxNode is not AttributeSyntax attributeSyntax)
12+
{
13+
attribute = default;
14+
return false;
15+
}
16+
17+
attribute = attributeSyntax;
18+
19+
switch (attributeSyntax.Name)
20+
{
21+
case IdentifierNameSyntax identifierNameSyntax
22+
when identifierNameSyntax.Identifier.Text.Contains(attributeName):
23+
case QualifiedNameSyntax qualifiedNameSyntax
24+
when qualifiedNameSyntax.Right.Identifier.Text.Contains(attributeName):
25+
return true;
26+
}
27+
28+
return false;
29+
}
30+
831
public static T? GetParent<T>(this SyntaxNode syntaxNode)
932
{
1033
var parent = syntaxNode.Parent;
@@ -22,10 +45,13 @@ internal static class SyntaxNodeExtensions
2245
return default;
2346
}
2447

25-
public static string? GetTypeNamespace(this TypeSyntax typeSyntax, GeneratorExecutionContext context)
48+
public static ITypeSymbol? GetTypeSymbol(this SyntaxNode syntaxNode, GeneratorExecutionContext context)
49+
{
50+
return context.Compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetTypeInfo(syntaxNode).Type;
51+
}
52+
53+
public static string? GetTypeNamespace(this SyntaxNode syntaxNode, GeneratorExecutionContext context)
2654
{
27-
return context.Compilation
28-
.GetSemanticModel(typeSyntax.SyntaxTree)
29-
.GetTypeInfo(typeSyntax).Type?.ContainingNamespace.ToString();
55+
return GetTypeSymbol(syntaxNode, context)?.ContainingNamespace.ToString();
3056
}
3157
}

src/UnityUxmlGenerator/Structs/UxmlAttributeInfo.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ namespace UnityUxmlGenerator.Structs;
44

55
public ref struct UxmlAttributeInfo
66
{
7+
public string PropertyName { get; init; }
8+
public string PrivateFieldName { get; init; }
9+
public string AttributeUxmlName { get; init; }
10+
711
public string TypeIdentifier { get; set; }
8-
public string PrivateFieldName { get; set; }
9-
public string AttributeUxmlName { get; set; }
1012
public ExpressionSyntax DefaultValueAssignmentExpression { get; set; }
1113
}

src/UnityUxmlGenerator/SyntaxReceivers/BaseReceiver.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ internal abstract class BaseReceiver : ISyntaxReceiver
1212
public abstract void OnVisitSyntaxNode(SyntaxNode syntaxNode);
1313

1414
protected void RegisterDiagnostic(DiagnosticDescriptor diagnosticDescriptor, Location location,
15-
params object[] args)
15+
params object?[]? args)
1616
{
1717
_diagnostics.Add(diagnosticDescriptor.CreateDiagnostic(location, args));
1818
}

src/UnityUxmlGenerator/SyntaxReceivers/UxmlFactoryReceiver.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,16 @@ internal sealed class UxmlFactoryReceiver : BaseReceiver
1515

1616
public override void OnVisitSyntaxNode(SyntaxNode syntaxNode)
1717
{
18-
if (syntaxNode is not AttributeSyntax
19-
{
20-
Name: IdentifierNameSyntax { Identifier.Text: AttributeName }
21-
} attribute)
18+
if (syntaxNode.IsAttributeWithName(AttributeName, out var attribute) == false)
2219
{
2320
return;
2421
}
2522

26-
var @class = attribute.GetParent<ClassDeclarationSyntax>();
23+
var @class = attribute!.GetParent<ClassDeclarationSyntax>();
2724

2825
if (@class.InheritsFromAnyType())
2926
{
30-
_captures.Add(new UxmlFactoryCapture(@class!));
27+
_captures.Add(new UxmlFactoryCapture(@class!, attribute!));
3128
}
3229
else if (@class is not null)
3330
{

src/UnityUxmlGenerator/SyntaxReceivers/UxmlTraitsReceiver.cs

Lines changed: 15 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,20 @@ internal sealed class UxmlTraitsReceiver : BaseReceiver
1616

1717
public override void OnVisitSyntaxNode(SyntaxNode syntaxNode)
1818
{
19-
if (syntaxNode is not AttributeSyntax
20-
{
21-
Name: IdentifierNameSyntax { Identifier.Text: AttributeName }
22-
} attribute)
19+
if (syntaxNode.IsAttributeWithName(AttributeName, out var attribute) == false)
2320
{
2421
return;
2522
}
2623

27-
var property = attribute.GetParent<PropertyDeclarationSyntax>();
24+
var property = attribute!.GetParent<PropertyDeclarationSyntax>();
2825
if (property is null)
2926
{
3027
return;
3128
}
3229

33-
if (attribute.ArgumentList is not null && attribute.ArgumentList.Arguments.Any())
30+
if (attribute!.ArgumentList is not null && attribute.ArgumentList.Arguments.Any())
3431
{
35-
if (HasSameType(property, attribute) == false)
32+
if (HasSameType(property, attribute.ArgumentList.Arguments.First()) == false)
3633
{
3734
RegisterDiagnostic(PropertyAndDefaultValueTypesMismatchError, property.GetLocation(),
3835
property.GetName());
@@ -61,12 +58,12 @@ public override void OnVisitSyntaxNode(SyntaxNode syntaxNode)
6158
_captures.Add(uxmlTraits.ClassName, uxmlTraits);
6259
}
6360

64-
uxmlTraits.Properties.Add((property, GetAttributeArgumentValue(attribute)));
61+
uxmlTraits.Properties.Add((property, attribute));
6562
}
6663

67-
private static bool HasSameType(BasePropertyDeclarationSyntax property, AttributeSyntax attribute)
64+
private static bool HasSameType(BasePropertyDeclarationSyntax property, AttributeArgumentSyntax attributeArgument)
6865
{
69-
var parameter = attribute.ArgumentList!.Arguments.First().Expression;
66+
var parameter = attributeArgument.Expression;
7067

7168
if (parameter.IsKind(SyntaxKind.DefaultLiteralExpression))
7269
{
@@ -95,42 +92,20 @@ private static bool HasSameType(BasePropertyDeclarationSyntax property, Attribut
9592
}
9693
}
9794

98-
if (property.Type is IdentifierNameSyntax identifierName)
95+
SyntaxToken? propertyTypeIdentifier = property.Type switch
9996
{
100-
if (identifierName.Identifier.IsKind(SyntaxKind.IdentifierToken) &&
101-
(parameter.IsKind(SyntaxKind.InvocationExpression) ||
102-
parameter.IsKind(SyntaxKind.SimpleMemberAccessExpression)))
103-
{
104-
return true;
105-
}
106-
}
107-
108-
return false;
109-
}
110-
111-
private static string? GetAttributeArgumentValue(AttributeSyntax attribute)
112-
{
113-
if (attribute.ArgumentList is null || attribute.ArgumentList.Arguments.Any() == false)
114-
{
115-
return null;
116-
}
117-
118-
return attribute.ArgumentList.Arguments.Single().Expression switch
119-
{
120-
LiteralExpressionSyntax literal => GetLiteralExpressionValue(literal),
121-
InvocationExpressionSyntax invocation => invocation.ArgumentList.Arguments.Single().Expression.GetText().ToString(),
122-
MemberAccessExpressionSyntax member => member.Parent?.ToString(),
97+
IdentifierNameSyntax identifierName => identifierName.Identifier,
98+
QualifiedNameSyntax qualifiedNameSyntax => qualifiedNameSyntax.Right.Identifier,
12399
_ => null
124100
};
125-
}
126101

127-
private static string? GetLiteralExpressionValue(LiteralExpressionSyntax literal)
128-
{
129-
if (literal.Token.IsKind(SyntaxKind.DefaultKeyword))
102+
if (propertyTypeIdentifier is null)
130103
{
131-
return null;
104+
return false;
132105
}
133106

134-
return literal.Token.IsKind(SyntaxKind.StringLiteralToken) ? literal.Token.ValueText : literal.Token.Text;
107+
return propertyTypeIdentifier.Value.IsKind(SyntaxKind.IdentifierToken) &&
108+
(parameter.IsKind(SyntaxKind.InvocationExpression) ||
109+
parameter.IsKind(SyntaxKind.SimpleMemberAccessExpression));
135110
}
136111
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System.ComponentModel;
2+
3+
// ReSharper disable CheckNamespace
4+
// ReSharper disable UnusedType.Global
5+
6+
namespace System.Runtime.CompilerServices;
7+
8+
[EditorBrowsable(EditorBrowsableState.Never)]
9+
internal static class IsExternalInit
10+
{
11+
}

src/UnityUxmlGenerator/UxmlGenerator.Attributes.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ private static SourceText GenerateUxmlAttributeAttribute()
2323
}
2424

2525
private static SourceText GenerateAttributeClass(string attributeClassIdentifier,
26-
MemberDeclarationSyntax[]? members = null)
26+
IEnumerable<MemberDeclarationSyntax>? members = null)
2727
{
2828
return CompilationUnitWidget(
2929
namespaceIdentifier: AssemblyName.Name,
30-
members: ClassWidget(
30+
member: ClassWidget(
3131
identifier: attributeClassIdentifier,
3232
modifiers: new[] { SyntaxKind.InternalKeyword, SyntaxKind.SealedKeyword },
3333
baseType: SimpleBaseType(IdentifierName(AttributeBaseType)),
@@ -37,7 +37,7 @@ private static SourceText GenerateAttributeClass(string attributeClassIdentifier
3737
.GetText(Encoding.UTF8);
3838
}
3939

40-
private static MemberDeclarationSyntax[] GetUxmlAttributeMembers()
40+
private static IEnumerable<MemberDeclarationSyntax> GetUxmlAttributeMembers()
4141
{
4242
return new MemberDeclarationSyntax[]
4343
{
@@ -48,7 +48,7 @@ private static MemberDeclarationSyntax[] GetUxmlAttributeMembers()
4848
identifier: "defaultValue",
4949
type: NullableType(PredefinedType(Token(SyntaxKind.ObjectKeyword))),
5050
addDefaultKeyword: true),
51-
bodyStatements: AssignmentStatementWidget(
51+
bodyStatement: AssignmentStatementWidget(
5252
left: IdentifierName("DefaultValue"),
5353
right: IdentifierName("defaultValue")),
5454
addGeneratedCodeAttributes: true
@@ -57,7 +57,7 @@ private static MemberDeclarationSyntax[] GetUxmlAttributeMembers()
5757
identifier: "DefaultValue",
5858
type: NullableType(PredefinedType(Token(SyntaxKind.ObjectKeyword))),
5959
modifier: SyntaxKind.PublicKeyword,
60-
accessors: SyntaxKind.GetAccessorDeclaration,
60+
accessor: SyntaxKind.GetAccessorDeclaration,
6161
addGeneratedCodeAttributes: true
6262
)
6363
};

src/UnityUxmlGenerator/UxmlGenerator.Factory.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ private static SourceText GenerateUxmlFactory(UxmlFactoryCapture capture)
1313
{
1414
return CompilationUnitWidget(
1515
namespaceIdentifier: capture.ClassNamespace,
16-
members: ClassWidget(
16+
member: ClassWidget(
1717
identifier: capture.ClassName,
1818
modifier: SyntaxKind.PartialKeyword,
1919
member: ClassWidget(

0 commit comments

Comments
 (0)