Add XC0067/MAUIX2015 warning for duplicate implicit content property assignments in XAML#32654
Conversation
There was a problem hiding this comment.
Pull Request Overview
This PR adds a warning diagnostic (XC0067/MAUIX2006) to detect when XAML properties are assigned multiple times, addressing issue #3059. The implementation is generalized to check all property assignments, not just ContentProperty.
Key changes:
- Added duplicate property detection in both Build.Tasks and SourceGen XAML processing
- Introduced new warning diagnostic MAUIX2006 with resource strings for error messages
- Added comprehensive unit tests covering duplicate property scenarios and proper NoWarn suppression
Reviewed Changes
Copilot reviewed 15 out of 16 changed files in this pull request and generated 14 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Controls/tests/Xaml.UnitTests/Issues/Issue3059b.xaml.cs | New test class verifying duplicate property assignment behavior (Text, Placeholder) - missing XamlInflator constructor |
| src/Controls/tests/Xaml.UnitTests/Issues/Issue3059b.xaml | Test XAML with duplicate property assignments for validation |
| src/Controls/tests/Xaml.UnitTests/Issues/Issue3059.xaml.cs | New test class verifying ContentProperty duplicate behavior - missing XamlInflator constructor |
| src/Controls/tests/Xaml.UnitTests/Issues/Issue3059.xaml | Test XAML with multiple children in Border to test ContentProperty duplication |
| src/Controls/tests/Xaml.UnitTests/Issues/Gh5095.rt.xaml.cs | Updated diagnostic count assertion (1→2) to account for new duplicate property warning |
| src/Controls/tests/Xaml.UnitTests/Controls.Xaml.UnitTests.csproj | Added NoWarn suppressions for test files and globally suppressed CS1030 |
| src/Controls/tests/SourceGen.UnitTests/InitializeComponent/MultipleChildrenWarningTests.cs | Comprehensive unit tests for duplicate property warning in SourceGen |
| src/Controls/src/SourceGen/xlf/MauiGResources.Designer.cs | Added resource string accessors for new diagnostic messages |
| src/Controls/src/SourceGen/Visitors/SetPropertiesVisitor.cs | Implemented duplicate property tracking and warning emission logic |
| src/Controls/src/SourceGen/MauiGResources.resx | Added resource strings for warning title and message |
| src/Controls/src/SourceGen/InitializeComponentCodeWriter.cs | Minor formatting adjustment to raw string literal |
| src/Controls/src/SourceGen/Descriptors.cs | Added MAUIX2006 diagnostic descriptor for duplicate properties |
| src/Controls/src/SourceGen/AnalyzerReleases.Unshipped.md | Documented new MAUIX2006 warning in analyzer releases |
| src/Controls/src/Build.Tasks/SetPropertiesVisitor.cs | Implemented duplicate property tracking for Build.Tasks (with formatting fix) |
| src/Controls/src/Build.Tasks/ErrorMessages.resx | Added error message resource for Build.Tasks |
| src/Controls/src/Build.Tasks/BuildException.cs | Added XC0067 exception code for duplicate property warnings |
Files not reviewed (1)
- src/Controls/src/SourceGen/xlf/MauiGResources.Designer.cs: Language not supported
| <data name="MultipleChildrenInContentProperty" xml:space="preserve"> | ||
| <value>Property '{0}' is being set multiple times. Only the last value will be used.</value> | ||
| <comment>0 is property name (e.g. "Border.Content" or "Label.Text")</comment> |
There was a problem hiding this comment.
The error message resource key MultipleChildrenInContentProperty is misleading since this diagnostic now detects duplicate assignments for any property, not just ContentProperty. Consider renaming to something more accurate like DuplicatePropertyAssignment or PropertySetMultipleTimes to better reflect its general-purpose nature.
| <data name="MultipleChildrenInContentProperty" xml:space="preserve"> | |
| <value>Property '{0}' is being set multiple times. Only the last value will be used.</value> | |
| <comment>0 is property name (e.g. "Border.Content" or "Label.Text")</comment> | |
| <data name="DuplicatePropertyAssignment" xml:space="preserve"> | |
| <value>Property '{0}' is being set multiple times. Only the last value will be used.</value> | |
| <comment>0 is the property name (e.g. "Border.Content" or "Label.Text")</comment> |
src/Controls/tests/Xaml.UnitTests/Controls.Xaml.UnitTests.csproj
Outdated
Show resolved
Hide resolved
| MAUIX2003 | XamlInflation | Error | Descriptors | ||
| MAUIX2004 | XamlInflation | Error | Descriptors | ||
| MAUIX2005 | XamlInflation | Warning | Descriptors | ||
| MAUIX2006 | XamlInflation | Warning | PropertySetMultipleTimes |
There was a problem hiding this comment.
The description "PropertySetMultipleTimes" doesn't match the misleading internal identifier name MultipleChildrenInContentProperty. While the description is accurate for the feature, it should ideally align with the descriptor name used in code. Consider updating the descriptor name in code to match this more accurate description.
| MAUIX2006 | XamlInflation | Warning | PropertySetMultipleTimes | |
| MAUIX2006 | XamlInflation | Warning | MultipleChildrenInContentProperty |
| @@ -140,6 +174,10 @@ public void Visit(ElementNode node, INode parentNode) | |||
| return; | |||
| if (parentNode is ElementNode node2 && node2.SkipProperties.Contains(propertyName)) | |||
There was a problem hiding this comment.
Line 175 checks node2.SkipProperties.Contains(propertyName) but propertyName is still the old value from the outer scope, not the newly assigned name variable from line 172. This should be checking name instead of propertyName.
Change to:
if (parentNode is ElementNode node2 && node2.SkipProperties.Contains(name))
return;| if (parentNode is ElementNode node2 && node2.SkipProperties.Contains(propertyName)) | |
| if (parentNode is ElementNode node2 && node2.SkipProperties.Contains(name)) |
f73e09a to
418703a
Compare
- Renamed MultipleChildrenInContentProperty to DuplicatePropertyAssignment across all files - Updated diagnostic IDs: XC0067 (Build.Tasks) and MAUIX2006 (SourceGen) - Renamed Issue3059 to Maui3059.rt (runtime XAML - no sourcegen/xamlC) - Added tests to verify MockSourceGenerator and MockCompiler report warnings correctly - Removed NoWarn suppression to allow warning verification in tests Addresses review comments on PR #32654
95d81ee to
1889047
Compare
| var noWarn = Context.ProjectItem.NoWarn; | ||
| bool shouldSuppress = !string.IsNullOrEmpty(noWarn) && | ||
| noWarn.Split(new[] { ',', ';', ' ' }) | ||
| .Select(code => code.Trim()) |
There was a problem hiding this comment.
The NoWarn parsing logic includes space (' ') as a delimiter, which could cause issues when combined with other delimiters. For example, NoWarn="67; 2006" would produce empty strings after splitting. The .Trim() call helps, but empty strings could still match warnings if not filtered. Consider adding .Where(code => !string.IsNullOrWhiteSpace(code)) before the .Any() check to filter out empty entries.
| .Select(code => code.Trim()) | |
| .Select(code => code.Trim()) | |
| .Where(code => !string.IsNullOrWhiteSpace(code)) |
| namespace Microsoft.Maui.Controls.Xaml.UnitTests; | ||
|
|
||
| // Note: This is a .rt.xaml file (runtime) which skips source generation and XamlC compilation | ||
| // This file tests that MockCompiler (XamlC) emits the XC0067 warning |
There was a problem hiding this comment.
[nitpick] This comment has a typo: "XamlC" should be "XamlC compilation" or "XamlC Build.Tasks" for clarity. The term "XamlC" alone might be confusing as it doesn't immediately convey that it refers to the Build.Tasks compilation path.
| // This file tests that MockCompiler (XamlC) emits the XC0067 warning | |
| // This file tests that MockCompiler (XamlC compilation) emits the XC0067 warning |
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <ContentPage | ||
| xmlns="http://schemas.microsoft.com/dotnet/2021/maui" | ||
| xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | ||
| x:Class="Microsoft.Maui.Controls.Xaml.UnitTests.Issue3059"> | ||
| <Border> | ||
| <Label Text="First" /> | ||
| <Label Text="Second" /> | ||
| </Border> | ||
| </ContentPage> |
There was a problem hiding this comment.
[nitpick] The test file naming is inconsistent. Both Issue3059.xaml and Maui3059.xaml exist, testing the same issue (#3059). The repository convention typically uses the prefix "Maui" for MAUI-specific issues (e.g., Maui3059) rather than "Issue". Consider standardizing on one naming pattern. Since this PR references issue #3059 which is a .NET MAUI issue, Maui3059 would be more consistent with the repository's current naming conventions.
|
|
||
| <!-- Suppress XC0067/MAUIX2006 (property set multiple times) for test files that intentionally test this scenario --> | ||
| <MauiXaml Update="Issues\Bz40906.xaml" NoWarn="67;2006" /> | ||
| <MauiXaml Update="Issues\Issue3059b.xaml" NoWarn="67;2006" /> |
There was a problem hiding this comment.
The file Issue3059b.xaml referenced in this NoWarn directive does not exist in the repository. This appears to be a typo or reference to a file that was never created. Consider removing this line or verifying if a different file name was intended.
| <MauiXaml Update="Issues\Issue3059b.xaml" NoWarn="67;2006" /> |
1424bc2 to
0a3fad0
Compare
0a3fad0 to
9ec54c0
Compare
47fbaa9 to
1697526
Compare
|
/azp run maui-pr-devicetests, maui-pr-uitests |
|
Azure Pipelines successfully started running 2 pipeline(s). |
7c58278 to
4e8a3ba
Compare
|
/azp run maui-pr-devicetests, maui-pr-uitests |
|
Azure Pipelines successfully started running 2 pipeline(s). |
| // Only check for duplicate property assignment if the property is not a collection | ||
| // Get the property type and check if it supports Add() for collection behavior | ||
| bool attached = false; | ||
| var localName = name.LocalName; | ||
| var bpFieldSymbol = parentVar.Type.GetBindableProperty(name.NamespaceURI, ref localName, out attached, context, node); | ||
| ITypeSymbol? propertyType = null; | ||
|
|
||
| // Try to get the property type | ||
| bool hasProperty = (bpFieldSymbol != null && SetPropertyHelpers.CanGetValue(parentVar, bpFieldSymbol, attached, context, out propertyType)) | ||
| || SetPropertyHelpers.CanGet(parentVar, localName, context, out propertyType, out _); | ||
|
|
||
| // Only warn if: | ||
| // 1. We can resolve the property and its type | ||
| // 2. The property type is NOT a collection (doesn't support Add) | ||
| // 3. The property type is not System.Object (unresolved generic) | ||
| bool isObject = propertyType != null && propertyType.SpecialType == Microsoft.CodeAnalysis.SpecialType.System_Object; | ||
| // Use parent namespace for duplicate tracking to match how explicit property assignments are keyed | ||
| var contentPropertyName = new XmlName(((ElementNode)parentNode).NamespaceURI, contentProperty); | ||
| if (hasProperty && propertyType != null && !isObject && !propertyType.CanAdd(context)) | ||
| CheckForDuplicateProperty((ElementNode)parentNode, contentPropertyName, node); |
There was a problem hiding this comment.
This non-trivial code is basically identical to a previous snippet of code. I suggest we move the logic to a method to ensure the behavior stays in sync.
There was a problem hiding this comment.
extracted the shared logic into a CheckImplicitContentForDuplicate helper method that's now called from both Visit(ValueNode) and Visit(ElementNode). The inline duplication is gone.Done
|
|
…assignments in XAML When a non-collection implicit content property (e.g. Border.Content, ContentPage.Content) is assigned more than once in XAML, only the last value is used and earlier assignments are silently discarded. This adds a compile-time warning to flag such cases. - Build.Tasks (XamlC): emits XC0067 for ElementNode duplicate assignments - SourceGen: emits MAUIX2015 for both ElementNode and ValueNode duplicates - Collection types (IEnumerable+Add) and System.Object are excluded from the check - Fix SkipProperties.Contains bug (was checking wrong XmlName variable) - Improve NoWarn parsing: support space separator and bare numeric suffixes - Add SourceGen, XAML, and visual-state regression tests - Suppress new warnings on existing test files that intentionally duplicate properties Fixes #3059
f6eea58 to
ccfb7f4
Compare
|
/azp run maui-pr-devicetests, maui-pr-uitests |
|
Azure Pipelines successfully started running 2 pipeline(s). |
…assignments in XAML (#32654) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! Fixes #3059 ## Description of Change Adds XC0067/MAUIX2006 warning diagnostic to detect when XAML **implicit content properties** are set multiple times (e.g., `<Border><Label/><Label/></Border>`). This implementation specifically targets the issue described in #3059: detecting multiple children in single-child content properties. ## Key Changes - **Focused implementation**: Only warns for implicit content property duplicates - **Does NOT warn for**: Explicit attribute duplicates like `<Label Text="A" Text="B" />` (XAML parser already rejects these at parse time) - **Collection-aware**: Correctly handles collection properties (e.g., `Style.Setters`, `VisualStateGroup.States`) - no warnings when adding multiple items - **Works for both implicit and explicit content property syntax**: - `<Border><Label/><Label/></Border>` → warns about `Border.Content` - `<Border><Border.Content><Label/><Label/></Border.Content></Border>` → warns about `Border.Content` ## Implementation Details - Duplicate detection only in `Visit(ElementNode)` for implicit content properties - Removed checks for explicit attribute assignments (ValueNode) - not needed - Added property type inspection to detect collection properties - Collections use `Add()` method, so multiple children are expected and don't trigger warnings - Improved NoWarn parsing to properly handle comma/semicolon-delimited codes - Implemented in both Build.Tasks and SourceGen ## Testing - Added comprehensive unit tests in SourceGen.UnitTests - Tests verify warnings for single-value content properties - Tests verify NO warnings for collection properties - Tests verify NO warnings for single children - Removed unnecessary tests for explicit attribute duplicates ## Issues Fixed Fixes #3059
Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Fixes #3059
Description of Change
Adds XC0067/MAUIX2006 warning diagnostic to detect when XAML implicit content properties are set multiple times (e.g.,
<Border><Label/><Label/></Border>).This implementation specifically targets the issue described in #3059: detecting multiple children in single-child content properties.
Key Changes
<Label Text="A" Text="B" />(XAML parser already rejects these at parse time)Style.Setters,VisualStateGroup.States) - no warnings when adding multiple items<Border><Label/><Label/></Border>→ warns aboutBorder.Content<Border><Border.Content><Label/><Label/></Border.Content></Border>→ warns aboutBorder.ContentImplementation Details
Visit(ElementNode)for implicit content propertiesAdd()method, so multiple children are expected and don't trigger warningsTesting
Issues Fixed
Fixes #3059