Skip to content
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
* SPDX-FileCopyrightText: Copyright (c) 2016-2025 Objectionary.com
* SPDX-License-Identifier: MIT
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:eo="https://www.eolang.org" id="missing-rho-reference" version="2.0">
<xsl:import href="/org/eolang/parser/_funcs.xsl"/>
<xsl:import href="/org/eolang/funcs/lineno.xsl"/>
<xsl:import href="/org/eolang/funcs/escape.xsl"/>
<xsl:import href="/org/eolang/funcs/defect-context.xsl"/>
<xsl:output encoding="UTF-8" method="xml"/>
<!--
Recursively check if owner object is not auto named object, otherwise check is attribute is present.
You can read more about auto named objects here: https://news.eolang.org/2025-02-21-auto-named-abstract-objects.html.
@todo #13:45min Restructure `report-missing` template and its call in the root template.
Currently, we are calling `report-missing` template from root template to do the job, while
we should return only the parent object to which the rho attribute refers. Also, don't forget
to rename the template to something like `rho-parent`.
-->
<xsl:template name="report-missing">
<xsl:param name="position"/>
<xsl:param name="attribute"/>
<xsl:param name="owner"/>
<xsl:choose>
<xsl:when test="$owner[not(eo:abstract(.))] or not(empty($owner/o[starts-with(@name, 'a🌵')]))">
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Logic appears inverted in the recursion condition.

The condition on line 25 seems to continue recursion when the owner is NOT abstract or HAS auto-named children. Based on the comment at line 13-14, shouldn't this skip auto-named objects instead? This logic appears inverted.

Consider reversing the logic:

-<xsl:when test="$owner[not(eo:abstract(.))] or not(empty($owner/o[starts-with(@name, 'a🌵')]))">
+<xsl:when test="$owner[eo:abstract(.)] and empty($owner/o[starts-with(@name, 'a🌵')])">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<xsl:when test="$owner[not(eo:abstract(.))] or not(empty($owner/o[starts-with(@name, 'a🌵')]))">
<xsl:when test="$owner[eo:abstract(.)] and empty($owner/o[starts-with(@name, 'a🌵')])">
🤖 Prompt for AI Agents
In src/main/resources/org/eolang/lints/errors/missing-rho-reference.xsl at line
25, the recursion condition incorrectly continues when the owner is not abstract
or has auto-named children, which is inverted. Modify the condition to skip
recursion for owners that are abstract or have auto-named children by reversing
the logic in the test expression accordingly.

<xsl:variable name="up" select="ancestor::node()[$position + 1]"/>
<xsl:if test="name($up) = 'o'">
<xsl:call-template name="report-missing">
<xsl:with-param name="position" select="$position + 1"/>
<xsl:with-param name="attribute" select="$attribute"/>
<xsl:with-param name="owner" select="$up"/>
</xsl:call-template>
Comment on lines +26 to +32
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Fragile ancestor navigation logic.

Using ancestor::node()[$position + 1] is fragile as it depends on the exact node structure. Consider using a more robust approach like ancestor::o to specifically target object elements.

Verify that the position calculation correctly navigates to parent objects in all cases by examining the XML structure:


🏁 Script executed:

#!/bin/bash
# Search for test cases that might reveal the expected XML structure
fd -e yaml -e xml | xargs rg -l "missing-rho-reference|\\$\\.\\^" | head -10 | xargs cat

Length of output: 2048


Use ancestor::o for reliable object lookup

The current lookup

<xsl:variable name="up" select="ancestor::node()[$position + 1]"/>

depends on every ancestor (including text or comment nodes) and is brittle. Instead, pick the Nth object ancestor directly:

Replace

- <xsl:variable name="up" select="ancestor::node()[$position + 1]"/>
+ <xsl:variable name="up" select="(ancestor::o)[$position + 1]"/>

This way you only count <o> elements (object scopes), so the $position+1 lookup always lands on the correct object.

🤖 Prompt for AI Agents
In src/main/resources/org/eolang/lints/errors/missing-rho-reference.xsl around
lines 26 to 32, the variable 'up' is assigned using ancestor::node() which
includes all ancestor nodes and can lead to incorrect selection. Change the
XPath to use ancestor::o to select only object elements, ensuring the
$position+1 index correctly targets the intended object ancestor. Replace
ancestor::node()[$position + 1] with ancestor::o[$position + 1] to fix this.

