Skip to content

Log and restore XmlNodeType in XmlNodeReader#124559

Open
lilinus wants to merge 3 commits intodotnet:mainfrom
lilinus:fix-xml-node-reader
Open

Log and restore XmlNodeType in XmlNodeReader#124559
lilinus wants to merge 3 commits intodotnet:mainfrom
lilinus:fix-xml-node-reader

Conversation

@lilinus
Copy link
Contributor

@lilinus lilinus commented Feb 18, 2026

Fixes #34443

Copilot AI review requested due to automatic review settings February 18, 2026 15:29
@dotnet-policy-service dotnet-policy-service bot added the community-contribution Indicates that the PR has been added by a community member label Feb 18, 2026
Copy link
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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@lilinus lilinus marked this pull request as ready for review February 19, 2026 08:24
Copy link
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 2 out of 2 changed files in this pull request and generated 1 comment.

Co-authored-by: Copilot <[email protected]>
Copilot AI review requested due to automatic review settings March 3, 2026 13:41
Copy link
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 2 out of 2 changed files in this pull request and generated 1 comment.

Comment on lines +129 to +134
XmlNodeReader nodeReader = NodeReaderTestHelper.CreateNodeReader(xml);
Assert.True(nodeReader.ReadToDescendant("child"));
Assert.True(nodeReader.MoveToAttribute("attr1"));
Assert.False(nodeReader.MoveToAttribute("attr2"));
nodeReader.ReadStartElement("child");
}
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

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

The new regression test verifies success by calling ReadStartElement, but it doesn’t directly assert the reader state after MoveToAttribute("attr2") returns false. To better lock in the intended fix (reader position must be unchanged), add assertions that NodeType/Name (and/or Depth) are still those of the existing attribute (e.g., still on attr1) before calling ReadStartElement.

Copilot generated this review using guidance from repository custom instructions.
@stephentoub
Copy link
Member

🤖 Copilot Code Review — PR #124559

Holistic Assessment

Motivation: The bug is real and well-documented. When XmlNodeReader is positioned on an attribute and MoveToAttribute returns false for a nonexistent attribute, the reader's _nodeType field gets corrupted to XmlNodeType.Element while _curNode still points to the original attribute — leaving the reader in an inconsistent state. This causes downstream operations like ReadStartElement to fail. Other XmlReader implementations (XmlTextReader, XmlReader.Create) handle this correctly.

Approach: The fix correctly identifies the root cause: ResetMove calls LogMove to save state, then navigates from attribute back to element (modifying _nodeType via ref), but the companion RollBackMove never restored _nodeType. The fix adds a _logNodeType field and threads it through LogMove/RollBackMove — the minimal, correct fix.

Summary: ✅ LGTM. The fix is correct, minimal, and follows the existing log/rollback pattern. All call sites for LogMove (3 callers) and RollBackMove (7 callers) are updated. The regression test directly reproduces the reported bug. Two minor suggestions below.


Detailed Findings

✅ Correctness — Fix properly restores _nodeType on rollback

Traced the bug scenario step by step:

  1. Reader on attr1 (_nodeType = Attribute)
  2. MoveToAttribute("attr2")ResetMove saves state via LogMove, then navigates to parent element, setting _nodeType = Element
  3. Attribute lookup fails → RollBackMove restores _curNode back to attr1

Before fix: _nodeType stays Element → inconsistent with _curNode (attr1). After fix: _logNodeType = Attribute is saved in LogMove and restored in RollBackMove → consistent state.

All 3 LogMove call sites and all 7 RollBackMove call sites are updated. The constructor initializes _logNodeType correctly.

✅ No new public API surface

XmlNodeReaderNavigator is internal sealed. LogMove and RollBackMove are internal-only methods. No ref assembly changes needed.

✅ Comment cleanup

The stale //, ref curDepth ) ) { comments on lines 1371 and 1392 are cleaned up — good housekeeping.

💡 Test — Consider adding explicit state assertions (suggestion, not blocking)

The test implicitly validates the fix via ReadStartElement("child") succeeding. Adding explicit assertions after the failed MoveToAttribute would make the test more precise and easier to diagnose if it ever regresses:

csharp Assert.False(nodeReader.MoveToAttribute("attr2")); Assert.Equal(XmlNodeType.Attribute, nodeReader.NodeType); Assert.Equal("attr1", nodeReader.Name);

This was flagged by all three model reviews (Claude, Gemini, GPT).

💡 Test — Consider testing MoveToAttribute(name, namespaceURI) overload (suggestion, not blocking)

The namespace-qualified MoveToAttribute(string, string?) overload has the same rollback pattern and fix. A [Theory] with both overloads or an additional test case would increase coverage with minimal effort.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-System.Xml community-contribution Indicates that the PR has been added by a community member

Projects

None yet

Development

Successfully merging this pull request may close these issues.

XmlNodeReader MoveToAttribute bug, leaves XmlReader in inconsistent state

3 participants