Skip to content

Commit 127d9f2

Browse files
sourcegen for x:Reference
1 parent 5c18a24 commit 127d9f2

File tree

8 files changed

+53
-17
lines changed

8 files changed

+53
-17
lines changed

src/Controls/src/SourceGen/KnownMarkups.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
34
using System.Xml;
45

@@ -428,4 +429,35 @@ static bool IsBindingContextBinding(IElementNode node)
428429
&& propertyName.LocalName == "BindingContext";
429430
}
430431
}
432+
433+
internal static string ProvideValueForReferenceExtension(IElementNode markupNode, SourceGenContext context, out ITypeSymbol? returnType)
434+
{
435+
// should be possible to return the right value, as soon as we no longer use the namescope
436+
returnType = context.Compilation.ObjectType;
437+
if (!markupNode.Properties.TryGetValue(new XmlName("", "Name"), out INode refNameNode)
438+
&& !markupNode.Properties.TryGetValue(new XmlName(null, "Name"), out refNameNode))
439+
refNameNode = markupNode.CollectionItems[0];
440+
var name = ((ValueNode)refNameNode).Value as string;
441+
442+
var node = markupNode;
443+
var currentcontext = context;
444+
while (currentcontext is not null && node is not null)
445+
{
446+
while (currentcontext is not null && !currentcontext.Scopes.ContainsKey(node))
447+
currentcontext = context.ParentContext;
448+
if (currentcontext is null)
449+
break;
450+
var namescope= currentcontext.Scopes[node];
451+
if (namescope.namesInScope != null && namescope.namesInScope.ContainsKey(name!))
452+
{
453+
returnType = namescope.namesInScope[name!].Type;
454+
return $"{namescope.namesInScope[name!].Name}";
455+
}
456+
node = node.Parent as IElementNode;
457+
}
458+
459+
//TODO report diagnostic
460+
context.ReportDiagnostic(Diagnostic.Create(Descriptors.XamlParserError, null, $"ReferenceExtension: Name '{name}' not found in any NameScope"));
461+
return "null"; // or throw an exception?
462+
}
431463
}

src/Controls/src/SourceGen/NodeSGExtensions.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,11 @@ public static Dictionary<ITypeSymbol, ProvideValueDelegate> GetKnownEarlyMarkupE
8080

