diff --git a/src/XamlNameReferenceGenerator/Infrastructure/MiniCompiler.cs b/src/XamlNameReferenceGenerator/Infrastructure/MiniCompiler.cs index 9d0a536f6d..f8309bc343 100644 --- a/src/XamlNameReferenceGenerator/Infrastructure/MiniCompiler.cs +++ b/src/XamlNameReferenceGenerator/Infrastructure/MiniCompiler.cs @@ -22,6 +22,7 @@ namespace XamlNameReferenceGenerator.Infrastructure private MiniCompiler(TransformerConfiguration configuration) : base(configuration, new XamlLanguageEmitMappings(), false) { + Transformers.Add(new NameDirectiveTransformer()); Transformers.Add(new KnownDirectivesTransformer()); Transformers.Add(new XamlIntrinsicsTransformer()); Transformers.Add(new XArgumentsTransformer()); diff --git a/src/XamlNameReferenceGenerator/Infrastructure/MiniNamedControlCollector.cs b/src/XamlNameReferenceGenerator/Infrastructure/MiniNamedControlCollector.cs deleted file mode 100644 index 46b5910a42..0000000000 --- a/src/XamlNameReferenceGenerator/Infrastructure/MiniNamedControlCollector.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Collections.Generic; -using XamlX.Ast; - -namespace XamlNameReferenceGenerator.Infrastructure -{ - internal sealed class MiniNamedControlCollector : IXamlAstVisitor - { - private readonly List<(string TypeName, string Name)> _items = new List<(string TypeName, string Name)>(); - - public IReadOnlyList<(string TypeName, string Name)> Controls => _items; - - public IXamlAstNode Visit(IXamlAstNode node) - { - if (!(node is XamlAstConstructableObjectNode constructableObjectNode)) - return node; - - foreach (var child in constructableObjectNode.Children) - { - var nameValue = ResolveNameDirectiveOrDefault(child); - if (nameValue == null) continue; - - var clrType = constructableObjectNode.Type.GetClrType(); - var typeNamePair = ($@"{clrType.Namespace}.{clrType.Name}", nameValue); - if (!_items.Contains(typeNamePair)) - { - _items.Add(typeNamePair); - } - } - - return node; - } - - public void Push(IXamlAstNode node) { } - - public void Pop() { } - - private static string ResolveNameDirectiveOrDefault(IXamlAstNode node) => - node switch - { - XamlAstXamlPropertyValueNode propertyValueNode when - propertyValueNode.Property is XamlAstClrProperty reference && - reference.Name == "Name" && - propertyValueNode.Values.Count > 0 && - propertyValueNode.Values[0] is XamlAstTextNode nameNode => nameNode.Text, - - XamlAstXmlDirective xmlDirective when - xmlDirective.Name == "Name" && - xmlDirective.Values.Count > 0 && - xmlDirective.Values[0] is XamlAstTextNode xNameNode => xNameNode.Text, - - _ => null - }; - } -} \ No newline at end of file diff --git a/src/XamlNameReferenceGenerator/Infrastructure/NameDirectiveTransformer.cs b/src/XamlNameReferenceGenerator/Infrastructure/NameDirectiveTransformer.cs new file mode 100644 index 0000000000..081fe5b4ee --- /dev/null +++ b/src/XamlNameReferenceGenerator/Infrastructure/NameDirectiveTransformer.cs @@ -0,0 +1,29 @@ +using XamlX; +using XamlX.Ast; +using XamlX.Transform; + +namespace XamlNameReferenceGenerator.Infrastructure +{ + public class NameDirectiveTransformer : IXamlAstTransformer + { + public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) + { + if (node is XamlAstObjectNode objectNode) + { + for (var index = 0; index < objectNode.Children.Count; index++) + { + var child = objectNode.Children[index]; + if (child is XamlAstXmlDirective directive && + directive.Namespace == XamlNamespaces.Xaml2006 && + directive.Name == "Name") + objectNode.Children[index] = new XamlAstXamlPropertyValueNode( + directive, + new XamlAstNamePropertyReference(directive, objectNode.Type, "Name", objectNode.Type), + directive.Values); + } + } + + return node; + } + } +} \ No newline at end of file diff --git a/src/XamlNameReferenceGenerator/Infrastructure/NamedControlCollector.cs b/src/XamlNameReferenceGenerator/Infrastructure/NamedControlCollector.cs new file mode 100644 index 0000000000..75903286c0 --- /dev/null +++ b/src/XamlNameReferenceGenerator/Infrastructure/NamedControlCollector.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using XamlX.Ast; + +namespace XamlNameReferenceGenerator.Infrastructure +{ + internal sealed class NamedControlCollector : IXamlAstVisitor + { + private readonly List<(string TypeName, string Name)> _items = new List<(string TypeName, string Name)>(); + + public IReadOnlyList<(string TypeName, string Name)> Controls => _items; + + public IXamlAstNode Visit(IXamlAstNode node) + { + if (node is XamlAstConstructableObjectNode constructableObjectNode) + { + foreach (var child in constructableObjectNode.Children) + { + if (child is XamlAstXamlPropertyValueNode propertyValueNode && + propertyValueNode.Property is XamlAstClrProperty clrProperty && + clrProperty.Name == "Name" && + propertyValueNode.Values.Count > 0 && + propertyValueNode.Values[0] is XamlAstTextNode text) + { + var clrType = constructableObjectNode.Type.GetClrType(); + var typeNamePair = ($@"{clrType.Namespace}.{clrType.Name}", text.Text); + + if (!_items.Contains(typeNamePair)) + { + _items.Add(typeNamePair); + } + } + } + + return node; + } + + return node; + } + + public void Push(IXamlAstNode node) { } + + public void Pop() { } + } +} \ No newline at end of file diff --git a/src/XamlNameReferenceGenerator/Infrastructure/PhysicalFileDebugger.cs b/src/XamlNameReferenceGenerator/Infrastructure/PhysicalFileDebugger.cs index 0b278611c2..68b70c5246 100644 --- a/src/XamlNameReferenceGenerator/Infrastructure/PhysicalFileDebugger.cs +++ b/src/XamlNameReferenceGenerator/Infrastructure/PhysicalFileDebugger.cs @@ -15,19 +15,17 @@ namespace XamlNameReferenceGenerator.Infrastructure if (File.Exists(_path)) File.Delete(_path); - string sourceCode; try { - sourceCode = function(); + var sourceCode = function(); File.WriteAllText(_path, sourceCode); + return sourceCode; } catch (Exception exception) { File.WriteAllText(_path, exception.ToString()); - sourceCode = string.Empty; + throw; } - - return sourceCode; } } } \ No newline at end of file diff --git a/src/XamlNameReferenceGenerator/NameReferenceGenerator.cs b/src/XamlNameReferenceGenerator/NameReferenceGenerator.cs index 34d1f5e07f..2cfbd05979 100644 --- a/src/XamlNameReferenceGenerator/NameReferenceGenerator.cs +++ b/src/XamlNameReferenceGenerator/NameReferenceGenerator.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Text; @@ -47,14 +48,49 @@ namespace XamlNameReferenceGenerator var symbols = UnpackAnnotatedTypes(compilation, receiver); foreach (var typeSymbol in symbols) { - var relevantXamlFile = context.AdditionalFiles - .First(text => - text.Path.EndsWith($"{typeSymbol.Name}.xaml") || - text.Path.EndsWith($"{typeSymbol.Name}.axaml")); + var xamlFileName = $"{typeSymbol.Name}.xaml"; + var aXamlFileName = $"{typeSymbol.Name}.axaml"; + var relevantXamlFile = context + .AdditionalFiles + .FirstOrDefault(text => + text.Path.EndsWith(xamlFileName) || + text.Path.EndsWith(aXamlFileName)); - var sourceCode = Debugger.Debug( - () => GenerateSourceCode(xamlParser, typeSymbol, relevantXamlFile)); - context.AddSource($"{typeSymbol.Name}.g.cs", SourceText.From(sourceCode, Encoding.UTF8)); + if (relevantXamlFile is null) + { + context.ReportDiagnostic( + Diagnostic.Create( + new DiagnosticDescriptor( + "AXN0001", + "Unable to discover the relevant Avalonia XAML file.", + "Unable to discover the relevant Avalonia XAML file " + + $"neither at {xamlFileName} nor at {aXamlFileName}", + "Usage", + DiagnosticSeverity.Error, + true), + Location.None)); + return; + } + + try + { + var sourceCode = Debugger.Debug(() => GenerateSourceCode(xamlParser, typeSymbol, relevantXamlFile)); + context.AddSource($"{typeSymbol.Name}.g.cs", SourceText.From(sourceCode, Encoding.UTF8)); + } + catch (Exception exception) + { + context.ReportDiagnostic( + Diagnostic.Create( + new DiagnosticDescriptor( + "AXN0002", + "Unhandled exception occured while generating typed Name references.", + $"Unhandled exception occured while generating typed Name references: {exception}", + "Usage", + DiagnosticSeverity.Error, + true), + Location.None)); + return; + } } } diff --git a/src/XamlNameReferenceGenerator/Parsers/XamlXNameReferenceXamlParser.cs b/src/XamlNameReferenceGenerator/Parsers/XamlXNameReferenceXamlParser.cs index 789c893730..a286027fcc 100644 --- a/src/XamlNameReferenceGenerator/Parsers/XamlXNameReferenceXamlParser.cs +++ b/src/XamlNameReferenceGenerator/Parsers/XamlXNameReferenceXamlParser.cs @@ -23,7 +23,7 @@ namespace XamlNameReferenceGenerator.Parsers .CreateDefault(new RoslynTypeSystem(_compilation)) .Transform(parsed); - var visitor = new MiniNamedControlCollector(); + var visitor = new NamedControlCollector(); parsed.Root.Visit(visitor); parsed.Root.VisitChildren(visitor); return visitor.Controls;