SourceFileManager::PropagateOutOfDateStatus: report malformed XAML against the offending file - #11695
Draft
Abhijeet Jha (iamAbhi-916) wants to merge 1 commit into
Conversation
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
Abhijeet Jha (iamAbhi-916)
force-pushed
the
user/abhijeetjha/xamlcompiler-malformed-xaml-file-attribution
branch
from
August 28, 2026 16:37
432de87 to
a1a71fd
Compare
…ainst the offending file Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Abhijeet Jha (iamAbhi-916)
force-pushed
the
user/abhijeetjha/xamlcompiler-malformed-xaml-file-attribution
branch
from
August 28, 2026 17:09
a1a71fd to
02d9624
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.
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.
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.