diff --git a/src/Controls/src/Xaml/SimplifyOnPlatformVisitor.cs b/src/Controls/src/Xaml/SimplifyOnPlatformVisitor.cs index 1f10ea7ef601..a3c34f3a437e 100644 --- a/src/Controls/src/Xaml/SimplifyOnPlatformVisitor.cs +++ b/src/Controls/src/Xaml/SimplifyOnPlatformVisitor.cs @@ -86,6 +86,16 @@ public void Visit(ElementNode node, INode parentNode) INode keyNode = null; bool hasKey = node.Properties.TryGetValue(XmlName.xKey, out keyNode); + // If no value found for target platform and no Default, + // create a default value node if we have x:TypeArguments + if (onNode == null && node.XmlType.TypeArguments != null && node.XmlType.TypeArguments.Count > 0) + { + // Create default value for the type (to match OnPlatform runtime behavior) + var typeArg = node.XmlType.TypeArguments[0]; + var elementNode = new ElementNode(typeArg, typeArg.NamespaceUri, node.NamespaceResolver, node.LineNumber, node.LinePosition); + onNode = elementNode; + } + // If OnPlatform has x:Key but the replacement node is a ValueNode, // check if we have x:TypeArguments to create a proper ElementNode if (hasKey && onNode is ValueNode valueNode) @@ -123,7 +133,11 @@ public void Visit(ElementNode node, INode parentNode) parentEnode.Properties[name] = onNode; } else - parentEnode.Properties.Remove(name); + { + // No value and no x:TypeArguments - skip simplification + // This maintains consistency with runtime OnPlatform behavior + return; + } return; } @@ -141,7 +155,11 @@ public void Visit(ElementNode node, INode parentNode) parentEnode2.CollectionItems[index] = onNode; } else - parentEnode2.CollectionItems.RemoveAt(index); + { + // No value and no x:TypeArguments - skip simplification + // This maintains consistency with runtime OnPlatform behavior + return; + } } } } diff --git a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs index e40d82f6566e..6d127b82aa66 100644 --- a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs +++ b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs @@ -352,4 +352,59 @@ public TestPage() Assert.Contains("Red", generated, StringComparison.Ordinal); Assert.DoesNotContain("Blue", generated, StringComparison.Ordinal); } + + [Fact] + public void OnPlatformWithMissingTargetPlatformShouldUseDefault() + { + // Reproduces Bugzilla39636: When MacCatalyst is not defined in OnPlatform, + // SourceGen should use default(T) instead of throwing an exception + var xaml = +""" + + + + + + + + + + +"""; + + var code = +""" +using System; +using Microsoft.Maui.Controls; +using Microsoft.Maui.Controls.Xaml; + +namespace Test; + +[XamlProcessing(XamlInflator.SourceGen)] +public partial class TestPage : ContentPage +{ + public TestPage() + { + InitializeComponent(); + } +} +"""; + + // Test with MacCatalyst where platform is not defined + var (result, generated) = RunGenerator(xaml, code, targetFramework: "net10.0-maccatalyst"); + + // Should not have any errors (no TargetInvocationException) + Assert.False(result.Diagnostics.Any(d => d.Severity == Microsoft.CodeAnalysis.DiagnosticSeverity.Error)); + + // Should generate SetValue with default(double) for the WidthRequest property + // The generated code should look like: label.SetValue(global::Microsoft.Maui.Controls.VisualElement.WidthRequestProperty, double1); + // where double1 is assigned from double0 which is: double double0 = default; + Assert.Contains("double double0 = default;", generated, StringComparison.Ordinal); + Assert.Contains("var double1 = double0;", generated, StringComparison.Ordinal); + Assert.Contains("label.SetValue(global::Microsoft.Maui.Controls.VisualElement.WidthRequestProperty, double1);", generated, StringComparison.Ordinal); + } } \ No newline at end of file diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml b/src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml new file mode 100644 index 000000000000..8cd573ad0327 --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml @@ -0,0 +1,14 @@ + + + + + + + + + + diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml.cs b/src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml.cs new file mode 100644 index 000000000000..00edd278a9fd --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml.cs @@ -0,0 +1,39 @@ +using Microsoft.Maui.Controls.Core.UnitTests; +using Microsoft.Maui.Devices; +using NUnit.Framework; + +namespace Microsoft.Maui.Controls.Xaml.UnitTests; + +public partial class Bugzilla39636 : ContentPage +{ + [TestFixture] + class Tests + { + MockDeviceInfo mockDeviceInfo; + + [SetUp] + public void Setup() + { + Application.SetCurrentApplication(new MockApplication()); + DeviceInfo.SetCurrent(mockDeviceInfo = new MockDeviceInfo()); + } + + [TearDown] public void TearDown() => DeviceInfo.SetCurrent(null); + + [Test] + public void OnPlatformWithMissingTargetPlatformShouldUseDefault([Values] XamlInflator inflator) + { + // Reproduces Bugzilla39636: When MacCatalyst is not defined in OnPlatform, + // all inflators should use default(T) instead of throwing an exception + + // Test with MacCatalyst where platform is not defined + mockDeviceInfo.Platform = DevicePlatform.MacCatalyst; + var page = new Bugzilla39636(inflator); + + // Should use default value (0.0 for double) instead of throwing + Assert.That(page.testLabel, Is.Not.Null); + Assert.That(page.testLabel.WidthRequest, Is.EqualTo(0.0)); + } + } +} +