Browse Source

throw XamlParseException if there are duplicate property setters in style or control theme

pull/10113/head
Dmitry Zhelnin 3 years ago
committed by DmitryZhelnin
parent
commit
a2c7f67c41
  1. 1
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
  2. 44
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDuplicateSettersChecker.cs
  3. 41
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs

1
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs

@ -49,6 +49,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
InsertBefore<ContentConvertTransformer>(
new AvaloniaXamlIlControlThemeTransformer(),
new AvaloniaXamlIlSelectorTransformer(),
new AvaloniaXamlIlDuplicateSettersChecker(),
new AvaloniaXamlIlControlTemplateTargetTypeMetadataTransformer(),
new AvaloniaXamlIlBindingPathParser(),
new AvaloniaXamlIlPropertyPathTransformer(),

44
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlDuplicateSettersChecker.cs

@ -0,0 +1,44 @@
using System.Collections.Generic;
using System.Linq;
using XamlX;
using XamlX.Ast;
using XamlX.Transform;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
class AvaloniaXamlIlDuplicateSettersChecker : IXamlAstTransformer
{
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
{
if (node is not XamlAstObjectNode objectNode)
{
return node;
}
var fullName = objectNode.Type.GetClrType().FullName;
if (fullName is not ("Avalonia.Styling.Style" or "Avalonia.Styling.ControlTheme"))
{
return node;
}
var properties = objectNode.Children
.OfType<XamlAstObjectNode>()
.Where(n => n.Type.GetClrType().Name == "Setter")
.SelectMany(setter =>
setter.Children.OfType<XamlAstXamlPropertyValueNode>()
.Where(c => c.Property.GetClrProperty().Name == "Property"))
.Select(p => p.Values[0])
.OfType<XamlAstTextNode>()
.Select(x => x.Text);
var index = new HashSet<string>();
foreach (var property in properties)
{
if (!index.Add(property))
{
throw new XamlParseException($"Duplicate setter encountered for property '{property}'", node);
}
}
return node;
}
}

41
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/XamlIlTests.cs

@ -334,6 +334,47 @@ namespace Avalonia.Markup.Xaml.UnitTests
var parsed = (Button)AvaloniaRuntimeXamlLoader.Load(document);
Assert.Equal(Colors.Blue, ((ISolidColorBrush)parsed.Background!).Color);
}
[Fact]
public void Style_Parser_Throws_For_Duplicate_Setter()
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:local='clr-namespace:Avalonia.Markup.Xaml.UnitTests.Xaml;assembly=Avalonia.Markup.Xaml.UnitTests'>
<Window.Styles>
<Style Selector='TextBlock'>
<Setter Property='Width' Value='100'/>
<Setter Property='Height' Value='20'/>
<Setter Property='Height' Value='30'/>
</Style>
</Window.Styles>
<TextBlock/>
</Window>";
AssertThrows(() => AvaloniaRuntimeXamlLoader.Load(xaml, typeof(XamlIlTests).Assembly, designMode: true),
e => e.Message.StartsWith("Duplicate setter encountered for property 'Height'"));
}
[Fact]
public void Control_Theme_Parser_Throws_For_Duplicate_Setter()
{
var xaml = @"
<Window xmlns='https://github.com/avaloniaui'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
xmlns:u='using:Avalonia.Markup.Xaml.UnitTests.Xaml'>
<Window.Resources>
<ControlTheme x:Key='MyTheme' TargetType='u:TestTemplatedControl'>
<Setter Property='Width' Value='100'/>
<Setter Property='Height' Value='20'/>
<Setter Property='Height' Value='30'/>
</ControlTheme>
</Window.Resources>
<u:TestTemplatedControl Theme='{StaticResource MyTheme}'/>
</Window>";
AssertThrows(() => AvaloniaRuntimeXamlLoader.Load(xaml, typeof(XamlIlTests).Assembly, designMode: true),
e => e.Message.StartsWith("Duplicate setter encountered for property 'Height'"));
}
}
public class XamlIlBugTestsEventHandlerCodeBehind : Window

Loading…
Cancel
Save