Skip to content

Add XC0067/MAUIX2015 warning for duplicate implicit content property assignments in XAML#32654

Merged
StephaneDelcroix merged 1 commit intonet11.0from
dev/stdelc/fix3059
Mar 30, 2026
Merged

Add XC0067/MAUIX2015 warning for duplicate implicit content property assignments in XAML#32654
StephaneDelcroix merged 1 commit intonet11.0from
dev/stdelc/fix3059

Conversation

@StephaneDelcroix
Copy link
Copy Markdown
Contributor

@StephaneDelcroix StephaneDelcroix commented Nov 16, 2025

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

  • 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

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment on lines +274 to +276
<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>
Copy link

Copilot AI Nov 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
<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>

Copilot uses AI. Check for mistakes.
MAUIX2003 | XamlInflation | Error | Descriptors
MAUIX2004 | XamlInflation | Error | Descriptors
MAUIX2005 | XamlInflation | Warning | Descriptors
MAUIX2006 | XamlInflation | Warning | PropertySetMultipleTimes
Copy link

Copilot AI Nov 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
MAUIX2006 | XamlInflation | Warning | PropertySetMultipleTimes
MAUIX2006 | XamlInflation | Warning | MultipleChildrenInContentProperty

Copilot uses AI. Check for mistakes.
@@ -140,6 +174,10 @@ public void Visit(ElementNode node, INode parentNode)
return;
if (parentNode is ElementNode node2 && node2.SkipProperties.Contains(propertyName))
Copy link

Copilot AI Nov 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;
Suggested change
if (parentNode is ElementNode node2 && node2.SkipProperties.Contains(propertyName))
if (parentNode is ElementNode node2 && node2.SkipProperties.Contains(name))

Copilot uses AI. Check for mistakes.
@StephaneDelcroix StephaneDelcroix marked this pull request as draft November 16, 2025 12:04
@StephaneDelcroix StephaneDelcroix marked this pull request as ready for review November 17, 2025 12:34
StephaneDelcroix added a commit that referenced this pull request Nov 17, 2025
- 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
@StephaneDelcroix StephaneDelcroix added the area-xaml XAML, CSS, Triggers, Behaviors label Nov 17, 2025
@StephaneDelcroix StephaneDelcroix added this to the .NET 11 Planning milestone Nov 24, 2025
@StephaneDelcroix StephaneDelcroix force-pushed the dev/stdelc/fix3059 branch 3 times, most recently from 95d81ee to 1889047 Compare November 25, 2025 09:37
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 21 changed files in this pull request and generated 4 comments.

Files not reviewed (1)
  • src/Controls/src/SourceGen/xlf/MauiGResources.Designer.cs: Language not supported

var noWarn = Context.ProjectItem.NoWarn;
bool shouldSuppress = !string.IsNullOrEmpty(noWarn) &&
noWarn.Split(new[] { ',', ';', ' ' })
.Select(code => code.Trim())
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
.Select(code => code.Trim())
.Select(code => code.Trim())
.Where(code => !string.IsNullOrWhiteSpace(code))

Copilot uses AI. Check for mistakes.
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
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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.

Suggested change
// This file tests that MockCompiler (XamlC) emits the XC0067 warning
// This file tests that MockCompiler (XamlC compilation) emits the XC0067 warning

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +10
<?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>
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[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.

Copilot uses AI. Check for mistakes.

<!-- 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" />
Copy link

Copilot AI Nov 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
<MauiXaml Update="Issues\Issue3059b.xaml" NoWarn="67;2006" />

Copilot uses AI. Check for mistakes.
@StephaneDelcroix StephaneDelcroix changed the title Add warning for duplicate property assignments in XAML [X] Add warning for duplicate property assignments in XAML Nov 29, 2025
@PureWeen PureWeen modified the milestones: .NET 10.0 SR3, .NET 10.0 SR4 Jan 21, 2026
@StephaneDelcroix StephaneDelcroix changed the base branch from main to net11.0 February 11, 2026 14:14
@StephaneDelcroix StephaneDelcroix force-pushed the dev/stdelc/fix3059 branch 3 times, most recently from 47fbaa9 to 1697526 Compare March 3, 2026 16:50
@StephaneDelcroix
Copy link
Copy Markdown
Contributor Author

/azp run maui-pr-devicetests, maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 2 pipeline(s).

@StephaneDelcroix
Copy link
Copy Markdown
Contributor Author

/azp run maui-pr-devicetests, maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 2 pipeline(s).

Comment on lines +275 to +294
// 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);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

@github-project-automation github-project-automation bot moved this from Todo to Approved in MAUI SDK Ongoing Mar 10, 2026
@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Mar 23, 2026

⚠️ Merge Conflict Detected — This PR has merge conflicts with its target branch. Please rebase onto the target branch and resolve the conflicts.

…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
@StephaneDelcroix
Copy link
Copy Markdown
Contributor Author

/azp run maui-pr-devicetests, maui-pr-uitests

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 2 pipeline(s).

@StephaneDelcroix StephaneDelcroix merged commit 8bb9726 into net11.0 Mar 30, 2026
92 of 146 checks passed
@StephaneDelcroix StephaneDelcroix deleted the dev/stdelc/fix3059 branch March 30, 2026 08:42
@github-project-automation github-project-automation bot moved this from Approved to Done in MAUI SDK Ongoing Mar 30, 2026
simonrozsival pushed a commit that referenced this pull request Mar 30, 2026
…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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-xaml XAML, CSS, Triggers, Behaviors

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[Bug] Border accepts multiple children

5 participants