From 9d95566157b85e78f2605ffe0e37b863f73b2be5 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Mon, 8 Sep 2025 14:28:45 +0200 Subject: [PATCH 1/6] [XSG] Use absolute file paths when generating #line --- src/Controls/src/SourceGen/PrePost.cs | 9 ++--- src/Controls/src/SourceGen/ProjectItem.cs | 7 +++- .../Visitors/SetPropertiesVisitor.cs | 6 ++-- .../InitializeComponent/LineInfoTests.cs | 36 +++++++++++++++++++ .../SourceGenXamlInitializeComponentTests.cs | 6 +++- 5 files changed, 55 insertions(+), 9 deletions(-) create mode 100644 src/Controls/tests/SourceGen.UnitTests/InitializeComponent/LineInfoTests.cs diff --git a/src/Controls/src/SourceGen/PrePost.cs b/src/Controls/src/SourceGen/PrePost.cs index fed5b62057b0..9a1ff8401d00 100644 --- a/src/Controls/src/SourceGen/PrePost.cs +++ b/src/Controls/src/SourceGen/PrePost.cs @@ -15,15 +15,16 @@ class PrePost : IDisposable /// /// /// - public static PrePost NewLineInfo(IndentedTextWriter codeWriter, IXmlLineInfo iXmlLineInfo, string? fileName) + public static PrePost NewLineInfo(IndentedTextWriter codeWriter, IXmlLineInfo iXmlLineInfo, ProjectItem? projectItem) { - static void LineInfo(IndentedTextWriter codeWriter, IXmlLineInfo iXmlLineInfo, string? fileName) - => codeWriter.WriteLineNoTabs($"#line {(iXmlLineInfo.LineNumber != -1 ? iXmlLineInfo.LineNumber : 1)} \"{fileName}\""); + // Emit #line with an absolute path since relative paths have undefined behavior (https://github.com/dotnet/roslyn/issues/71202#issuecomment-1874649780) + static void LineInfo(IndentedTextWriter codeWriter, IXmlLineInfo iXmlLineInfo, ProjectItem? projectItem) + => codeWriter.WriteLineNoTabs($"#line {(iXmlLineInfo.LineNumber != -1 ? iXmlLineInfo.LineNumber : 1)} \"{projectItem?.AbsolutePath}\""); static void LineDefault(IndentedTextWriter codeWriter, IXmlLineInfo iXmlLineInfo) => codeWriter.WriteLineNoTabs("#line default"); - return new(() => LineInfo(codeWriter, iXmlLineInfo, fileName), () => LineDefault(codeWriter, iXmlLineInfo)); + return new(() => LineInfo(codeWriter, iXmlLineInfo, projectItem), () => LineDefault(codeWriter, iXmlLineInfo)); } public static PrePost NoBlock() => diff --git a/src/Controls/src/SourceGen/ProjectItem.cs b/src/Controls/src/SourceGen/ProjectItem.cs index 7a935f87ec40..bdc1c4f46282 100644 --- a/src/Controls/src/SourceGen/ProjectItem.cs +++ b/src/Controls/src/SourceGen/ProjectItem.cs @@ -7,6 +7,8 @@ namespace Microsoft.Maui.Controls.SourceGen; record ProjectItem(AdditionalText AdditionalText, AnalyzerConfigOptions Options) { + private readonly AdditionalText _additionalText = AdditionalText; + public string Configuration => Options.GetValueOrDefault("build_property.Configuration", "Debug"); @@ -62,9 +64,12 @@ public string NoWarn public string? RelativePath => Options.GetValueOrNull("build_metadata.additionalfiles.RelativePath"); + public string? AbsolutePath + => _additionalText.Path; + public string? TargetFramework => Options.GetValueOrNull("build_property.targetFramework"); public string? TargetPath - => Options.GetValueOrDefault("build_metadata.additionalfiles.TargetPath", AdditionalText.Path); + => Options.GetValueOrDefault("build_metadata.additionalfiles.TargetPath", _additionalText.Path); } diff --git a/src/Controls/src/SourceGen/Visitors/SetPropertiesVisitor.cs b/src/Controls/src/SourceGen/Visitors/SetPropertiesVisitor.cs index b0ac5db8e972..0522c3a43415 100644 --- a/src/Controls/src/SourceGen/Visitors/SetPropertiesVisitor.cs +++ b/src/Controls/src/SourceGen/Visitors/SetPropertiesVisitor.cs @@ -474,14 +474,14 @@ static void Set(IndentedTextWriter writer, LocalVariable parentVar, string local if (node is ValueNode valueNode) { - using (context.ProjectItem.EnableLineInfo ? PrePost.NewLineInfo(writer, (IXmlLineInfo)node, context.ProjectItem.RelativePath) : PrePost.NoBlock()) + using (context.ProjectItem.EnableLineInfo ? PrePost.NewLineInfo(writer, (IXmlLineInfo)node, context.ProjectItem) : PrePost.NoBlock()) { var valueString = valueNode.ConvertTo(property, context, parentVar); writer.WriteLine($"{parentVar.Name}.{EscapeIdentifier(localName)} = {valueString};"); } } else if (node is ElementNode elementNode) - using (context.ProjectItem.EnableLineInfo ? PrePost.NewLineInfo(writer, (IXmlLineInfo)node, context.ProjectItem.RelativePath) : PrePost.NoBlock()) + using (context.ProjectItem.EnableLineInfo ? PrePost.NewLineInfo(writer, (IXmlLineInfo)node, context.ProjectItem) : PrePost.NoBlock()) writer.WriteLine($"{parentVar.Name}.{EscapeIdentifier(localName)} = ({property.Type.ToFQDisplayString()}){(HasDoubleImplicitConversion(context.Variables[elementNode].Type, property.Type, context, out var conv) ? "(" + conv!.ReturnType.ToFQDisplayString() + ")" : string.Empty)}{context.Variables[elementNode].Name};"); } @@ -610,7 +610,7 @@ static void Add(IndentedTextWriter writer, LocalVariable parentVar, XmlName prop if (HasDoubleImplicitConversion(context.Variables[valueNode].Type, itemType, context, out var conv)) cast = "(" + conv!.ReturnType.ToFQDisplayString() + ")"; - using (context.ProjectItem.EnableLineInfo ? PrePost.NewLineInfo(writer, (IXmlLineInfo)valueNode, context.ProjectItem.RelativePath) : PrePost.NoBlock()) + using (context.ProjectItem.EnableLineInfo ? PrePost.NewLineInfo(writer, (IXmlLineInfo)valueNode, context.ProjectItem) : PrePost.NoBlock()) writer.WriteLine($"{parentObj}.Add(({itemType.ToFQDisplayString()}){cast}{context.Variables[valueNode].Name});"); } diff --git a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/LineInfoTests.cs b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/LineInfoTests.cs new file mode 100644 index 000000000000..a4183c7265be --- /dev/null +++ b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/LineInfoTests.cs @@ -0,0 +1,36 @@ +using System; +using System.IO; +using System.Linq; +using NUnit.Framework; + +namespace Microsoft.Maui.Controls.SourceGen.UnitTests.InitializeComponent; + +public class LineInfoTests : SourceGenXamlInitializeComponentTestBase +{ + [Test] + public void DiagnosticShowsLocationInInputXamlFile() + { + var xaml = +""" + + + + + + + + + + +"""; + + var (result, _) = RunGenerator(xaml, string.Empty); + + var generatedCode = result.GeneratedTrees.Single(tree => tree.FilePath.EndsWith("_Test.xaml.xsg.cs")).ToString(); + var expectedFilePath = Path.Combine(Environment.CurrentDirectory, "Test.xaml"); + Assert.IsTrue(generatedCode.Contains(@$"#line 9 ""{expectedFilePath}""", StringComparison.Ordinal)); + } +} diff --git a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SourceGenXamlInitializeComponentTests.cs b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SourceGenXamlInitializeComponentTests.cs index c546788146d5..887984940dde 100644 --- a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SourceGenXamlInitializeComponentTests.cs +++ b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SourceGenXamlInitializeComponentTests.cs @@ -1,3 +1,5 @@ +using System; +using System.IO; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -16,7 +18,9 @@ protected record AdditionalXamlFile(string Path, string Content, string? Relativ { var compilation = CreateMauiCompilation(); compilation = compilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText(code)); - var result = RunGenerator(compilation, new AdditionalXamlFile("Test.xaml", xaml, TargetFramework: targetFramework, NoWarn: noWarn)); + var workingDirectory = Environment.CurrentDirectory; + var xamlFile = new AdditionalXamlFile(Path.Combine(workingDirectory, "Test.xaml"), xaml, RelativePath: "Test.xaml", TargetFramework: targetFramework, NoWarn: noWarn); + var result = RunGenerator(compilation, xamlFile); var generated = result.Results.SingleOrDefault().GeneratedSources.SingleOrDefault(gs => gs.HintName.EndsWith(".xsg.cs")).SourceText?.ToString(); return (result, generated); From a2fb5c744cc578dfd6325b1ddecefc050f3fb05a Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Tue, 9 Sep 2025 11:23:15 +0200 Subject: [PATCH 2/6] Fix snapshot tests --- .../InitializeComponent/CompiledBindings.cs | 9 +++++---- .../InitializeComponent/SetBinding.cs | 8 +++++--- .../InitializeComponent/SimplifyOnPlatform.cs | 17 +++++++++-------- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/CompiledBindings.cs b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/CompiledBindings.cs index 84cd5c7f4ff7..40db7a803a23 100644 --- a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/CompiledBindings.cs +++ b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/CompiledBindings.cs @@ -1,3 +1,5 @@ +using System; +using System.IO; using System.Linq; using NUnit.Framework; @@ -50,9 +52,8 @@ public struct Bar public string Title { get; set; } = "Title"; } """; - - var expected = -""" + var testXamlFilePath = Path.Combine(Environment.CurrentDirectory, "Test.xaml"); + var expected = $$""" //------------------------------------------------------------------------------ // @@ -81,7 +82,7 @@ private partial void InitializeComponent() #if !_MAUIXAML_SG_NAMESCOPE_DISABLE global::Microsoft.Maui.Controls.Internals.NameScope.SetNameScope(__root, iNameScope); #endif -#line 1 "Test.xaml" +#line 1 "{{testXamlFilePath}}" bindingExtension.Path = "Foo.Bar.Title"; #line default var bindingBase = CreateTypedBindingFrom_bindingExtension(bindingExtension); diff --git a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SetBinding.cs b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SetBinding.cs index 994df67db456..1e3e29253e94 100644 --- a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SetBinding.cs +++ b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SetBinding.cs @@ -1,3 +1,5 @@ +using System; +using System.IO; using System.Linq; using NUnit.Framework; @@ -36,8 +38,8 @@ public TestPage() } """; - var expected = -""" + var testXamlFilePath = Path.Combine(Environment.CurrentDirectory, "Test.xaml"); + var expected = $$""" //------------------------------------------------------------------------------ // @@ -66,7 +68,7 @@ private partial void InitializeComponent() #if !_MAUIXAML_SG_NAMESCOPE_DISABLE global::Microsoft.Maui.Controls.Internals.NameScope.SetNameScope(__root, iNameScope); #endif -#line 1 "Test.xaml" +#line 1 "{{testXamlFilePath}}" bindingExtension.Path = "Title"; #line default var bindingBase = new global::Microsoft.Maui.Controls.Binding(bindingExtension.Path, bindingExtension.Mode, bindingExtension.Converter, bindingExtension.ConverterParameter, bindingExtension.StringFormat, bindingExtension.Source) { UpdateSourceEventName = bindingExtension.UpdateSourceEventName, FallbackValue = bindingExtension.FallbackValue, TargetNullValue = bindingExtension.TargetNullValue }; diff --git a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs index f23faac3404e..ffbe73ea692f 100644 --- a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs +++ b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/SimplifyOnPlatform.cs @@ -1,4 +1,5 @@ using System; +using System.IO; using System.Linq; using NUnit.Framework; @@ -43,8 +44,8 @@ public TestPage() } """; -var expected = -""" + var testXamlFilePath = Path.Combine(Environment.CurrentDirectory, "Test.xaml"); + var expected = $$""" //------------------------------------------------------------------------------ // @@ -97,7 +98,7 @@ private partial void InitializeComponent() #if !_MAUIXAML_SG_NAMESCOPE_DISABLE global::Microsoft.Maui.Controls.Internals.INameScope iNameScope2 = new global::Microsoft.Maui.Controls.Internals.NameScope(); #endif -#line 8 "Test.xaml" +#line 8 "{{testXamlFilePath}}" var xamlServiceProvider1 = new global::Microsoft.Maui.Controls.Xaml.Internals.XamlServiceProvider(this); var iProvideValueTarget1 = new global::Microsoft.Maui.Controls.Xaml.Internals.SimpleValueTargetProvider( new object?[] {setter, style1, __root}, @@ -118,16 +119,16 @@ private partial void InitializeComponent() xamlServiceProvider1.Add(typeof(global::Microsoft.Maui.Controls.Xaml.IXamlTypeResolver), new global::Microsoft.Maui.Controls.Xaml.Internals.XamlTypeResolver(xmlNamespaceResolver1, typeof(global::Test.TestPage).Assembly)); setter.Property = ((global::Microsoft.Maui.Controls.IExtendedTypeConverter)new global::Microsoft.Maui.Controls.BindablePropertyConverter()).ConvertFromInvariantString("TextColor", xamlServiceProvider1) as global::Microsoft.Maui.Controls.BindableProperty; #line default -#line 8 "Test.xaml" +#line 8 "{{testXamlFilePath}}" setter.Value = "Pink"; #line default var setter2 = new global::Microsoft.Maui.Controls.Setter {Property = global::Microsoft.Maui.Controls.Label.TextColorProperty, Value = global::Microsoft.Maui.Graphics.Color.Parse("Pink")}; if (global::Microsoft.Maui.VisualDiagnostics.GetSourceInfo(setter2!) == null) global::Microsoft.Maui.VisualDiagnostics.RegisterSourceInfo(setter2!, new global::System.Uri(@"Test.xaml;assembly=SourceGeneratorDriver.Generated", global::System.UriKind.Relative), 8, 14); -#line 8 "Test.xaml" +#line 8 "{{testXamlFilePath}}" ((global::System.Collections.Generic.ICollection)style1.Setters).Add((global::Microsoft.Maui.Controls.Setter)setter2); #line default -#line 9 "Test.xaml" +#line 9 "{{testXamlFilePath}}" var xamlServiceProvider2 = new global::Microsoft.Maui.Controls.Xaml.Internals.XamlServiceProvider(this); var iProvideValueTarget2 = new global::Microsoft.Maui.Controls.Xaml.Internals.SimpleValueTargetProvider( new object?[] {setter1, style1, __root}, @@ -148,13 +149,13 @@ private partial void InitializeComponent() xamlServiceProvider2.Add(typeof(global::Microsoft.Maui.Controls.Xaml.IXamlTypeResolver), new global::Microsoft.Maui.Controls.Xaml.Internals.XamlTypeResolver(xmlNamespaceResolver2, typeof(global::Test.TestPage).Assembly)); setter1.Property = ((global::Microsoft.Maui.Controls.IExtendedTypeConverter)new global::Microsoft.Maui.Controls.BindablePropertyConverter()).ConvertFromInvariantString("IsVisible", xamlServiceProvider2) as global::Microsoft.Maui.Controls.BindableProperty; #line default -#line 1 "Test.xaml" +#line 1 "{{testXamlFilePath}}" setter1.Value = "True"; #line default var setter3 = new global::Microsoft.Maui.Controls.Setter {Property = global::Microsoft.Maui.Controls.VisualElement.IsVisibleProperty, Value = (bool)new global::Microsoft.Maui.Controls.VisualElement.VisibilityConverter().ConvertFromInvariantString("True")!}; if (global::Microsoft.Maui.VisualDiagnostics.GetSourceInfo(setter3!) == null) global::Microsoft.Maui.VisualDiagnostics.RegisterSourceInfo(setter3!, new global::System.Uri(@"Test.xaml;assembly=SourceGeneratorDriver.Generated", global::System.UriKind.Relative), 9, 14); -#line 9 "Test.xaml" +#line 9 "{{testXamlFilePath}}" ((global::System.Collections.Generic.ICollection)style1.Setters).Add((global::Microsoft.Maui.Controls.Setter)setter3); #line default var resourceDictionary = __root.Resources; From 1a761b24a96e4b2e79b95f04ae784731265081ca Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 10 Sep 2025 10:46:49 +0200 Subject: [PATCH 3/6] Use TargetPath --- src/Controls/src/SourceGen/PrePost.cs | 5 ++++- src/Controls/src/SourceGen/ProjectItem.cs | 3 --- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Controls/src/SourceGen/PrePost.cs b/src/Controls/src/SourceGen/PrePost.cs index 9a1ff8401d00..d28aef7b36d2 100644 --- a/src/Controls/src/SourceGen/PrePost.cs +++ b/src/Controls/src/SourceGen/PrePost.cs @@ -19,7 +19,10 @@ public static PrePost NewLineInfo(IndentedTextWriter codeWriter, IXmlLineInfo iX { // Emit #line with an absolute path since relative paths have undefined behavior (https://github.com/dotnet/roslyn/issues/71202#issuecomment-1874649780) static void LineInfo(IndentedTextWriter codeWriter, IXmlLineInfo iXmlLineInfo, ProjectItem? projectItem) - => codeWriter.WriteLineNoTabs($"#line {(iXmlLineInfo.LineNumber != -1 ? iXmlLineInfo.LineNumber : 1)} \"{projectItem?.AbsolutePath}\""); + { + var lineNumber = iXmlLineInfo.LineNumber != -1 ? iXmlLineInfo.LineNumber : 1; + codeWriter.WriteLineNoTabs($"#line {lineNumber} \"{projectItem?.TargetPath}\""); + } static void LineDefault(IndentedTextWriter codeWriter, IXmlLineInfo iXmlLineInfo) => codeWriter.WriteLineNoTabs("#line default"); diff --git a/src/Controls/src/SourceGen/ProjectItem.cs b/src/Controls/src/SourceGen/ProjectItem.cs index bdc1c4f46282..63c18b382a55 100644 --- a/src/Controls/src/SourceGen/ProjectItem.cs +++ b/src/Controls/src/SourceGen/ProjectItem.cs @@ -64,9 +64,6 @@ public string NoWarn public string? RelativePath => Options.GetValueOrNull("build_metadata.additionalfiles.RelativePath"); - public string? AbsolutePath - => _additionalText.Path; - public string? TargetFramework => Options.GetValueOrNull("build_property.targetFramework"); From 56675e46fb38f41ee269ad5c85612e443eddbe2c Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 10 Sep 2025 10:47:33 +0200 Subject: [PATCH 4/6] Fix hint name --- .../src/SourceGen/CodeBehindGenerator.cs | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/Controls/src/SourceGen/CodeBehindGenerator.cs b/src/Controls/src/SourceGen/CodeBehindGenerator.cs index 478aef991f59..e6b5aab713dc 100644 --- a/src/Controls/src/SourceGen/CodeBehindGenerator.cs +++ b/src/Controls/src/SourceGen/CodeBehindGenerator.cs @@ -96,12 +96,11 @@ public void Initialize(IncrementalGeneratorInitializationContext initContext) initContext.RegisterSourceOutput(xamlSourceProviderForCB, static (sourceProductionContext, provider) => { var (xamlItem, xmlnsCache, typeCache, compilation) = provider; - var fileName = $"{(string.IsNullOrEmpty(Path.GetDirectoryName(xamlItem!.ProjectItem!.TargetPath)) ? "" : Path.GetDirectoryName(xamlItem.ProjectItem.TargetPath) + Path.DirectorySeparatorChar)}{Path.GetFileNameWithoutExtension(xamlItem.ProjectItem.TargetPath)}.{xamlItem.ProjectItem.Kind.ToLowerInvariant()}.sg.cs".Replace(Path.DirectorySeparatorChar, '_'); try { var code = CodeBehindCodeWriter.GenerateXamlCodeBehind(xamlItem, compilation, sourceProductionContext.ReportDiagnostic, sourceProductionContext.CancellationToken, xmlnsCache, typeCache); - sourceProductionContext.AddSource(fileName, code); + sourceProductionContext.AddSource(GetHintName(xamlItem?.ProjectItem, "sg"), code); } catch (Exception e) { @@ -115,7 +114,10 @@ public void Initialize(IncrementalGeneratorInitializationContext initContext) { var (xamlItem, xmlnsCache, typeCache, compilation) = provider; - var fileName = $"{(string.IsNullOrEmpty(Path.GetDirectoryName(xamlItem!.ProjectItem!.TargetPath)) ? "" : Path.GetDirectoryName(xamlItem.ProjectItem.TargetPath) + Path.DirectorySeparatorChar)}{Path.GetFileNameWithoutExtension(xamlItem.ProjectItem.TargetPath)}.{xamlItem.ProjectItem.Kind.ToLowerInvariant()}.xsg.cs".Replace(Path.DirectorySeparatorChar, '_'); + if (xamlItem?.ProjectItem?.RelativePath is not string relativePath) + { + throw new InvalidOperationException("Xaml item or target path is null"); + } if (!ShouldGenerateSourceGenInitializeComponent(xamlItem, xmlnsCache, compilation)) return; @@ -125,7 +127,7 @@ public void Initialize(IncrementalGeneratorInitializationContext initContext) if (xamlItem != null && xamlItem.Exception != null) { var lineInfo = xamlItem.Exception is XamlParseException xpe ? xpe.XmlInfo : new XmlLineInfo(); - var location = LocationCreate(fileName, lineInfo, string.Empty); + var location = LocationCreate(relativePath, lineInfo, string.Empty); sourceProductionContext.ReportDiagnostic(Diagnostic.Create(Descriptors.XamlParserError, location, xamlItem.Exception.Message)); } return; @@ -137,14 +139,13 @@ public void Initialize(IncrementalGeneratorInitializationContext initContext) return; var code = InitializeComponentCodeWriter.GenerateInitializeComponent(xamlItem, compilation, sourceProductionContext, xmlnsCache, typeCache); - sourceProductionContext.AddSource(fileName, code); + sourceProductionContext.AddSource(GetHintName(xamlItem.ProjectItem, "xsg"), code); } catch (Exception e) { var location = xamlItem?.ProjectItem?.RelativePath is not null ? Location.Create(xamlItem.ProjectItem.RelativePath, new TextSpan(), new LinePositionSpan()) : null; sourceProductionContext.ReportDiagnostic(Diagnostic.Create(Descriptors.XamlParserError, location, e.Message)); } - }); // Register the CSS pipeline @@ -184,6 +185,19 @@ public void Initialize(IncrementalGeneratorInitializationContext initContext) }); } + private static string GetHintName(ProjectItem? projectItem, string suffix) + { + if (projectItem?.RelativePath is not string relativePath) + { + throw new InvalidOperationException("Project item or target path is null"); + } + + var prefix = Path.GetDirectoryName(relativePath).Replace(Path.DirectorySeparatorChar, '_').Replace(':', '_'); + var fileNameNoExtension = Path.GetFileNameWithoutExtension(relativePath); + var kind = projectItem.Kind.ToLowerInvariant() ?? "unknown-kind"; + return $"{prefix}{fileNameNoExtension}.{kind}.{suffix}.cs"; + } + private static string? GenerateGlobalXmlns(SourceProductionContext sourceProductionContext, AssemblyCaches xmlnsCache) { if (xmlnsCache.GlobalGeneratedXmlnsDefinitions.Count == 0) From 0989ad2a3e77402cd999a841919a872f48c78926 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 10 Sep 2025 10:48:03 +0200 Subject: [PATCH 5/6] Fix test build --- src/Controls/tests/SourceGen.UnitTests/SourceGeneratorDriver.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Controls/tests/SourceGen.UnitTests/SourceGeneratorDriver.cs b/src/Controls/tests/SourceGen.UnitTests/SourceGeneratorDriver.cs index 85259d23cb16..60fc6fdec84f 100644 --- a/src/Controls/tests/SourceGen.UnitTests/SourceGeneratorDriver.cs +++ b/src/Controls/tests/SourceGen.UnitTests/SourceGeneratorDriver.cs @@ -94,6 +94,7 @@ private static MetadataReference[] GetMauiReferences() MetadataReference.CreateFromFile(Path.Combine(dotNetAssemblyPath, "System.Private.CoreLib.dll")), MetadataReference.CreateFromFile(Path.Combine(dotNetAssemblyPath, "System.Runtime.dll")), MetadataReference.CreateFromFile(Path.Combine(dotNetAssemblyPath, "System.ObjectModel.dll")), + MetadataReference.CreateFromFile(typeof(Uri).Assembly.Location), //System.Private.Uri MetadataReference.CreateFromFile(typeof(Color).Assembly.Location), //Graphics MetadataReference.CreateFromFile(typeof(Button).Assembly.Location), //Controls MetadataReference.CreateFromFile(typeof(BindingExtension).Assembly.Location), //Xaml From ed170719b4bd93887aece70584d705d2ab6512d4 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 10 Sep 2025 10:49:15 +0200 Subject: [PATCH 6/6] Fix test --- .../SourceGen.UnitTests/InitializeComponent/LineInfoTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/LineInfoTests.cs b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/LineInfoTests.cs index a4183c7265be..58602d6b395e 100644 --- a/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/LineInfoTests.cs +++ b/src/Controls/tests/SourceGen.UnitTests/InitializeComponent/LineInfoTests.cs @@ -29,7 +29,7 @@ public void DiagnosticShowsLocationInInputXamlFile() var (result, _) = RunGenerator(xaml, string.Empty); - var generatedCode = result.GeneratedTrees.Single(tree => tree.FilePath.EndsWith("_Test.xaml.xsg.cs")).ToString(); + var generatedCode = result.GeneratedTrees.Single(tree => Path.GetFileName(tree.FilePath) == "Test.xaml.xsg.cs").ToString(); var expectedFilePath = Path.Combine(Environment.CurrentDirectory, "Test.xaml"); Assert.IsTrue(generatedCode.Contains(@$"#line 9 ""{expectedFilePath}""", StringComparison.Ordinal)); }