</xsl:if>
</xsl:when>
<xsl:otherwise>
<!--
@todo #13:90min Enable processing of rho references inside auto named objects.
For now we ignore them, but we need to check them as well, as they are abstract
objects. Don't forget to add test packs with auto named objects.
-->
<xsl:if test="not($owner/o[@name=$attribute]) and not(contains($owner/@name, 'a🌵'))">
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Inconsistent pattern matching for auto-named objects.

Line 41 uses contains() while line 25 uses starts-with() for checking the auto-named pattern. This inconsistency could lead to different behavior.

Apply consistent pattern matching:

-<xsl:if test="not($owner/o[@name=$attribute]) and not(contains($owner/@name, 'a🌵'))">
+<xsl:if test="not($owner/o[@name=$attribute]) and not(starts-with($owner/@name, 'a🌵'))">
🤖 Prompt for AI Agents
In src/main/resources/org/eolang/lints/errors/missing-rho-reference.xsl at line
41, replace the contains() function with starts-with() to match the pattern used
at line 25 for checking auto-named objects. This ensures consistent pattern
matching by using starts-with($owner/@name, 'a🌵') instead of contains(),
aligning the logic across the file.

<xsl:element name="defect">
<xsl:variable name="line" select="eo:lineno(@line)"/>
<xsl:attribute name="line">
<xsl:value-of select="$line"/>
</xsl:attribute>
<xsl:if test="$line = '0'">
<xsl:attribute name="context">
<xsl:value-of select="eo:defect-context(.)"/>
</xsl:attribute>
</xsl:if>
<xsl:attribute name="severity">
<xsl:text>error</xsl:text>
</xsl:attribute>
<xsl:text>The rho reference </xsl:text>
<xsl:value-of select="eo:escape($attribute)"/>
<xsl:text> is missing in </xsl:text>
<xsl:value-of select="eo:escape($owner/@name)"/>
<xsl:text> object</xsl:text>
</xsl:element>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="/">
<defects>
<xsl:for-each select="//o[@base and starts-with(@base, '$.^')]">
<xsl:variable name="position" select="(count(tokenize(@base, '\^')) - 1) * 2"/>
<xsl:call-template name="report-missing">
<xsl:with-param name="position" select="$position"/>
<xsl:with-param name="attribute" select="translate(translate(@base, '.^', ''), '$', '')"/>
<xsl:with-param name="owner" select="ancestor::node()[$position]"/>
Comment on lines +68 to +72
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Unclear position calculation logic.

The position calculation (count(tokenize(@base, '\^')) - 1) * 2 lacks documentation. Why multiply by 2? This makes the code hard to understand and maintain.

Add documentation or verify the calculation:


🏁 Script executed:

#!/bin/bash
# Search for examples of rho references to understand the pattern
rg -A 5 -B 5 '\$\.\^' --type yaml

Length of output: 35


Here’s a deeper inspection across all XSL error templates to see if this “* 2” pattern recurs and how other templates document it:


🏁 Script executed:

#!/bin/bash
echo "1) Listing all missing-*-reference.xsl files:"
ls src/main/resources/org/eolang/lints/errors/missing-*-reference.xsl

