Browse Source
* Implement AVLN2208 diagnostic - ItemContainerInsideTemplate * Add AVLN2208 tests * Enable AVLN2208 as an error in the repository globally * Fix invalid ListBoxItem inside of DataTemplate in devtoolspull/18154/head
committed by
GitHub
9 changed files with 199 additions and 39 deletions
@ -0,0 +1,79 @@ |
|||||
|
using System; |
||||
|
using System.Collections; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; |
||||
|
using XamlX; |
||||
|
using XamlX.Ast; |
||||
|
using XamlX.Transform; |
||||
|
using XamlX.TypeSystem; |
||||
|
|
||||
|
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers; |
||||
|
|
||||
|
#if !XAMLX_INTERNAL
|
||||
|
public |
||||
|
#endif
|
||||
|
class AvaloniaXamlIlDataTemplateWarningsTransformer : IXamlAstTransformer |
||||
|
{ |
||||
|
public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node) |
||||
|
{ |
||||
|
var avaloniaTypes = context.GetAvaloniaTypes(); |
||||
|
var contentControl = context.GetAvaloniaTypes().ContentControl; |
||||
|
|
||||
|
// This transformers only looks for ContentControl delivered objects inside of DataTemplate
|
||||
|
if ((node is not XamlAstObjectNode objectNode) |
||||
|
|| !contentControl.IsAssignableFrom(objectNode.Type.GetClrType()) |
||||
|
|| context.ParentNodes().FirstOrDefault() is not XamlAstObjectNode parentNode |
||||
|
|| !avaloniaTypes.IDataTemplate.IsAssignableFrom(parentNode.Type.GetClrType())) |
||||
|
{ |
||||
|
return node; |
||||
|
} |
||||
|
|
||||
|
// And only inside of ItemTemplate or DataTemplates property value.
|
||||
|
if (context.ParentNodes().OfType<XamlAstXamlPropertyValueNode>().FirstOrDefault() is not { } valueNode |
||||
|
|| valueNode.Property.GetClrProperty() is not { } clrProperty |
||||
|
|| !((clrProperty.Name == "ItemTemplate" && clrProperty.DeclaringType == avaloniaTypes.ItemsControl) |
||||
|
|| (clrProperty.Name == "DataTemplates" && clrProperty.DeclaringType == avaloniaTypes.Control))) |
||||
|
{ |
||||
|
return node; |
||||
|
} |
||||
|
|
||||
|
// And only inside of ItemsControl
|
||||
|
if (context.ParentNodes().SkipWhile(p => p != valueNode) |
||||
|
.OfType<XamlAstObjectNode>().FirstOrDefault() is not { } itemsControlNode |
||||
|
|| !avaloniaTypes.ItemsControl.IsAssignableFrom(itemsControlNode.Type.GetClrType())) |
||||
|
{ |
||||
|
return node; |
||||
|
} |
||||
|
|
||||
|
// Avalonia doesn't have any reliable way to determine container type from the API.
|
||||
|
if (GetKnownItemContainerTypeFullName(itemsControlNode.Type.GetClrType()) is not { } knownItemContainerTypeName |
||||
|
|| itemsControlNode.Type.GetClrType().Assembly?.FindType(knownItemContainerTypeName) is not { } knownItemContainerType) |
||||
|
{ |
||||
|
return node; |
||||
|
} |
||||
|
|
||||
|
if (knownItemContainerType.IsAssignableFrom(objectNode.Type.GetClrType())) |
||||
|
{ |
||||
|
context.ReportDiagnostic(new XamlDiagnostic( |
||||
|
AvaloniaXamlDiagnosticCodes.ItemContainerInsideTemplate, |
||||
|
XamlDiagnosticSeverity.Warning, |
||||
|
$"Unexpected '{knownItemContainerType.Name}' inside of '{itemsControlNode.Type.GetClrType().Name}.{clrProperty.Name}'. " |
||||
|
+ $"'{itemsControlNode.Type.GetClrType().Name}.{clrProperty.Name}' defines template of the container content, not the container itself.", node)); |
||||
|
} |
||||
|
|
||||
|
return node; |
||||
|
} |
||||
|
|
||||
|
private static string? GetKnownItemContainerTypeFullName(IXamlType itemsControlType) => itemsControlType.FullName switch |
||||
|
{ |
||||
|
"Avalonia.Controls.ListBox" => "Avalonia.Controls.ListBoxItem", |
||||
|
"Avalonia.Controls.ComboBox" => "Avalonia.Controls.ComboBoxItem", |
||||
|
"Avalonia.Controls.Menu" => "Avalonia.Controls.MenuItem", |
||||
|
"Avalonia.Controls.MenuItem" => "Avalonia.Controls.MenuItem", |
||||
|
"Avalonia.Controls.Primitives.TabStrip" => "Avalonia.Controls.Primitives.TabStripItem", |
||||
|
"Avalonia.Controls.TabControl" => "Avalonia.Controls.TabItem", |
||||
|
"Avalonia.Controls.TreeView" => "Avalonia.Controls.TreeViewItem", |
||||
|
_ => null |
||||
|
}; |
||||
|
} |
||||
Loading…
Reference in new issue