Skip to content

[XSG] Pre-compute x:DataType with PropagateDataTypeVisitor#34740

Draft
simonrozsival wants to merge 6 commits intonet11.0from
dev/simonrozsival/propagate-binding-context-datatype
Draft

[XSG] Pre-compute x:DataType with PropagateDataTypeVisitor#34740
simonrozsival wants to merge 6 commits intonet11.0from
dev/simonrozsival/propagate-binding-context-datatype

Conversation

@simonrozsival
Copy link
Copy Markdown
Member

@simonrozsival simonrozsival commented Mar 30, 2026

Description

Adds a PropagateDataTypeVisitor to the XAML Source Generator pipeline that pre-computes x:DataType for every ElementNode in the tree. This replaces the per-binding tree walk with an O(1) dictionary lookup.

Part of #34696. Based on #34513.

What changed

  1. BindingContextDataType model — new record with Resolved/ExplicitNull/Unresolved states, stored per-node in SourceGenContext.BindingContextDataTypes

  2. PropagateDataTypeVisitor — top-down visitor that resolves x:DataType declarations and propagates them to children, respecting DoesNotInheritDataType boundaries, DataTemplate scope crossings, and x:DataType="{x:Null}" opt-outs. Reports diagnostics for unresolvable x:DataType values.

  3. Refactored binding compilationTryGetXDataType/TryFindXDataTypeNode tree walk replaced with dictionary lookup. Removed ~130 lines of redundant code.

  4. Pipeline integration — visitor runs in the main pipeline, template inflation (SetPropertiesVisitor), and lazy resource inflation (SetPropertyHelpers). Template and lazy resource contexts are seeded with the parent's BindingContextDataType.

Not yet implemented (follow-up)

Tests

All 205 SourceGen unit tests pass (201 from #34513 + 4 new).

New tests:

  • XReferenceDirectProperty_DoesNotFallBackSilently
  • XReferenceInDataTemplate_DoesNotReportFalsePositive
  • XReferenceNonExistentProperty_FallsBackToRuntime
  • XDataTypeExplicitNull_BlocksInheritance

When a binding uses Source={x:Reference}, resolve the referenced element's
type by walking namescopes and compile the binding against it. This enables
compiled bindings for paths like Path=Text on a referenced Label.

When the path can't be fully resolved (e.g. Path=BindingContext.X where
BindingContext is 'object'), fall back silently to runtime binding without
emitting MAUIG2045 — these bindings were never compiled before, so a new
warning would be a regression.

The MAUIG2045 diagnostic is moved from TryParsePath to the caller via an
out parameter, letting the caller decide whether to emit it (only for
x:DataType-sourced bindings, not x:Reference).

Fixes #34490

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 30, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 34740

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 34740"

@simonrozsival
Copy link
Copy Markdown
Member Author

Note: This PR is part of #34696 ([Proposal] Reduce compiled binding friction).

Dependencies

This PR should be rebased on top of #34513 once it merges — there is overlapping code in KnownMarkups.cs (x:Reference source resolution, HasRelativeSourceBinding). Conflicts are localized and straightforward to resolve.

After #34513 merges, we can adopt its TryCompileBinding diagnostic suppression approach (returning out Diagnostic? instead of emitting inline) which is cleaner than our current "skip compilation for BindingContext.* paths" strategy.

StephaneDelcroix and others added 5 commits March 30, 2026 16:22
…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
Add BindingContextDataType record and PropagateDataTypeVisitor that
pre-computes x:DataType for all ElementNodes in the XAML tree. This is
a new visitor in the SourceGen pipeline that runs after
SetNamescopesAndRegisterNamesVisitor and before SetResourcesVisitor.

The visitor:
- Resolves x:DataType declarations (string names, {x:Type}, {x:Null})
- Propagates resolved types to children (top-down)
- Respects DoesNotInheritDataType boundaries
- Tracks DataTemplate boundary crossings
- Handles x:DataType={x:Null} as explicit opt-out (blocks inheritance)

Also integrates into template re-inflation in SetPropertiesVisitor,
seeding child contexts with the parent's BindingContextDataType.

No behavior change yet — the dictionary is populated but not consumed
by binding compilation. That refactor comes next.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the TryGetXDataType/TryFindXDataTypeNode tree walk in
ProvideValueForBindingExtension with an O(1) dictionary lookup into
context.BindingContextDataTypes, pre-computed by PropagateDataTypeVisitor.

Key changes:
- Remove TryGetXDataType, TryFindXDataTypeNode, DoesNotInheritDataType
  local functions from KnownMarkups (replaced by the visitor)
- Add TryGetBindingContextDataType with BindingContext={Binding} skip
  logic: looks up grandparent's type when target is BindingContext
- Add PropagateDataTypeVisitor to lazy resource inflation path in
  SetPropertyHelpers.AddLazyResourceToResourceDictionary
- Seed BindingContextDataType into lambda contexts for both template
  inflation and lazy resource paths

All 200 SourceGen unit tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
New test cases:
- XReferenceDirectProperty: x:Reference with direct property path
  (e.g., Slider.Value) does not produce false MAUIG2045
- XReferenceInDataTemplate: x:Reference inside DataTemplate does not
  resolve against the DataTemplate's x:DataType
- XReferenceNonExistentProperty: non-existent property on referenced
  element correctly reports MAUIG2045
- XDataTypeExplicitNull: x:DataType={x:Null} blocks inheritance and
  prevents compilation (no MAUIG2045)

All 204 SourceGen unit tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Match SetPropertiesVisitor behavior: copy parentDataType as-is instead
of forcing CrossedTemplateBoundary=true. The visitor handles the flag.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival force-pushed the dev/simonrozsival/propagate-binding-context-datatype branch from f27ab72 to efdf50d Compare March 30, 2026 15:08
@simonrozsival simonrozsival changed the base branch from net11.0 to 34490-xref-compile-net11 March 30, 2026 15:09
Base automatically changed from 34490-xref-compile-net11 to net11.0 April 1, 2026 08:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants