Skip to content

Commit 817a0ea

Browse files
committed
Add diagnostics. Fix default keyword issue.
1 parent e47644f commit 817a0ea

File tree

10 files changed

+137
-17
lines changed

10 files changed

+137
-17
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using Microsoft.CodeAnalysis;
2+
3+
namespace UnityUxmlGenerator.Diagnostics;
4+
5+
internal static class DiagnosticDescriptors
6+
{
7+
public static readonly DiagnosticDescriptor ClassHasNoBaseClassError = new(
8+
id: "UXMLG001",
9+
title: "Class has no base class",
10+
messageFormat: "Class '{0}' must be declared as a partial and be inherited from 'VisualElement' or one of its derived classes.",
11+
category: typeof(UxmlGenerator).FullName,
12+
defaultSeverity: DiagnosticSeverity.Error,
13+
isEnabledByDefault: true);
14+
15+
public static readonly DiagnosticDescriptor ClassDoesNotInheritFromVisualElementError = new(
16+
id: "UXMLG002",
17+
title: "Class does not inherit from VisualElement",
18+
messageFormat: "Class '{0}' must be declared as a partial and be inherited from 'VisualElement' or one of its derived classes.",
19+
category: typeof(UxmlGenerator).FullName,
20+
defaultSeverity: DiagnosticSeverity.Error,
21+
isEnabledByDefault: true);
22+
23+
public static readonly DiagnosticDescriptor PropertyAndDefaultValueTypesMismatchError = new(
24+
id: "UXMLG003",
25+
title: "Types mismatch",
26+
messageFormat: "UxmlAttribute for '{0}' property was not created. The default property and attribute value must be of the same type.",
27+
category: typeof(UxmlGenerator).FullName,
28+
defaultSeverity: DiagnosticSeverity.Error,
29+
isEnabledByDefault: true);
30+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using Microsoft.CodeAnalysis;
2+
3+
namespace UnityUxmlGenerator.Diagnostics;
4+
5+
internal static class DiagnosticExtensions
6+
{
7+
public static Diagnostic CreateDiagnostic(this DiagnosticDescriptor descriptor, Location location, params object[] args)
8+
{
9+
return Diagnostic.Create(descriptor, location, args);
10+
}
11+
}

src/UnityUxmlGenerator/Extensions/BaseTypeDeclarationSyntaxExtensions.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ namespace UnityUxmlGenerator.Extensions;
66

77
internal static class BaseTypeDeclarationSyntaxExtensions
88
{
9+
public static bool InheritsFromAnyType(this BaseTypeDeclarationSyntax? @class)
10+
{
11+
return @class?.BaseList is not null && @class.BaseList.Types.Count != 0;
12+
}
13+
914
public static bool InheritsFromFullyQualifiedName(this BaseTypeDeclarationSyntax @class,
1015
GeneratorExecutionContext context, string name)
1116
{
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using Microsoft.CodeAnalysis;
2+
using UnityUxmlGenerator.Diagnostics;
3+
4+
namespace UnityUxmlGenerator.SyntaxReceivers;
5+
6+
internal abstract class BaseReceiver : ISyntaxReceiver
7+
{
8+
private readonly List<Diagnostic> _diagnostics = new();
9+
10+
public IEnumerable<Diagnostic> Diagnostics => _diagnostics;
11+
12+
public abstract void OnVisitSyntaxNode(SyntaxNode syntaxNode);
13+
14+
protected void RegisterDiagnostic(DiagnosticDescriptor diagnosticDescriptor, Location location,
15+
params object[] args)
16+
{
17+
_diagnostics.Add(diagnosticDescriptor.CreateDiagnostic(location, args));
18+
}
19+
}

src/UnityUxmlGenerator/SyntaxReceivers/UxmlFactoryReceiver.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55

66
namespace UnityUxmlGenerator.SyntaxReceivers;
77

8-
internal sealed class UxmlFactoryReceiver : ISyntaxReceiver
8+
internal sealed class UxmlFactoryReceiver : BaseReceiver
99
{
1010
private const string AttributeName = "UxmlElement";
1111

1212
private readonly List<UxmlFactoryCapture> _captures = new();
1313

1414
public IEnumerable<UxmlFactoryCapture> Captures => _captures;
1515

16-
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
16+
public override void OnVisitSyntaxNode(SyntaxNode syntaxNode)
1717
{
1818
if (syntaxNode is not AttributeSyntax
1919
{
@@ -25,11 +25,13 @@ public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
2525

2626
var @class = attribute.GetParent<ClassDeclarationSyntax>();
2727

28-
if (@class?.BaseList is null || @class.BaseList.Types.Count == 0)
28+
if (@class.InheritsFromAnyType())
2929
{
30-
return;
30+
_captures.Add(new UxmlFactoryCapture(@class!));
31+
}
32+
else if (@class is not null)
33+
{
34+
RegisterDiagnostic(ClassHasNoBaseClassError, @class.GetLocation(), @class.Identifier.Text);
3135
}
32-
33-
_captures.Add(new UxmlFactoryCapture(@class));
3436
}
3537
}

src/UnityUxmlGenerator/SyntaxReceivers/UxmlTraitsReceiver.cs

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,15 @@
66

77
namespace UnityUxmlGenerator.SyntaxReceivers;
88

9-
internal sealed class UxmlTraitsReceiver : ISyntaxReceiver
9+
internal sealed class UxmlTraitsReceiver : BaseReceiver
1010
{
1111
private const string AttributeName = "UxmlAttribute";
1212

1313
private readonly Dictionary<string, UxmlTraitsCapture> _captures = new();
1414

1515
public IReadOnlyDictionary<string, UxmlTraitsCapture> Captures => _captures;
1616

17-
public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
17+
public override void OnVisitSyntaxNode(SyntaxNode syntaxNode)
1818
{
1919
if (syntaxNode is not AttributeSyntax
2020
{
@@ -34,19 +34,30 @@ public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
3434
{
3535
if (HasSameType(property, attribute) == false)
3636
{
37+
RegisterDiagnostic(PropertyAndDefaultValueTypesMismatchError, property.GetLocation(),
38+
property.GetName());
3739
return;
3840
}
3941
}
4042

4143
var @class = property.GetParent<ClassDeclarationSyntax>();
42-
if (@class?.BaseList is null || @class.BaseList.Types.Count == 0)
44+
if (@class.InheritsFromAnyType() == false)
4345
{
46+
if (@class is null)
47+
{
48+
RegisterDiagnostic(ClassHasNoBaseClassError, property.GetLocation());
49+
}
50+
else
51+
{
52+
RegisterDiagnostic(ClassHasNoBaseClassError, @class.GetLocation(), @class.Identifier.Text);
53+
}
54+
4455
return;
4556
}
4657

47-
if (_captures.TryGetValue(@class.Identifier.Text, out var uxmlTraits) == false)
58+
if (_captures.TryGetValue(@class!.Identifier.Text, out var uxmlTraits) == false)
4859
{
49-
uxmlTraits = new UxmlTraitsCapture(@class, @class.BaseList.Types.First().Type);
60+
uxmlTraits = new UxmlTraitsCapture(@class, @class.BaseList!.Types.First().Type);
5061
_captures.Add(@class.Identifier.Text, uxmlTraits);
5162
}
5263

@@ -57,6 +68,11 @@ private static bool HasSameType(BasePropertyDeclarationSyntax property, Attribut
5768
{
5869
var parameter = attribute.ArgumentList!.Arguments.First().Expression;
5970

71+
if (parameter.IsKind(SyntaxKind.DefaultLiteralExpression))
72+
{
73+
return true;
74+
}
75+
6076
if (property.Type is PredefinedTypeSyntax predefinedType)
6177
{
6278
if (predefinedType.IsBoolType() &&
@@ -94,14 +110,27 @@ private static bool HasSameType(BasePropertyDeclarationSyntax property, Attribut
94110

95111
private static string? GetAttributeArgumentValue(AttributeSyntax attribute)
96112
{
97-
return attribute.ArgumentList?.Arguments.Single().Expression switch
113+
if (attribute.ArgumentList is null || attribute.ArgumentList.Arguments.Any() == false)
98114
{
99-
LiteralExpressionSyntax literal => literal.Token.IsKind(SyntaxKind.StringLiteralToken)
100-
? literal.Token.ValueText
101-
: literal.Token.Text,
115+
return null;
116+
}
117+
118+
return attribute.ArgumentList.Arguments.Single().Expression switch
119+
{
120+
LiteralExpressionSyntax literal => GetLiteralExpressionValue(literal),
102121
InvocationExpressionSyntax invocation => invocation.ArgumentList.Arguments.Single().Expression.GetText().ToString(),
103122
MemberAccessExpressionSyntax member => member.Parent?.ToString(),
104123
_ => null
105124
};
106125
}
126+
127+
private static string? GetLiteralExpressionValue(LiteralExpressionSyntax literal)
128+
{
129+
if (literal.Token.IsKind(SyntaxKind.DefaultKeyword))
130+
{
131+
return null;
132+
}
133+
134+
return literal.Token.IsKind(SyntaxKind.StringLiteralToken) ? literal.Token.ValueText : literal.Token.Text;
135+
}
107136
}

src/UnityUxmlGenerator/Usings.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
global using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
1+
global using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
2+
global using static UnityUxmlGenerator.Diagnostics.DiagnosticDescriptors;

src/UnityUxmlGenerator/UxmlGenerator.Execute.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ private static CompilationUnitSyntax GetCompilationUnit(TypeDeclarationSyntax ty
3333
// <auto-generated/>
3434
// #pragma warning disable
3535
// #nullable enable
36-
SyntaxTriviaList syntaxTriviaList = TriviaList(
36+
var syntaxTriviaList = TriviaList(
3737
Comment("// <auto-generated/>"),
3838
Trivia(PragmaWarningDirectiveTrivia(Token(SyntaxKind.DisableKeyword), true)),
3939
Trivia(NullableDirectiveTrivia(Token(SyntaxKind.EnableKeyword), true)));

src/UnityUxmlGenerator/UxmlGenerator.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
using Microsoft.CodeAnalysis;
2+
using Microsoft.CodeAnalysis.CSharp.Syntax;
3+
using UnityUxmlGenerator.Captures;
4+
using UnityUxmlGenerator.Diagnostics;
25
using UnityUxmlGenerator.Extensions;
36
using UnityUxmlGenerator.SyntaxReceivers;
47

@@ -30,6 +33,10 @@ public void Execute(GeneratorExecutionContext context)
3033
{
3134
context.AddSource($"{uxmlElement.ClassName}.UxmlFactory.g.cs", GenerateUxmlFactory(uxmlElement));
3235
}
36+
else
37+
{
38+
ReportClassDoesNotInheritFromVisualElementError(context, uxmlElement.Class);
39+
}
3340
}
3441

3542
foreach (var capture in receiver.UxmlTraitsReceiver.Captures)
@@ -40,6 +47,22 @@ public void Execute(GeneratorExecutionContext context)
4047
{
4148
context.AddSource($"{traitsCapture.ClassName}.UxmlTraits.g.cs", GenerateUxmlTraits(context, traitsCapture));
4249
}
50+
else
51+
{
52+
ReportClassDoesNotInheritFromVisualElementError(context, traitsCapture.Class);
53+
}
54+
}
55+
56+
foreach (var diagnostic in receiver.UxmlTraitsReceiver.Diagnostics)
57+
{
58+
context.ReportDiagnostic(diagnostic);
4359
}
4460
}
61+
62+
private static void ReportClassDoesNotInheritFromVisualElementError(GeneratorExecutionContext context,
63+
BaseTypeDeclarationSyntax @class)
64+
{
65+
context.ReportDiagnostic(
66+
ClassDoesNotInheritFromVisualElementError.CreateDiagnostic(@class.GetLocation(), @class.Identifier.Text));
67+
}
4568
}

0 commit comments

Comments
 (0)