Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
29 changes: 13 additions & 16 deletions src/main/resources/org/eolang/lints/misc/redundant-object.xsl
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,25 @@
<xsl:import href="/org/eolang/funcs/defect-context.xsl"/>
<xsl:output encoding="UTF-8" method="xml"/>
<xsl:template match="/">
<xsl:variable name="eligible" select="//o[@name and @name!='φ' and @base and @base!='∅' and not(@base='ξ' and @name='xi🌵')]"/>
<xsl:variable name="ref-groups" as="element(group)*">
<xsl:for-each-group select="//o[matches(@base, '^ξ(?:\.ρ)*\.')]" group-by="replace(@base, '^ξ(?:\.ρ)*\.([^.\s]+).*', '$1')">
<group name="{current-grouping-key()}" count="{count(current-group())}"/>
</xsl:for-each-group>
</xsl:variable>
Comment on lines +13 to +17
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Refs are counted by name globally; collisions across scopes → incorrect results

Grouping by only the name merges references from different lexical scopes (e.g., multiple siblings each having o @name="tmp"). This can suppress true defects or create false negatives/positives. Group by nearest scope + name, and use that for $refs.

Apply this diff:

-    <xsl:variable name="ref-groups" as="element(group)*">
-      <xsl:for-each-group select="//o[matches(@base, '^ξ(?:\.ρ)*\.')]" group-by="replace(@base, '^ξ(?:\.ρ)*\.([^.\s]+).*', '$1')">
-        <group name="{current-grouping-key()}" count="{count(current-group())}"/>
-      </xsl:for-each-group>
-    </xsl:variable>
+    <xsl:variable name="ref-groups" as="element(group)*">
+      <!-- Group by lexical scope (nearest ancestor o) + local name to avoid cross-scope collisions -->
+      <xsl:for-each-group
+        select="//o[matches(@base, '^ξ(?:\.ρ)*\.')]"
+        group-by="concat(generate-id(ancestor::o[1]), '::', replace(@base, '^ξ(?:\.ρ)*\.([^.\s]+).*', '$1'))">
+        <group
+          scope="{generate-id(current-group()[1]/ancestor::o[1])}"
+          name="{replace(current-group()[1]/@base, '^ξ(?:\.ρ)*\.([^.\s]+).*', '$1')}"
+          count="{count(current-group())}"/>
+      </xsl:for-each-group>
+    </xsl:variable>
@@
-        <xsl:variable name="refs" select="number(($ref-groups[@name = current()/@name]/@count, 0)[1])"/>
+        <xsl:variable name="refs"
+          select="number(($ref-groups[@name = current()/@name and @scope = generate-id(ancestor::o[1])]/@count, 0)[1])"/>

Also applies to: 21-21

🤖 Prompt for AI Agents
In src/main/resources/org/eolang/lints/misc/redundant-object.xsl around lines 13
to 17 (also apply same change at line 21), the current grouping key uses only
the object name which causes collisions across different lexical scopes; change
the group-by to include the nearest scope identifier plus the name (e.g.,
compute or capture the nearest enclosing scope node or its unique id and combine
it with replace(@base, '^ξ(?:\.ρ)*\.([^.\s]+).*', '$1') into the grouping key)
and then use that combined scope+name key for $refs so references are counted
per-scope rather than globally.

<defects>
<xsl:variable name="top" select="/object/o/generate-id()"/>
<xsl:for-each select="//o[generate-id() != $top and @name and @name != 'φ' and @base and @base != '∅' and not(@base='ξ' and @name='xi🌵')]">
<xsl:variable name="usage" select="concat('^ξ(?:\.ρ)*\.', @name, '(?:\.[\w-]+)*$')"/>
<xsl:if test="count(//o[matches(@base, $usage)])&lt;=1 and not(@name and o[1]/@base = 'Φ.org.eolang.dataized')">
<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:for-each select="$eligible[generate-id() != $top]">
<xsl:variable name="refs" select="number(($ref-groups[@name = current()/@name]/@count, 0)[1])"/>
<xsl:if test="$refs &lt;= 1 and not(@name and o[1]/@base = 'Φ.org.eolang.dataized')">
<defect line="{eo:lineno(@line)}" severity="warning">
<xsl:if test="eo:lineno(@line) = '0'">
<xsl:attribute name="context" select="eo:defect-context(.)"/>
</xsl:if>
<xsl:attribute name="severity">
<xsl:text>warning</xsl:text>
</xsl:attribute>
<xsl:text>The object </xsl:text>
<xsl:value-of select="eo:escape(@name)"/>
<xsl:text> is redundant and may be inlined</xsl:text>
</xsl:element>
</defect>
</xsl:if>
</xsl:for-each>
</defects>
Expand Down
6 changes: 1 addition & 5 deletions src/test/java/benchmarks/SourceBench.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,10 @@ public class SourceBench {
* @todo #555:35min Enable `duplicate-names-in-diff-context` benchmark.
* Currently, its slow, especially for `L` and `XL`-sized sources. Let's optimize it,
* in order to enable this benchmark.
* @todo #376:60min Enable redundant object in the single XMIR scope benchmarks.
* As for now, the lint is too slow, especially on L, XL and XXL-sized XMIRs.
* This happens mostly because of multiple XPath `//o` selects in the XSL. Once,
* the lint will be optimized, we can enable the lint in the benchmarks.
*/
@Benchmark
public final void scansXmir(final BenchmarkState state) {
new Source(state.xmir).without("redundant-object", "duplicate-names-in-diff-context")
new Source(state.xmir).without("duplicate-names-in-diff-context")
.defects();
}

Expand Down