Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions src/Controls/src/Xaml/SimplifyOnPlatformVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> 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)
Expand Down Expand Up @@ -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<T> behavior
return;
}
return;
}

Expand All @@ -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<T> behavior
return;
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
"""
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Test.TestPage">
<ContentPage.Resources>
<OnPlatform x:Key="SizeMedium" x:TypeArguments="x:Double">
<On Platform="iOS" Value="40"/>
<On Platform="Android" Value="30"/>
<On Platform="UWP" Value="60"/>
</OnPlatform>
</ContentPage.Resources>
<Label Text="Test" WidthRequest="{StaticResource SizeMedium}" />
</ContentPage>
""";

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);
}
}
14 changes: 14 additions & 0 deletions src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Microsoft.Maui.Controls.Xaml.UnitTests"
x:Class="Microsoft.Maui.Controls.Xaml.UnitTests.Bugzilla39636">
<ContentPage.Resources>
<OnPlatform x:Key="SizeMedium" x:TypeArguments="x:Double">
<On Platform="iOS" Value="40"/>
<On Platform="Android" Value="30"/>
<On Platform="UWP" Value="60"/>
</OnPlatform>
</ContentPage.Resources>
<Label x:Name="testLabel" Text="Test" WidthRequest="{StaticResource SizeMedium}" />
</ContentPage>
39 changes: 39 additions & 0 deletions src/Controls/tests/Xaml.UnitTests/Issues/Bugzilla39636.xaml.cs
Original file line number Diff line number Diff line change
@@ -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));
}
}
}

Loading