objectionary / objectionary/lints
`*-line-out-of-listing` rules incorrectly count lines in source files ending with a newline
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 14
- Forks
- 39
- Avg merge
- 22h 54m
- Merged PRs (30d)
- 90
Description
The three *-line-out-of-listing rules compute the listing's length by counting the pieces tokenize returns, and a source file ending with a newline yields one piece more than it has lines. So each rule's bound is one too high and a line exactly one past the end of the file is never reported.
The same expression is in all three, on line 9 of each:
<xsl:variable name="max" select="count(tokenize(/object/listing, ' '))"/>
lines/error-line-out-of-listing.xsl, lines/meta-line-out-of-listing.xsl and lines/object-line-out-of-listing.xsl, with the check on line 12 in each:
<xsl:for-each select="/object/errors/error[number(@line) and @line > $max]">
tokenize("a\nb\n", "\n") gives ("a", "b", "") — three items for two lines. EO sources end with a newline, so the trailing empty piece is always there. Measured on a parsed XMIR from eo-runtime:
listing tail : ' a false > closed\n'
count(tokenize(listing, LF)) ($max) : 233
real line count of bool.eo : 232
An @line of 233 on that file is out of the listing and passes the @line > $max test unreported. The rules do catch anything further out, so the effect is narrow — exactly one line — but it is the most likely wrong value: a line number produced by counting from the wrong end, or by adding one to the last line, lands precisely there.
Subtracting the trailing piece fixes all three:
<xsl:variable name="max" select="count(tokenize(/object/listing, ' ')[. != '' or position() != last()])"/>
or, more plainly, by tokenizing the listing with its trailing newline removed:
<xsl:variable name="max" select="count(tokenize(replace(/object/listing, ' $', ''), ' '))"/>
The second form also behaves correctly for a listing that does not end with a newline, where the current expression is already right and must not lose a line.
Worth checking the empty case while the expression is being touched: for an empty listing, tokenize('', '\n') returns one empty piece, so $max is 1 and a defect on line 1 of a file with no content goes unreported.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Open lines/error-line-out-of-listing.xsl, lines/meta-line-out-of-listing.xsl, and lines/object-line-out-of-listing.xsl, focusing on the $max expression on line 9 and its check on line 12. Verify the behavior for listings with and without a trailing newline, including an empty listing. Done means all three rules report the first line beyond the listing without losing the valid final line.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- xml
- Domain
- devtools
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100