Browse Source

Fix style without selector not finding target type (#18026)

* Add failing style test without selector

* Fix XAML target type of style without selector

* Address review

* Throw for style without selector in ControlTheme
pull/18049/head
Julien Lebosquain 1 year ago
committed by GitHub
parent
commit
01a7c859d4
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 55
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs
  2. 2
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  3. 52
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs

55
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs

@ -32,8 +32,26 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
var pn = on.Children.OfType<XamlAstXamlPropertyValueNode>()
.FirstOrDefault(p => p.Property.GetClrProperty().Name == "Selector");
// Missing selector, use the object's target type if available
if (pn == null)
{
// We already went through this node
if (context.ParentNodes().FirstOrDefault() is AvaloniaXamlIlTargetTypeMetadataNode metadataNode
&& metadataNode.Value == on)
{
return node;
}
if (FindStyleParentObject(on, context) is { } parentObjectNode)
{
return new AvaloniaXamlIlTargetTypeMetadataNode(
on,
new XamlAstClrTypeReference(node, parentObjectNode.Type.GetClrType(), false),
AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style);
}
return node;
}
if (pn.Values.Count != 1)
throw new XamlSelectorsTransformException("Selector property should have exactly one value",
@ -195,6 +213,20 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
pn.Values[0] = selector;
var templateType = GetLastTemplateTypeFromSelector(selector);
// Empty selector, use the object's target type if available
if (selector == initialNode)
{
if (FindStyleParentObject(on, context) is { } parentObjectNode)
{
return new AvaloniaXamlIlTargetTypeMetadataNode(
on,
new XamlAstClrTypeReference(node, parentObjectNode.Type.GetClrType(), false),
AvaloniaXamlIlTargetTypeMetadataNode.ScopeTypes.Style);
}
return node;
}
var styleNode = new AvaloniaXamlIlTargetTypeMetadataNode(on,
new XamlAstClrTypeReference(selector, selector.TargetType!, false),
@ -209,6 +241,29 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
};
}
private static XamlAstObjectNode? FindStyleParentObject(XamlAstNode styleNode, AstTransformationContext context)
{
var avaloniaTypes = context.GetAvaloniaTypes();
var parentNode = context
.ParentNodes()
.OfType<XamlAstObjectNode>()
.FirstOrDefault(n => !avaloniaTypes.Styles.IsAssignableFrom(n.Type.GetClrType()));
if (parentNode is not null)
{
var parentType = parentNode.Type.GetClrType();
if (avaloniaTypes.StyledElement.IsAssignableFrom(parentType))
return parentNode;
if (avaloniaTypes.ControlTheme.IsAssignableFrom(parentType))
throw new XamlTransformException("Cannot add a Style without selector to a ControlTheme.", styleNode);
}
return null;
}
private static IXamlType? GetLastTemplateTypeFromSelector(XamlIlSelectorNode? node)
{
while (node is not null)

2
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs

@ -126,6 +126,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlType UriKind { get; }
public IXamlConstructor UriConstructor { get; }
public IXamlType Style { get; }
public IXamlType Styles { get; }
public IXamlType ControlTheme { get; }
public IXamlType WindowTransparencyLevel { get; }
public IXamlType IReadOnlyListOfT { get; }
@ -325,6 +326,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
UriKind = cfg.TypeSystem.GetType("System.UriKind");
UriConstructor = Uri.GetConstructor(new List<IXamlType>() { cfg.WellKnownTypes.String, UriKind });
Style = cfg.TypeSystem.GetType("Avalonia.Styling.Style");
Styles = cfg.TypeSystem.GetType("Avalonia.Styling.Styles");
ControlTheme = cfg.TypeSystem.GetType("Avalonia.Styling.ControlTheme");
ControlTemplate = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Templates.ControlTemplate");
IReadOnlyListOfT = cfg.TypeSystem.GetType("System.Collections.Generic.IReadOnlyList`1");

52
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs

@ -15,6 +15,7 @@ using Xunit;
namespace Avalonia.Markup.Xaml.UnitTests.Xaml
{
[InvariantCulture]
public class StyleTests : XamlTestBase
{
[Fact]
@ -713,9 +714,6 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
[Fact]
public void Fails_Use_Classes_In_Setter_When_Selector_Is_Complex()
{
// XmlException contains culture specific position message
Thread.CurrentThread.CurrentUICulture = CultureInfo.InvariantCulture;
using (UnitTestApplication.Start(TestServices.StyledWindow))
{
var xaml = $"""
@ -739,5 +737,53 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
Assert.Equal ("Cannot set Classes Binding property '(Classes.Banned)' because the style has an activator. Line 6, position 14.", exception.Message);
}
}
[Theory]
[InlineData("<Style>", "</Style>")]
[InlineData("<Style Selector=''>", "</Style>")]
[InlineData("<Styles><Style>", "</Style></Styles>")]
[InlineData("<Styles><Style Selector=''>", "</Style></Styles>")]
public void No_Selector_Should_Target_Parent_Type(string styleStart, string styleEnd)
{
using var app = UnitTestApplication.Start(TestServices.StyledWindow);
var window = (Window)AvaloniaRuntimeXamlLoader.Load(
$"""
<Window xmlns="https://github.com/avaloniaui">
<Window.Styles>
{styleStart}
<Setter Property="Title" Value="title set via style!" />
{styleEnd}
</Window.Styles>
</Window>
""");
Assert.Equal("title set via style!", window.Title);
}
[Theory]
[InlineData("<Style>", "</Style>")]
[InlineData("<Style Selector=''>", "</Style>")]
public void No_Selector_Should_Fail_In_Control_Theme(string styleStart, string styleEnd)
{
using var app = UnitTestApplication.Start(TestServices.StyledWindow);
var exception = Assert.ThrowsAny<XmlException>(() => (Window)AvaloniaRuntimeXamlLoader.Load(
$$"""
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.Resources>
<ControlTheme x:Key="{x:Type Window}" TargetType="Window">
{{styleStart}}
<Setter Property="Title" Value="title set via style!" />
{{styleEnd}}
</ControlTheme>
</Window.Resources>
</Window>
"""));
Assert.Equal("Cannot add a Style without selector to a ControlTheme. Line 5, position 14.", exception.Message);
}
}
}

Loading…
Cancel
Save