8181
// These markup extensions can only provide values late once the properties have their final values
8282
public static Dictionary<ITypeSymbol, ProvideValueDelegate> GetKnownLateMarkupExtensions(SourceGenContext context)
83-
=> KnownSGLateMarkupExtensions ??= new (SymbolEqualityComparer.Default)
84-
{
85-
{context.Compilation.GetTypeByMetadataName("Microsoft.Maui.Controls.Xaml.BindingExtension")!, KnownMarkups.ProvideValueForBindingExtension},
86-
{context.Compilation.GetTypeByMetadataName("Microsoft.Maui.Controls.Xaml.TemplateBindingExtension")!, KnownMarkups.ProvideValueForTemplateBindingExtension},
83+
=> KnownSGLateMarkupExtensions ??= new(SymbolEqualityComparer.Default)
84+
{
85+
{context.Compilation.GetTypeByMetadataName("Microsoft.Maui.Controls.Xaml.BindingExtension")!, KnownMarkups.ProvideValueForBindingExtension},
86+
{context.Compilation.GetTypeByMetadataName("Microsoft.Maui.Controls.Xaml.TemplateBindingExtension")!, KnownMarkups.ProvideValueForTemplateBindingExtension},
87+
{context.Compilation.GetTypeByMetadataName("Microsoft.Maui.Controls.Xaml.ReferenceExtension")!, KnownMarkups.ProvideValueForReferenceExtension},
8788
};
8889

8990
public static bool TryGetPropertyName(this INode node, INode parentNode, out XmlName name)

src/Controls/src/SourceGen/SourceGenContext.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ class SourceGenContext (IndentedTextWriter writer, Compilation compilation, Sour
2222
public void ReportDiagnostic(Diagnostic diagnostic) => sourceProductionContext.ReportDiagnostic(diagnostic);
2323
public string? FilePath {get; set;}
2424
public IDictionary<INode, LocalVariable> ServiceProviders { get; } = new Dictionary<INode, LocalVariable>();
25-
public IDictionary<INode, (LocalVariable namescope, IList<string> namesInScope)> Scopes = new Dictionary<INode, (LocalVariable, IList<string>)>();
25+
public IDictionary<INode, (LocalVariable namescope, IDictionary<string, LocalVariable> namesInScope)> Scopes = new Dictionary<INode, (LocalVariable, IDictionary<string,LocalVariable>)>();
2626
public SourceGenContext? ParentContext {get;set;}
2727
public ITypeSymbol? BaseType { get; } = baseType;
2828
public IDictionary<INode, ITypeSymbol> Types { get; } = new Dictionary<INode, ITypeSymbol>();

src/Controls/src/SourceGen/Visitors/SetNamescopesAndRegisterNames.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ public void Visit(ValueNode node, INode parentNode)
2727
if (!IsXNameProperty(node, parentNode))
2828
return;
2929
var name = (string)node.Value;
30-
if (namescope.namesInScope.Contains(name))
30+
if (namescope.namesInScope.ContainsKey(name))
3131
//TODO send diagnostic instead
3232
throw new Exception("dup x:Name");
33-
namescope.namesInScope.Add(name);
33+
namescope.namesInScope.Add(name,Context.Variables[(IElementNode)parentNode]);
3434
Writer.WriteLine($"{namescope.namescope.Name}.RegisterName(\"{name}\", {Context.Variables[(IElementNode)parentNode].Name});");
3535

3636
SetStyleId((string)node.Value, Context.Variables[(IElementNode)parentNode]);
@@ -42,13 +42,13 @@ public void Visit(MarkupNode node, INode parentNode)
4242
public void Visit(ElementNode node, INode parentNode)
4343
{
4444
LocalVariable namescope;
45-
IList<string> namesInNamescope;
45+
IDictionary<string,LocalVariable> namesInNamescope;
4646
var setNameScope = false;
4747

4848
if (parentNode == null || IsDataTemplate(node, parentNode) || IsStyle(node, parentNode) || IsVisualStateGroupList(node))
4949
{
5050
namescope = CreateNamescope();
51-
namesInNamescope = [];
51+
namesInNamescope = new Dictionary<string, LocalVariable>();
5252
setNameScope = true;
5353
}
5454
else
@@ -71,7 +71,7 @@ public void Visit(RootNode node, INode parentNode)
7171
if (Context.RootType.InheritsFrom(Context.Compilation.GetTypeByMetadataName("Microsoft.Maui.Controls.BindableObject")!, Context))
7272
Writer.WriteLine($"global::Microsoft.Maui.Controls.Internals.NameScope.SetNameScope({Context.Variables[node].Name}, {namescope.Name});");
7373

74-
Context.Scopes[node] = new (namescope, []);
74+
Context.Scopes[node] = new (namescope, new Dictionary<string, LocalVariable>());
7575
}
7676

7777
public void Visit(ListNode node, INode parentNode)

src/Controls/src/Xaml/MarkupExtensions/ReferenceExtension.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System;
22
using Microsoft.Extensions.DependencyInjection;
33
using Microsoft.Maui.Controls.Internals;
4-
using Microsoft.Maui.Controls.Xaml.Internals;
54

65
namespace Microsoft.Maui.Controls.Xaml
76
{
@@ -22,12 +21,13 @@ public object ProvideValue(IServiceProvider serviceProvider)
2221

2322
//fallback
2423
var valueProvider = serviceProvider.GetService<IProvideValueTarget>() as IProvideParentValues
25-
?? throw new ArgumentException("serviceProvider does not provide an IProvideValueTarget");
24+
?? throw new ArgumentException("serviceProvider does not provide an IProvideValueTarget");
25+
2626
foreach (var target in valueProvider.ParentObjects)
2727
{
28-
if (!(target is BindableObject bo))
28+
if (target is not BindableObject bo)
2929
continue;
30-
if (!(NameScope.GetNameScope(bo) is INameScope ns))
30+
if (NameScope.GetNameScope(bo) is not INameScope ns)
3131
continue;
3232
value = ns.FindByName(Name);
3333
if (value != null)

src/Controls/src/Xaml/XamlProcessingAttribute.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Runtime.Versioning;
23

34
namespace Microsoft.Maui.Controls.Xaml;
45

src/Controls/src/Xaml/XamlServiceProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ public class SimpleValueTargetProvider : IProvideParentValues, IProvideValueTarg
152152
[Obsolete("Use the other ctor")]
153153
//used by XamlC
154154
public SimpleValueTargetProvider(object[] objectAndParents, object targetProperty, INameScope scope)
155-
: this(objectAndParents, targetProperty, new INameScope[] { scope }, false)
155+
: this(objectAndParents, targetProperty, [scope], false)
156156
{
157157
}
158158

src/Controls/tests/Xaml.UnitTests/Issues/Gh2007.xaml.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace Microsoft.Maui.Controls.Xaml.UnitTests;
44

5-
[XamlProcessing(XamlInflator.Default, true)]
5+
[XamlProcessing(XamlInflator.Runtime|XamlInflator.XamlC, true)]
66
public partial class Gh2007 : ContentPage
77
{
88
public Gh2007() => InitializeComponent();
@@ -15,11 +15,13 @@ public void UsefullxResourceErrorMessages([Values] XamlInflator inflator)
1515
{
1616
if (inflator == XamlInflator.Runtime || inflator == XamlInflator.XamlC)
1717
Assert.Throws<XamlParseException>(() => new Gh2007(inflator));
18-
if (inflator == XamlInflator.SourceGen)
18+
else if (inflator == XamlInflator.SourceGen)
1919
{
2020
var result = MockSourceGenerator.RunMauiSourceGenerator(MockSourceGenerator.CreateMauiCompilation(), typeof(Gh2007));
2121
Assert.That(result.Diagnostics, Is.Not.Empty);
2222
}
23+
else
24+
Assert.Ignore("ignoring test for {inflator} as it is not supported in this context");
2325
}
2426
}
2527
}

0 commit comments

Comments
 (0)