objectionary / objectionary/lints

`*-line-out-of-listing` rules incorrectly count lines in source files ending with a newline

Open Beginner friendly
#1,358 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug good-title
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, '&#10;'))"/>

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 &gt; $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 &gt; 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, '&#10;')[. != '' 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, '&#10;$', ''), '&#10;'))"/>

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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.