objectionary / objectionary/lints
`unoptimizable-recursion` incorrectly reports non-recursive objects as recursive due to self-references in `++>` tests
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 14
- Forks
- 39
- Avg merge
- 22h 54m
- Merged PRs (30d)
- 90
Description
What happens
unoptimizable-recursion counts a reference from a unit test as a recursive self-call, so an object that isn't recursive at all is reported for unoptimizable recursion. In the new test layout a ++> test is an attribute of the object it tests, and naming that object inside the test is exactly what a test does.
Run over eo-runtime/src/main/eo/clock.eo (objectionary/eo at 8655f8755e, lints at de102d68):
--- as it is
DEFECT line 35: The recursion in "clock" cannot be turned into a loop, the self-call is the receiver of a dispatch
--- with its tests cut off
no unoptimizable-recursion defect
Line 35 of clock.eo is a test, not a body:
clock.as-bytes.size.eq 8 ++> can-be-eight-bytes-long
clock is a plain formation whose φ is an as-i64 of a syscall. There is no recursion in it. Cutting the tests off the file and leaving the body untouched makes the defect go away, so the test is the whole reason for it.
The same shape shows up on other objects of the runtime whose tests mention them by name:
eo-runtime/src/main/eo/mktemp.eo:63 | mktemp.tmpfile.exists ++> can-create-a-temp-file
eo-runtime/src/main/eo/nop.eo:24 | nop 42 ++> accepts-a-number-argument
eo-runtime/src/main/eo/false.eo:19 | false.if
eo-runtime/src/main/eo/getenv.eo:26 | getenv "EO_UNSET_VARIABLE_6142" "first"
Why
The self-calls are collected over the whole subtree of the formation, tests included, while the tail-position search looks only inside φ (design/unoptimizable-recursion.xsl):
<xsl:variable name="root" select="o[@name='φ']"/>
<xsl:variable name="calls" select=".//o[eo:is-self-call(string(@base), $name)]"/>
<xsl:variable name="loops" select="($root | $root//o)[eo:is-self-tail(string(@base), $name) and eo:tail(., $root)]"/>
<xsl:if test="exists($calls) and empty($loops)">
A call written in a test therefore lands in $calls but can never land in $loops, and the pair "some calls, no loops" is the defect condition. Every neighbouring lint that must not look at tests uses eo:test-name() for it, for example misc/sparse-decoration.xsl and design/excessive-visibility.xsl; this one doesn't.
How to fix
Leave the test attributes out of the self-call search, the way the neighbours do:
<xsl:variable name="calls"
select=".//o[eo:is-self-call(string(@base), $name)][not(ancestor-or-self::o[eo:test-name(@name)])]"/>
A pack for it: a formation with a non-recursive body and a ++> test that names the formation must give defects: 0, while the same formation with a real self-call in its body must still be reported.
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
Start in design/unoptimizable-recursion.xsl and compare its self-call search with misc/sparse-decoration.xsl and design/excessive-visibility.xsl, which exclude test attributes. Add the described pack in the existing test layout, then verify a non-recursive formation with a ++> self-reference reports defects: 0 while a real self-call in the body is still reported.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, xml
- Domain
- devtools
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 88/100