Skip to content

Commit

Permalink
fix(XamlGen): Handle base type not specified in code-behind
Browse files Browse the repository at this point in the history
  • Loading branch information
Youssef1313 committed Sep 29, 2022
1 parent d422e71 commit 8af10a0
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Uno.UI.SourceGenerators.Tests.Verifiers;
using Microsoft.CodeAnalysis.Testing;
using Uno.UI.SourceGenerators.Tests.Verifiers;

namespace Uno.UI.SourceGenerators.Tests.Windows_UI_Xaml_Data.BindingTests;

Expand Down Expand Up @@ -33,4 +34,86 @@ public async Task When_Binding_ElementName_In_Template()
};
await Verify.AssertXamlGeneratorDiagnostics(test);
}

[TestMethod]
public async Task TestBaseTypeNotSpecifiedInCodeBehind()
{
var xamlFiles = new[]
{
new XamlFile("UserControl1.xaml", """
<UserControl
x:Class="TestRepro.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TestRepro"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid></Grid>
</UserControl>
"""),
new XamlFile("MainPage.xaml", """
<Page
x:Class="TestRepro.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TestRepro"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<TextBlock Text="Hello, world!" Margin="20" FontSize="30" />
<local:UserControl1 DataContext="{Binding PreviewDropViewModel}"/>
</Grid>
</Page>
"""),
};
var test = new Verify.Test(xamlFiles)
{
TestState =
{
Sources =
{
"""
namespace TestRepro
{
public sealed partial class UserControl1
{
public UserControl1()
{
this.InitializeComponent();
}
}
}
""",
"""
using Windows.UI.Xaml.Controls;
namespace TestRepro
{
public sealed partial class MainPage : Page
{
public string PreviewDropViewModel { get; set; }
public MainPage()
{
this.InitializeComponent();
}
}
}
"""
}
}
};
test.ExpectedDiagnostics.Add(
// /0/Test0.cs(3,30): warning UXAML0002: TestRepro.UserControl1 does not explicitly define the Windows.UI.Xaml.Controls.UserControl base type in code behind.
DiagnosticResult.CompilerWarning("UXAML0002").WithSpan(3, 30, 3, 42).WithArguments("TestRepro.UserControl1 does not explicitly define the Windows.UI.Xaml.Controls.UserControl base type in code behind.")
);
await test.RunAsync();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,23 @@ public List<KeyValuePair<string, string>> Generate(GenerationRunInfo generationR
var resourceKeys = GetResourceKeys(_generatorContext.CancellationToken);
var filesFull = new XamlFileParser(_excludeXamlNamespaces, _includeXamlNamespaces, _metadataHelper)
.ParseFiles(_xamlSourceFiles, _generatorContext.CancellationToken);

var xamlTypeToXamlTypeBaseMap = new Dictionary<INamedTypeSymbol, XamlRedirection.XamlType>();
foreach (var file in filesFull)
{
var topLevelControl = file.Objects.FirstOrDefault();
if (topLevelControl is null)
{
continue;
}

var xClassSymbol = XamlFileGenerator.FindClassSymbol(topLevelControl, _metadataHelper);
if (xClassSymbol is not null)
{
xamlTypeToXamlTypeBaseMap.Add(xClassSymbol, topLevelControl.Type);
}
}

var files = filesFull
.Trim()
.OrderBy(f => f.UniqueID)
Expand Down Expand Up @@ -336,7 +353,8 @@ public List<KeyValuePair<string, string>> Generate(GenerationRunInfo generationR
isLazyVisualStateManagerEnabled: _isLazyVisualStateManagerEnabled,
generatorContext: _generatorContext,
xamlResourcesTrimming: _xamlResourcesTrimming,
generationRunFileInfo: generationRunInfo.GetRunFileInfo(file.UniqueID)
generationRunFileInfo: generationRunInfo.GetRunFileInfo(file.UniqueID),
xamlTypeToXamlTypeBaseMap: xamlTypeToXamlTypeBaseMap
)
.GenerateFile()
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ internal partial class XamlFileGenerator
/// </summary>
private readonly GenerationRunFileInfo _generationRunFileInfo;

private readonly Dictionary<INamedTypeSymbol, XamlType> _xamlTypeToXamlTypeBaseMap;

/// <summary>
/// Information about types used in .Apply() scenarios
/// </summary>
Expand Down Expand Up @@ -238,7 +240,8 @@ public XamlFileGenerator(
bool isLazyVisualStateManagerEnabled,
GeneratorExecutionContext generatorContext,
bool xamlResourcesTrimming,
GenerationRunFileInfo generationRunFileInfo)
GenerationRunFileInfo generationRunFileInfo,
Dictionary<INamedTypeSymbol, XamlType> xamlTypeToXamlTypeBaseMap)
{
_fileDefinition = file;
_targetPath = targetPath;
Expand All @@ -261,6 +264,7 @@ public XamlFileGenerator(
_generatorContext = generatorContext;
_xamlResourcesTrimming = xamlResourcesTrimming;
_generationRunFileInfo = generationRunFileInfo;
_xamlTypeToXamlTypeBaseMap = xamlTypeToXamlTypeBaseMap;

InitCaches();

Expand Down Expand Up @@ -2213,6 +2217,21 @@ private XClassName GetClassName(XamlObjectDefinition control)
}
}

internal static INamedTypeSymbol? FindClassSymbol(XamlObjectDefinition control, RoslynMetadataHelper metadataHelper)
{
var classMember = control.Members.FirstOrDefault(m => m.Member.Name == "Class");

if (classMember?.Value != null)
{
var fullName = classMember.Value.ToString() ?? "";
return metadataHelper.FindTypeByFullName(fullName) as INamedTypeSymbol;
}
else
{
return null;
}
}

[MemberNotNull(nameof(_xClassName))]
private void EnsureXClassName()
{
Expand Down Expand Up @@ -3999,7 +4018,11 @@ private void BuildComplexPropertyValue(IIndentedStringBuilder writer, XamlMember
TryAnnotateWithGeneratorSource(writer, suffix: "HasBindingOptions");
var isAttachedProperty = IsDependencyProperty(member.Member);
var isBindingType = SymbolEqualityComparer.Default.Equals(FindPropertyType(member.Member), _dataBindingSymbol);
var isOwnerDependencyObject = member.Owner != null && GetType(member.Owner.Type).GetAllInterfaces().Any(i => SymbolEqualityComparer.Default.Equals(i, _dependencyObjectSymbol));
var isOwnerDependencyObject = member.Owner != null && GetType(member.Owner.Type) is { } ownerType &&
(
(_xamlTypeToXamlTypeBaseMap.TryGetValue(ownerType, out var baseTypeSymbol) && FindType(baseTypeSymbol)?.GetAllInterfaces().Any(i => SymbolEqualityComparer.Default.Equals(i, _dependencyObjectSymbol)) == true) ||
ownerType.GetAllInterfaces().Any(i => SymbolEqualityComparer.Default.Equals(i, _dependencyObjectSymbol))
);

if (isAttachedProperty)
{
Expand Down

0 comments on commit 8af10a0

Please sign in to comment.