Skip to content

Return None from value_source_location() for attributes written without a value - #334

Open
krickert wants to merge 1 commit into
cloudflare:mainfrom
krickert:fix/valueless-attr-value-source-location
Open

Return None from value_source_location() for attributes written without a value#334
krickert wants to merge 1 commit into
cloudflare:mainfrom
krickert:fix/valueless-attr-value-source-location

Conversation

@krickert

Copy link
Copy Markdown

Fixes #333.

Problem

For an attribute written without a value (e.g. <div hidden>),
Attribute::value_source_location() either returned None or returned a
zero-length span pointing at the start of the parser's current buffer, and
which of the two you got depended on where the tag fell relative to a
write() boundary. In the None case name_source_location() was absent
too. Source locations describe the input, so they should not depend on how
the caller chunked it.

Cause

AttributeOutline::value was a plain Range defaulting to 0..0, and
iter_attrs inferred value presence from base + value.start being nonzero:

NonZero::new(base + a.value.start).map(|val| (base + a.name.start, val)),

With value.start == 0 that reduces to NonZero::new(base), which is None
only when the tag happens to sit at buffer offset 0.

Fix

Carry presence explicitly: AttributeOutline::value is now Option<Range>,
set only by finish_attr_value. Attribute::name_value_start becomes
Option<(usize, Option<usize>)>, dropping the NonZero workaround. Behavior
after the fix:

  • name_source_location() is always present for parsed attributes,
    including valueless ones (previously it could be None for them).
  • value_source_location() is None for valueless attributes (doc comment
    updated to say so).
  • Both are independent of the chunking of write() calls.

Downstream consumers of the outline keep their observable behavior:
Attribute::value() still returns "" for valueless attributes, selector
matching still treats a missing value as empty, and the <annotation-xml>
integration-point encoding check skips valueless attributes as before.

Testing

…ut a value

AttributeOutline::value defaulted to 0..0 for valueless attributes, and
iter_attrs inferred "has a value" from `base + value.start` being nonzero.
As a result Attribute::value_source_location() returned a zero-length span
pointing at the start of the parser's current buffer -- or None, together
with the name location, whenever the tag happened to sit at buffer offset
0 -- so the reported spans depended on where the tag fell relative to a
write() boundary.

Carry value presence explicitly as Option<Range> instead of deriving it
from the offset. name_source_location() is now always present for parsed
attributes, value_source_location() is None for valueless ones, and both
are independent of how the input was chunked.

Fixes cloudflare#333.
@krickert
krickert requested review from a team, Noah-Kennedy, jasnell and orium as code owners August 12, 2026 03:40
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.

value_source_location() returns a bogus span for valueless attributes, and whether it does depends on chunk size

2 participants