From f1c462d1cd9f1c5dd7d05e1feb34877b367304c5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 20 Nov 2025 15:02:03 +0000 Subject: [PATCH 1/9] Initial plan From df2dbc41733303ad37302e44b7237e5c228e7cc1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 20 Nov 2025 15:21:22 +0000 Subject: [PATCH 2/9] Fix OnPlatform SourceGen to use default value instead of removing property Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> --- .../src/Xaml/SimplifyOnPlatformVisitor.cs | 22 +++++++- .../InitializeComponent/SimplifyOnPlatform.cs | 55 +++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) 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..7dfdacec95bf 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 code that creates the resource with default value + // The resource key should still be present + Assert.Contains("SizeMedium", generated, StringComparison.Ordinal); + + // The OnPlatform should be simplified to a default Double value + Assert.DoesNotContain("OnPlatform", generated, StringComparison.Ordinal); + } } \ No newline at end of file From 9f48d2578387a217731f82eb142bec8ae995f3c3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 20 Nov 2025 15:24:13 +0000 Subject: [PATCH 3/9] Add runtime test to verify OnPlatform default value behavior Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> --- .../tests/Xaml.UnitTests/OnPlatformTests.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/Controls/tests/Xaml.UnitTests/OnPlatformTests.cs b/src/Controls/tests/Xaml.UnitTests/OnPlatformTests.cs index c6a47525e1b9..fa6ee523d387 100644 --- a/src/Controls/tests/Xaml.UnitTests/OnPlatformTests.cs +++ b/src/Controls/tests/Xaml.UnitTests/OnPlatformTests.cs @@ -139,6 +139,36 @@ public void ChecksPreferWinUI() image = new Image().LoadFromXaml(xaml); Assert.AreEqual("Images/icon_twitter_preferred.png", (image.Source as FileImageSource).File); } + + [Test] + public void MissingPlatformUsesDefaultValue() + { + // Reproduces Bugzilla39636: When MacCatalyst is not defined in OnPlatform, + // runtime should use default(T) instead of throwing an exception + var xaml = @" + + + + + + + + + "; + + ContentPage page; + + // Test with MacCatalyst where platform is not defined + mockDeviceInfo.Platform = DevicePlatform.MacCatalyst; + page = new ContentPage().LoadFromXaml(xaml); + + // Should use default value (0.0 for double) instead of throwing + var label = page.Content as Label; + Assert.NotNull(label); + Assert.AreEqual(0.0, label.WidthRequest); + } } [TestFixture] From 1cadffd4901afd6e0a0f44c9f2a309c3ec2fba48 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 20 Nov 2025 18:54:10 +0000 Subject: [PATCH 4/9] Address PR review feedback - move runtime test to shared inflator test Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> --- .../InitializeComponent/SimplifyOnPlatform.cs | 8 ++-- .../Xaml.UnitTests/Issues/Bugzilla39636.xaml | 14 +++++++ .../Issues/Bugzilla39636.xaml.cs | 42 +++++++++++++++++++ .../tests/Xaml.UnitTests/OnPlatformTests.cs | 30 ------------- 4 files changed, 61 insertions(+), 33 deletions(-) create mode 100644 src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml create mode 100644 src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml.cs diff --git a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs index 7dfdacec95bf..6dcb198b2124 100644 --- a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs +++ b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs @@ -401,10 +401,12 @@ public TestPage() Assert.False(result.Diagnostics.Any(d => d.Severity == Microsoft.CodeAnalysis.DiagnosticSeverity.Error)); // Should generate code that creates the resource with default value - // The resource key should still be present Assert.Contains("SizeMedium", generated, StringComparison.Ordinal); - // The OnPlatform should be simplified to a default Double value - Assert.DoesNotContain("OnPlatform", generated, StringComparison.Ordinal); + // 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("label.SetValue(global::Microsoft.Maui.Controls.VisualElement.WidthRequestProperty,", 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..1087d01277d6 --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml.cs @@ -0,0 +1,42 @@ +using Microsoft.Maui.Controls.Core.UnitTests; +using Microsoft.Maui.Devices; +using NUnit.Framework; + +namespace Microsoft.Maui.Controls.Xaml.UnitTests; + +public partial class Bugzilla39636 : ContentPage +{ + public Bugzilla39636() => InitializeComponent(); + + [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.Runtime, XamlInflator.SourceGen, XamlInflator.XamlC)] 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)); + } + } +} + diff --git a/src/Controls/tests/Xaml.UnitTests/OnPlatformTests.cs b/src/Controls/tests/Xaml.UnitTests/OnPlatformTests.cs index fa6ee523d387..c6a47525e1b9 100644 --- a/src/Controls/tests/Xaml.UnitTests/OnPlatformTests.cs +++ b/src/Controls/tests/Xaml.UnitTests/OnPlatformTests.cs @@ -139,36 +139,6 @@ public void ChecksPreferWinUI() image = new Image().LoadFromXaml(xaml); Assert.AreEqual("Images/icon_twitter_preferred.png", (image.Source as FileImageSource).File); } - - [Test] - public void MissingPlatformUsesDefaultValue() - { - // Reproduces Bugzilla39636: When MacCatalyst is not defined in OnPlatform, - // runtime should use default(T) instead of throwing an exception - var xaml = @" - - - - - - - - - "; - - ContentPage page; - - // Test with MacCatalyst where platform is not defined - mockDeviceInfo.Platform = DevicePlatform.MacCatalyst; - page = new ContentPage().LoadFromXaml(xaml); - - // Should use default value (0.0 for double) instead of throwing - var label = page.Content as Label; - Assert.NotNull(label); - Assert.AreEqual(0.0, label.WidthRequest); - } } [TestFixture] From facc2a1559282f9092ee00b7db60366bf479f7a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Rozs=C3=ADval?= Date: Fri, 21 Nov 2025 07:56:29 +0000 Subject: [PATCH 5/9] Improve assertion --- .../InitializeComponent/SimplifyOnPlatform.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs index 6dcb198b2124..15a5f5a56873 100644 --- a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs +++ b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs @@ -399,14 +399,11 @@ public TestPage() // Should not have any errors (no TargetInvocationException) Assert.False(result.Diagnostics.Any(d => d.Severity == Microsoft.CodeAnalysis.DiagnosticSeverity.Error)); - - // Should generate code that creates the resource with default value - Assert.Contains("SizeMedium", generated, StringComparison.Ordinal); // 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("label.SetValue(global::Microsoft.Maui.Controls.VisualElement.WidthRequestProperty,", generated, StringComparison.Ordinal); + Assert.Contains("label.SetValue(global::Microsoft.Maui.Controls.VisualElement.WidthRequestProperty, double0);", generated, StringComparison.Ordinal); } } \ No newline at end of file From dee492ac087d54fe04881d015387d892012a8a9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Rozs=C3=ADval?= Date: Fri, 21 Nov 2025 08:00:43 +0000 Subject: [PATCH 6/9] Simplify test setup --- src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml.cs b/src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml.cs index 1087d01277d6..2ff2cf5899c4 100644 --- a/src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml.cs +++ b/src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml.cs @@ -23,8 +23,7 @@ public void Setup() [TearDown] public void TearDown() => DeviceInfo.SetCurrent(null); [Test] - public void OnPlatformWithMissingTargetPlatformShouldUseDefault( - [Values(XamlInflator.Runtime, XamlInflator.SourceGen, XamlInflator.XamlC)] XamlInflator inflator) + 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 From a7be478dd155766a12bb3f0ac743b3b8c9c78c75 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Fri, 21 Nov 2025 22:29:57 +0100 Subject: [PATCH 7/9] Fix test --- .../InitializeComponent/SimplifyOnPlatform.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs index 15a5f5a56873..6d127b82aa66 100644 --- a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs +++ b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs @@ -404,6 +404,7 @@ public TestPage() // 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("label.SetValue(global::Microsoft.Maui.Controls.VisualElement.WidthRequestProperty, double0);", 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 From ca683e0b69ecf7c9c5d698446c2d4694dc83ab67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Rozs=C3=ADval?= Date: Mon, 24 Nov 2025 10:46:03 +0100 Subject: [PATCH 8/9] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml.cs b/src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml.cs index 2ff2cf5899c4..7a51e34b8f08 100644 --- a/src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml.cs +++ b/src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml.cs @@ -7,6 +7,7 @@ namespace Microsoft.Maui.Controls.Xaml.UnitTests; public partial class Bugzilla39636 : ContentPage { public Bugzilla39636() => InitializeComponent(); + public Bugzilla39636(XamlInflator inflator) => InitializeComponent(inflator); [TestFixture] class Tests From 0f8ff251b3a73603ed7fa2e5a364d8aabc291ac0 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 24 Nov 2025 15:13:42 +0100 Subject: [PATCH 9/9] Remove custom constructors in test --- src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml.cs b/src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml.cs index 7a51e34b8f08..00edd278a9fd 100644 --- a/src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml.cs +++ b/src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml.cs @@ -6,9 +6,6 @@ namespace Microsoft.Maui.Controls.Xaml.UnitTests; public partial class Bugzilla39636 : ContentPage { - public Bugzilla39636() => InitializeComponent(); - public Bugzilla39636(XamlInflator inflator) => InitializeComponent(inflator); - [TestFixture] class Tests {