echo
echo "2) Looking for all count(tokenize occurrences in these XSLs:"
rg -n "count(tokenize" src/main/resources/org/eolang/lints/errors/*.xsl

echo
echo "3) Checking how ancestor::node() is used elsewhere:"
rg -n "ancestor::node" src/main/resources/org/eolang/lints/errors/*.xsl

echo
echo "4) Finding the report-missing template definition for context:"
rg -n "<xsl:template name=\"report-missing\"" src/main/resources/org/eolang/lints/errors/*.xsl

Length of output: 2011


Document the position‐calculation logic

The (count(tokenize(@base,'\^')) - 1) * 2 expression is only used in missing-rho-reference.xsl, and nothing else in the repo uses that same pattern—so it really needs an explanatory comment. Without it, future maintainers will struggle to understand why each caret maps to “two levels” in the XSLT tree.

Please update src/main/resources/org/eolang/lints/errors/missing-rho-reference.xsl around line 68 to include a clear XSL comment, for example:

<!-- 
  Each '^' in @base represents a missing ρ-reference.
  We step up two ancestor nodes per caret, so:
    (count of '^') - 1  → number of jumps
    × 2                 → levels per jump 
-->
<xsl:variable name="position"
              select="(count(tokenize(@base, '\^')) - 1) * 2"/>
  • File: src/main/resources/org/eolang/lints/errors/missing-rho-reference.xsl
  • Lines: ~68
🤖 Prompt for AI Agents
In src/main/resources/org/eolang/lints/errors/missing-rho-reference.xsl around
line 68, add an explanatory XSL comment above the position variable declaration
to clarify the logic behind the expression (count(tokenize(@base, '\^')) - 1) *
2. The comment should explain that each '^' in @base represents a missing
ρ-reference and that the calculation steps up two ancestor nodes per caret,
detailing the meaning of each part of the expression for future maintainers.

</xsl:call-template>
</xsl:for-each>
</defects>
</xsl:template>
</xsl:stylesheet>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Missing `^.` Reference

Each [rho][EO-specials] reference (`^.`) should point only to an existing
objects.
Comment on lines +3 to +4
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix grammar error in documentation.

-Each [rho][EO-specials] reference (`^.`) should point only to an existing
-objects.
+Each [rho][EO-specials] reference (`^.`) should point only to existing
+objects.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Each [rho][EO-specials] reference (`^.`) should point only to an existing
objects.
Each [rho][EO-specials] reference (`^.`) should point only to existing
objects.
🧰 Tools
🪛 LanguageTool

[grammar] ~3-~3: The plural noun “objects” cannot be used with the article “an”. Did you mean “an existing object” or “existing objects”?
Context: ...] reference (^.) should point only to an existing objects. Incorrect: ```eo # Foo. [] > foo [...

(A_NNS)

🤖 Prompt for AI Agents
In src/main/resources/org/eolang/motives/errors/missing-rho-reference.md around
lines 3 to 4, fix the grammar error by changing "objects" to "object" so the
sentence reads "Each [rho][EO-specials] reference (`^.`) should point only to an
existing object."


Incorrect:

```eo
# Foo.
[] > foo
[] > message
[] > bar
^.boom > @
```

Correct:

```eo
# Foo.
[] > foo
[] > message
[] > bar
^.message > @
```

[EO-specials]: https://news.eolang.org/2024-05-14-rho-sigma-delta-lambda.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# SPDX-FileCopyrightText: Copyright (c) 2016-2025 Objectionary.com
# SPDX-License-Identifier: MIT
---
sheets:
- /org/eolang/lints/errors/missing-rho-reference.xsl
asserts:
- /defects[count(defect[@severity='error'])=0]
input: |
# Foo.
[] > foo
"yes" > message
[] > bar
while > @
[i] >>
if. > @
i.lt 5
QQ.io.stdout
^.^.message
true
true > [i]
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# SPDX-FileCopyrightText: Copyright (c) 2016-2025 Objectionary.com
# SPDX-License-Identifier: MIT
---
sheets:
- /org/eolang/lints/errors/missing-rho-reference.xsl
asserts:
- /defects[count(defect[@severity='error'])=0]
input: |
# Foo.
[] > foo
[] > hello
[] > bar
^.hello > @
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# SPDX-FileCopyrightText: Copyright (c) 2016-2025 Objectionary.com
# SPDX-License-Identifier: MIT
---
sheets:
- /org/eolang/lints/errors/missing-rho-reference.xsl
asserts:
- /defects[count(defect[@severity='error'])=0]
input: |
# Foo.
[] > foo
[] > hello
[] > message
[] > bar
^.message > @
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# SPDX-FileCopyrightText: Copyright (c) 2016-2025 Objectionary.com
# SPDX-License-Identifier: MIT
---
# yamllint disable rule:line-length
sheets:
- /org/eolang/lints/errors/missing-rho-reference.xsl
asserts:
- /defects[count(defect[@severity='error'])=1]
- /defects/defect[@line='9']
- /defects/defect[1][normalize-space()='The rho reference "messageeee" is missing in "foo" object']
input: |
# Foo.
[] > foo
[] > bar
while > @
[i] >>
if. > @
i.lt 5
QQ.io.stdout
^.^.messageeee
true
true > [i]
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# SPDX-FileCopyrightText: Copyright (c) 2016-2025 Objectionary.com
# SPDX-License-Identifier: MIT
---
sheets:
- /org/eolang/lints/errors/missing-rho-reference.xsl
asserts:
- /defects[count(defect[@severity='error'])=1]
- /defects/defect[@line='4']
input: |
# Foo.
[] > foo
[] > bar
^.hello > @