diff --git a/Core/Parser/XmlAttributeState.cs b/Core/Parser/XmlAttributeState.cs index 6c403d4..094428e 100644 --- a/Core/Parser/XmlAttributeState.cs +++ b/Core/Parser/XmlAttributeState.cs @@ -136,6 +136,10 @@ public XmlAttributeState ( var att = (XAttribute)context.Nodes.Pop (); att.End (context.PositionBeforeCurrentChar); + if (!att.IsNamed) { + return Parent; + } + var element = (IAttributedXObject)context.Nodes.Peek (); if (logDuplicate && context.Diagnostics is not null && element.Attributes.Get (att.Name, false) is not null) { diff --git a/Core/Parser/XmlParser.cs b/Core/Parser/XmlParser.cs index bf22845..7cc2f8a 100644 --- a/Core/Parser/XmlParser.cs +++ b/Core/Parser/XmlParser.cs @@ -141,7 +141,7 @@ public void Push (char c) if (Context.Nodes.Count != 1 || Context.Nodes.Pop () is not XDocument doc) { throw new InvalidParserStateException ("Malformed state stack when ending all nodes"); } - doc.End (Context.Position); + doc.End (Context.PositionBeforeCurrentChar); for (int i = nodes.Length - 1; i >= 0; i--) { var node = nodes[i]; diff --git a/Core/Parser/XmlParserContext.cs b/Core/Parser/XmlParserContext.cs index 2ce663f..499aff4 100644 --- a/Core/Parser/XmlParserContext.cs +++ b/Core/Parser/XmlParserContext.cs @@ -61,8 +61,10 @@ public XmlParserContext (XmlParserState? previousState, XmlParserState currentSt internal bool IsAtEndOfFile { get; set; } - internal int PositionBeforeCurrentChar => Position; - internal int PositionAfterCurrentChar => IsAtEndOfFile ? Position : Position + 1; + // during EOF, the position is after the last char, so these helpers ensure we don't end up with an invalid span + internal int PositionBeforeCurrentChar => IsAtEndOfFile? Position - 1 : Position; + internal int PositionAfterCurrentChar => IsAtEndOfFile ? Position - 1 : Position + 1; + internal TextSpan CurrentStateSpanIncludingCurrentChar => TextSpan.FromBounds(Position - CurrentStateLength, PositionAfterCurrentChar); internal TextSpan CurrentStateSpanExcludingCurrentChar => TextSpan.FromBounds(Position - CurrentStateLength, PositionBeforeCurrentChar); diff --git a/Core/Parser/XmlTagState.cs b/Core/Parser/XmlTagState.cs index 1c121b9..831e0ea 100644 --- a/Core/Parser/XmlTagState.cs +++ b/Core/Parser/XmlTagState.cs @@ -68,7 +68,11 @@ public XmlTagState (XmlAttributeState attributeState, XmlNameState nameState) // if the current node on the stack is ended or not an element, then it's the parent // and we need to create the new element - if ((element is null || (element.IsEnded && !isEndOfFile))) { + if (isEndOfFile) { + if (element is null) { + throw new InvalidParserStateException ("When entering tag state during EOF, there must be an XElement on the stack"); + } + } else if (element is null || element.IsEnded) { var parent = peekedNode; element = new XElement (context.Position - STARTOFFSET) { Parent = parent }; context.Nodes.Push (element); @@ -78,14 +82,16 @@ public XmlTagState (XmlAttributeState attributeState, XmlNameState nameState) } if (isEndOfFile || c == '<') { - element.End (context.PositionBeforeCurrentChar); + if (!element.IsEnded) { + element.End (context.PositionBeforeCurrentChar); + } if (isEndOfFile) { context.Diagnostics?.Add (XmlCoreDiagnostics.IncompleteTagEof, context.PositionBeforeCurrentChar); } else if (element.Name.IsValid) { context.Diagnostics?.Add (XmlCoreDiagnostics.MalformedNamedTag, context.PositionBeforeCurrentChar, element.Name.FullName, '<'); } else { - context.Diagnostics?.Add (XmlCoreDiagnostics.UnnamedTag, context.Position); + context.Diagnostics?.Add (XmlCoreDiagnostics.UnnamedTag, context.PositionBeforeCurrentChar); } if (isEndOfFile) {