Skip to content

SourceFileManager::PropagateOutOfDateStatus: report malformed XAML against the offending file - #11695

Draft
Abhijeet Jha (iamAbhi-916) wants to merge 1 commit into
mainfrom
user/abhijeetjha/xamlcompiler-malformed-xaml-file-attribution
Draft

SourceFileManager::PropagateOutOfDateStatus: report malformed XAML against the offending file#11695
Abhijeet Jha (iamAbhi-916) wants to merge 1 commit into
mainfrom
user/abhijeetjha/xamlcompiler-malformed-xaml-file-attribution

Conversation

@iamAbhi-916

@iamAbhi-916 Abhijeet Jha (iamAbhi-916) commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Description:
Typing any character before the first '<' in MainWindow.xaml fails the build with a diagnostic that blames the SDK targets file and never names the XAML file.

Microsoft.UI.Xaml.Markup.Compiler.interop.targets(505,9): Xaml Internal Error error WMC9999: Data at the root level is invalid. Line 1, position 1.

No .xaml file appears anywhere in the output, so on a project with many pages there is nothing to go on.

Root cause:
Before compiling anything, DoExecute calls SourceFileManager::PropagateOutOfDateStatus, which reads x:Class from every out-of-date XAML file to notice a class rename since the last incremental build. That read is speculative, not the authoritative parse, and a malformed file makes it throw.

System.Xml.XmlException escapes the read, escapes the walk, and reaches the catch-all in DoExecute, which logs LogError_XamlInternalError(e, null). A null file is what makes MSBuild attribute the error to the targets file.

CompileXamlInternal::DoExecute                              CompileXamlInternal.cs:903
  +- SourceFileManager::PropagateOutOfDateStatus            SourceFileManager.cs:106
  |    +- XamlNodeStreamHelper::ReadXClassFromXamlFileStream
  |         XamlNodeStreamReader.cs:12
  |         +- XamlXmlReader -> XmlTextReaderImpl::ParseRootLevelWhitespace
  |              throws XmlException          <- speculative x:Class read
  |
  +- catch (Exception e)                                    CompileXamlInternal.cs:1090
       LogError_XamlInternalError(e, null)  -> WMC9999, file == null

The compiler already reports this correctly. LoadXamlDom is the authoritative parse and has catch (XmlException e) -> LogError_XamlXMLParsingError(e, xamlPath) at CompileXamlInternal.cs:2609, which is WMC9997 named against the offending .xaml. It is simply never reached, because the speculative read fails the task first.

A second, smaller gap: LogUnhandledException reads LineNumber/LinePosition only from System.Xaml.XamlException. System.Xml.XmlException does not derive from it, so the position of every malformed-XML failure is silently dropped.

Fix:
Add XamlNodeStreamHelper::TryReadXClassFromXamlFileStream following the TryXxx(out) convention already used across the compiler (ClrNamespaceParser::TryParseUri, XamlSchemaCodeInfo::TryFindType, XamlDomValidator::TryGetStringAttribute), so the speculative read reports failure instead of throwing and the call site has no exception handling. The existing ReadXClassFromXamlFileStream is unchanged for callers that want the throwing form.

PropagateOutOfDateStatus keeps the cached class name when the read fails and continues. The per-file loop then parses the same file through LoadXamlDom and reports it there, with the file.

Also read LineNumber/LinePosition from XmlException in LogUnhandledException.

Behavior:
The repro now reports MainWindow.xaml(1,1): WMC9997 instead of interop.targets(518,9): WMC9999.

Because the task no longer fails on the first bad file, every malformed file is reported rather than just one. Valid XAML, unknown-type errors and other WMC diagnostics are unchanged. Class-rename detection still works: a failed read only means the cached class name is kept for that build, and the file is recompiled anyway since it is out of date.

Testing:
Repro'd locally in a WinUI 3 app, msbuild.exe amd64, in-proc CompileXaml task, with the compiler built twice from src/XamlCompiler and swapped via XamlCompilerTaskPath.

Stray character before '<': main gives interop.targets(518,9) WMC9999 with no file, the fix gives MainWindow.xaml(1,1) WMC9997.

Two malformed XAML files: main emits one fileless WMC9999, the fix emits WMC9997 for both MainWindow.xaml and ReproPage.xaml.

Malformed XML below the root element, a stray end tag on line 31: main gives MainWindow.xaml with no line/column, the fix gives MainWindow.xaml(31,15). This is the XmlException position gap.

No regression: valid XAML builds clean on both. An unknown type reports MainWindow.xaml(31,14) WMC0001 identically on both. Break-repair-rebuild without a clean recovers on both. x:Class rename across incremental builds, the scenario the speculative read exists for, passes on both and produces the same generated .g.cs.

There is no existing unit-test seam for this path. Nothing under src/XamlCompiler/Tests constructs CompileXamlInternal or SourceFileManager; ValidatorTests drives the validator directly and never reaches the task. Rather than add reflection scaffolding to reach a leaf, the validation above exercises the real MSBuild task end to end. Happy to add a RegressionProjects-style case that builds a malformed project and asserts WMC9997 if reviewers prefer that.

Compilation verified for net8.0 in this repo and for net472, net8.0 and XamlCompiler.exe against the identical sources internally. The net472 leg fails to restore Microsoft.VisualStudio.ProjectSystem in a plain OSS clone; that failure reproduces unchanged on clean main and is unrelated to this change.

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

@microsoft-github-policy-service microsoft-github-policy-service Bot added the needs-triage Issue needs to be triaged by the area owners label Aug 28, 2026
@iamAbhi-916
Abhijeet Jha (iamAbhi-916) force-pushed the user/abhijeetjha/xamlcompiler-malformed-xaml-file-attribution branch from 432de87 to a1a71fd Compare August 28, 2026 16:37
…ainst the offending file

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@iamAbhi-916
Abhijeet Jha (iamAbhi-916) force-pushed the user/abhijeetjha/xamlcompiler-malformed-xaml-file-attribution branch from a1a71fd to 02d9624 Compare August 28, 2026 17:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-triage Issue needs to be triaged by the area owners

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant