objectionary / objectionary/lints

`unoptimizable-recursion` incorrectly reports non-recursive objects as recursive due to self-references in `++>` tests

Open Beginner friendly
#1,460 0 comments 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

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

  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

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.