Skip to content

Conversation

@Snehakb1219-christ
Copy link

@Snehakb1219-christ Snehakb1219-christ commented Sep 15, 2025

Fixes #690

Summary by CodeRabbit

  • New Features

    • Added a lint rule that warns about redundant ξ.ρ notation when an object can be accessed without it.
    • Emits clear WARNING messages indicating when a name resolves to the same target without ξ.ρ.
    • Rule identifier: “redundant-hat”.
  • Tests

    • Added unit tests ensuring no warnings when ξ.ρ is necessary and warnings appear when it’s redundant.
    • Verified rule naming and behavior across single and multiple match scenarios.

@coderabbitai
Copy link

coderabbitai bot commented Sep 15, 2025

Walkthrough

Introduces a new lint LtRedundantRho that scans EO XMIR for redundant ξ.ρ usages and emits WARNING defects when the caret is unnecessary. Adds a corresponding JUnit 5 test suite validating rule naming, no-defect scenarios, and defect reporting with multiple matches.

Changes

Cohort / File(s) Summary
Lint implementation
src/main/java/org/eolang/lints/LtRedundantRho.java
Adds a package-private lint that navigates XMIR (via Xnav) to detect objects using ξ.ρ, evaluates name and ancestor matches, and emits WARNING defects when usage is redundant. Loads motive text from resources and identifies as name "redundant-hat".
Tests
src/test/java/org/eolang/lints/LtRedundantRhoTest.java
Adds JUnit 5 tests covering no-defect cases when caret is necessary, rule name stability, and defect emission when redundancy is detected, using EoSyntax and XMLDocument.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Dev as Developer
  participant Lint as LtRedundantRho
  participant XMIR as XMIR XML
  participant Nav as Xnav View
  participant Rep as Defect Reporter

  Dev->>Lint: defects(XMIR)
  Lint->>XMIR: Load XML
  Lint->>Nav: Build navigation view
  Nav-->>Lint: Objects with base starting ξ.ρ
  loop For each candidate object
    Lint->>Nav: Resolve object name and ancestors
    alt Exactly one match
      Lint->>Rep: WARNING: redundant 'ξ.ρ' (single match)
    else Multiple matches and ancestor target matches
      Lint->>Rep: WARNING: redundant 'ξ.ρ' (resolves same)
    else Necessary usage
      Lint-->>Dev: No defect
    end
  end
  Rep-->>Dev: Collected defects
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

A nibble, a hop, through XMIR I go,
Sniffing out hats where they needn’t show.
“ξ.ρ? Not so!” my whiskers declare—
One name is enough, no caret to wear.
With paws on the lints and ears held high,
I prune the code—streamlined, spry. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Added linter for warning about unnecessary usage of ^ in code" is a concise, single-sentence summary that accurately describes the primary change (adding a lint to warn about unnecessary caret '^' usage) and matches the added LtRedundantRho class and tests in the changeset.
Linked Issues Check ✅ Passed The PR implements LtRedundantRho which detects redundant 'ξ.ρ' (caret) usages via Xnav queries, emits warnings when a name is unambiguous or resolves to the same object without the caret, names the rule "redundant-hat", and includes unit tests covering necessary and redundant cases; this behaviour directly addresses issue #690's objective to warn when '^' is unnecessary.
Out of Scope Changes Check ✅ Passed The changes are limited to a new package-private lint class (LtRedundantRho) and its test class (LtRedundantRhoTest) with no modifications to other modules, public APIs, or unrelated files, so no out-of-scope changes were detected.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (4)
src/test/java/org/eolang/lints/LtRedundantHatTest.java (3)

22-34: Test doesn’t exercise “necessary hat” scenario; it only has no ^ at all.

This currently asserts “no defects” because there are zero o[@base='^'] nodes, not because a hat is necessary. Either rename the test to reflect that or construct XMIR that actually contains a necessary ^ and assert no defects.

I can help craft realistic XMIR for both “necessary hat” and “redundant hat” cases once we lock the XPath used in the lint.


36-44: Rename to present tense to satisfy qulice (RulePresentTense).

Apply:

-    void motiveContainsGuidance() throws IOException {
+    void containsGuidanceInMotive() throws IOException {
@@
-    @Test
-    void motiveContainsGuidance() throws IOException {
+    @Test
+    void containsGuidanceInMotive() throws IOException {

46-54: Rename to present tense to satisfy qulice (RulePresentTense).

Apply:

-    void nameIsStableId() {
+    void returnsStableId() {
@@
-    @Test
-    void nameIsStableId() {
+    @Test
+    void returnsStableId() {
src/main/java/org/eolang/lints/LtRedundantHat.java (1)

51-53: Motive string grammar.

Replace “please omitted” with “please omit it”.

-        return "The '^' notation is used when there are no naming conflicts, please omitted for brevity.";
+        return "The '^' notation should be used only when there are naming conflicts; otherwise, please omit it for brevity.";
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b1af474 and b28765e.

📒 Files selected for processing (2)
  • src/main/java/org/eolang/lints/LtRedundantHat.java (1 hunks)
  • src/test/java/org/eolang/lints/LtRedundantHatTest.java (1 hunks)
🧰 Additional context used
🪛 GitHub Actions: qulice
src/test/java/org/eolang/lints/LtRedundantHatTest.java

[error] 1-1: Test name 'nameIsStableId' doesn't follow present-tense naming convention. Rename to present tense. (RulePresentTense)


[error] 1-1: Test name 'motiveContainsGuidance' doesn't follow present-tense naming convention. Rename to present tense. (RulePresentTense)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: mvn (windows-2022, 21)
  • GitHub Check: mvn (macos-15, 11)
  • GitHub Check: mvn (ubuntu-24.04, 11)
  • GitHub Check: mvn (macos-15, 21)
  • GitHub Check: mvn (windows-2022, 11)
  • GitHub Check: mvn (ubuntu-24.04, 21)
  • GitHub Check: deep
  • GitHub Check: ort
🔇 Additional comments (2)
src/main/java/org/eolang/lints/LtRedundantHat.java (2)

21-21: Access level check.

If this lint must be discoverable outside the package (e.g., via service loading), make the class public; otherwise package‑private is fine. Please confirm intended visibility.


27-46: Fix inverted necessity check and make hat-name extraction null-safe

File: src/main/java/org/eolang/lints/LtRedundantHat.java
Lines: 27-46

  • Wrong necessity check: current logic looks for any ancestor that contains a descendant with the same name (inverted). Only the nearest container (ancestor::o[1]) should be checked for a local sibling; a hat is necessary only when the nearest container already defines the same name.
  • Robustness: object.attribute("name").text().get() throws when @name is absent on the hat; derive the name safely from hat/@name or the first child o[@name] and skip if unknown to avoid false positives.

Apply:

-        final List<Xnav> objects = xml.path("//o[@base='^']").collect(Collectors.toList());
-        for (final Xnav object : objects) {
-            final String name = object.attribute("name").text().get();
-            final List<Xnav> parents = object.path(String.format("ancestor::o[.//o[@name='%s']]", name))
-                .collect(Collectors.toList());
-            if (parents.isEmpty()) {
+        final List<Xnav> hats = xml.path("//o[@base='^']").collect(Collectors.toList());
+        for (final Xnav hat : hats) {
+            String name = hat.attribute("name").text().orElse(null);
+            if (name == null || name.isEmpty()) {
+                final List<Xnav> named = hat.path("o[@name]").collect(Collectors.toList());
+                if (named.isEmpty()) {
+                    // Can't reliably determine the target; skip to avoid false positives
+                    continue;
+                }
+                name = named.get(0).attribute("name").text().orElse(null);
+                if (name == null || name.isEmpty()) {
+                    continue;
+                }
+            }
+            // A hat is necessary only if the nearest container already defines the same name.
+            final List<Xnav> locals = hat
+                .path(String.format("ancestor::o[1]/o[@name='%s']", name))
+                .collect(Collectors.toList());
+            if (locals.isEmpty()) {
                 defects.add(
                     new Defect.Default(
                         this.name(),
                         Severity.WARNING,
-                        new OnDefault(xmir).get(),
-                        Integer.parseInt(object.attribute("line").text().orElse("0")),
+                        new OnDefault(xmir).get(),
+                        Integer.parseInt(hat.attribute("line").text().orElse("0")),
                         String.format(
                             "Redundant '^' notation: '%s' can be accessed without it",
                             name
                         )
                     )
                 );
             }
         }

Repository search produced no matches for o[@base='^'] in the earlier run (empty output). Re-run these checks from the repo root and paste outputs:
rg -nP -C3 --glob '/*.xml' 'base="^"'
rg -nP --glob '
/*.xml' '<o[^>]*base="^"[^>]*name="' -c

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (3)
src/test/java/org/eolang/lints/LtRedundantHatTest.java (3)

25-25: Fix Checkstyle cascade indentation (line 25)

Indent the continued argument by +4 (or align with the opening parenthesis). Example:

-        final XML xml = new XMLDocument(
-                        "<program><object name='foo' line='10'><o name='+test' line='12'></o></object></program>"
-        );
+        final XML xml = new XMLDocument(
+            "<program><object name='foo' line='10'><o name='+test' line='12'></o></object></program>"
+        );

30-30: Fix Checkstyle cascade indentation (line 30)

Arguments of assertThat should be indented consistently:

-        MatcherAssert.assertThat(
-                "Should not report a defect when '^' is necessary to resolve ambiguity",
-                defects,
-                Matchers.empty()
-        );
+        MatcherAssert.assertThat(
+            "Should not report a defect when '^' is necessary to resolve ambiguity",
+            defects,
+            Matchers.empty()
+        );

7-13: Remove unused IOException import and throws clauses

Neither test performs I/O. Drop the import and the throws in methods to keep qulice happy.

-import java.io.IOException;
-    void reportsNoDefectsForNecessaryHat() throws IOException {
+    void reportsNoDefectsForNecessaryHat() {
-    void containsGuidanceInMotive() throws IOException {
+    void containsGuidanceInMotive() {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b28765e and f85cda0.

📒 Files selected for processing (1)
  • src/test/java/org/eolang/lints/LtRedundantHatTest.java (1 hunks)
🧰 Additional context used
🪛 GitHub Actions: qulice
src/test/java/org/eolang/lints/LtRedundantHatTest.java

[error] 25-25: Checkstyle: Indentation issue at LtRedundantHatTest.java:25. CascadeIndentationCheck violation.


[error] 30-30: Checkstyle: Indentation issue at LtRedundantHatTest.java:30. CascadeIndentationCheck violation.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: deep
  • GitHub Check: mvn (macos-15, 11)
  • GitHub Check: mvn (windows-2022, 11)
  • GitHub Check: mvn (ubuntu-24.04, 21)
  • GitHub Check: mvn (ubuntu-24.04, 11)
  • GitHub Check: mvn (windows-2022, 21)
  • GitHub Check: mvn (macos-15, 21)
🔇 Additional comments (1)
src/test/java/org/eolang/lints/LtRedundantHatTest.java (1)

47-54: Stable rule id assertion looks good

Asserting name() == "redundant-hat" is clear and stable.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
src/main/java/org/eolang/lints/LtRedundantHat.java (4)

49-68: Review ancestor comparison logic.

The logic compares match.equals(target) where target is ancestors.get(0). However, this comparison might not work as expected since it's comparing Xnav objects, and the equality semantics may not match what's intended (comparing XML nodes vs. object identity).

Consider clarifying the comparison logic. If you need to compare XML nodes, you might need to compare their underlying DOM nodes or use a more specific comparison method:

-                    if (match.equals(target)) {
+                    if (match.node().equals(target.node())) {

Or verify that Xnav.equals() has the correct semantics for this use case.


42-42: Potential NumberFormatException on malformed line attribute.

The code uses Integer.parseInt() without catching potential NumberFormatException if the line attribute contains non-numeric data.

Add proper error handling:

-                        Integer.parseInt(obj.attribute("line").text().orElse("0")),
+                        this.parseLineNumber(obj.attribute("line").text().orElse("0")),

And add a helper method:

private int parseLineNumber(final String line) {
    try {
        return Integer.parseInt(line);
    } catch (final NumberFormatException ex) {
        return 0;
    }
}

Also applies to: 58-58


74-76: Consider more descriptive motive text.

The motive could be more helpful by explaining when the ^ notation is actually necessary vs. when it's redundant.

-        return "The '^' notation is redundant and can be omitted for brevity.";
+        return "The '^' notation is redundant when there's no name ambiguity and can be omitted for brevity.";

23-23: Consider using more appropriate initial capacity.

The ArrayList is initialized with capacity 0, which will cause immediate reallocation when defects are added. Since you're processing multiple objects, a small initial capacity would be more efficient.

-        final Collection<Defect> defects = new ArrayList<>(0);
+        final Collection<Defect> defects = new ArrayList<>();
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b1880ed and 0f2b0eb.

📒 Files selected for processing (2)
  • src/main/java/org/eolang/lints/LtRedundantHat.java (1 hunks)
  • src/test/java/org/eolang/lints/LtRedundantHatTest.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/test/java/org/eolang/lints/LtRedundantHatTest.java
🔇 Additional comments (2)
src/main/java/org/eolang/lints/LtRedundantHat.java (2)

20-20: LGTM! Appropriate package-private visibility.

The class is correctly scoped as package-private since it's an internal implementation detail of the linting framework.


36-48: Don't treat '^' as redundant just because matches.size() == 1.

matches.size() == 1 can produce false positives — the hat (^) may be required for scoping/upward traversal even if only one object with that name exists globally; verify EO/XMIR semantics for base='^' and adjust the lint to confirm the caret doesn't change resolution (e.g., nearest-ancestor vs. global lookup) before reporting.
Location: src/main/java/org/eolang/lints/LtRedundantHat.java (around lines 36–48).

@Snehakb1219-christ
Copy link
Author

Hi @yegor256

Kindly review the PR and merge it. All workflows and checks have passed.

Thank you!

@yegor256
Copy link
Member

@h1alexbel could you please review this one?

@yegor256 yegor256 requested a review from h1alexbel September 15, 2025 17:17
Copy link
Member

@h1alexbel h1alexbel left a comment

Choose a reason for hiding this comment

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

@Snehakb1219-christ please check

final Collection<Defect> defects = new ArrayList<>(0);
final Xnav xml = new Xnav(xmir.inner());
final List<Xnav> objs = xml
.path("//o[@base='^']")
Copy link
Member

Choose a reason for hiding this comment

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

@Snehakb1219-christ in EO, we can't have @base="^", the correct representation of attribute is @base="ξ.ρ". After ξ.ρ we can call any other attribute, e.g.: ξ.ρ.foo.

* Lint that warns if a redundant {@code ^} is used.
* @since 0.0.59
*/
final class LtRedundantHat implements Lint<XML> {
Copy link
Member

Choose a reason for hiding this comment

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

@Snehakb1219-christ looks like we can implement this lint in XSL, there is no need to do it in Java, I believe

Copy link
Author

Choose a reason for hiding this comment

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

@h1alexbel It works fine in java also. I think it's necessary to do this in XSL.
WDYT?

Copy link
Member

@h1alexbel h1alexbel Oct 14, 2025

Choose a reason for hiding this comment

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

@Snehakb1219-christ I believe that we should implement all lints in XSL. Java is for cases, where XSL won't work. Make sense?

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (5)
src/main/java/org/eolang/lints/LtRedundantRho.java (2)

26-26: Avoid pre-sizing with zero.

new ArrayList<>(0) is unnecessary and misleading; just use the diamond operator.

-        final Collection<Defect> defects = new ArrayList<>(0);
+        final Collection<Defect> defects = new ArrayList<>();

45-45: Safer line parsing.

If @line is missing or malformed, Integer.parseInt throws. Defaulting to 0 hides issues. Consider a safe parse helper with a non-zero fallback (e.g., element line or 1).

-                        Integer.parseInt(obj.attribute("line").text().orElse("0")),
+                        obj.attribute("line").text().map(Integer::parseInt).orElse(1),
src/test/java/org/eolang/lints/LtRedundantRhoTest.java (3)

22-32: Use a realistic EO/XMIR sample and precise emptiness matcher.

  • The EO snippet ^foo likely should be ^.foo. Please confirm actual syntax to ensure the test exercises the rule.
  • Prefer emptyIterable() for clarity over iterableWithSize(equalTo(0)).
-                new EoSyntax("[] > foo\n+test > @\n^foo > bar").parsed()
+                new EoSyntax("[] > foo\n+test > @\n^.foo > bar").parsed()
@@
-            Matchers.allOf(
-                Matchers.iterableWithSize(Matchers.equalTo(0))
-            )
+            Matchers.emptyIterable()

43-52: Duplicate test scenario; consolidate.

doesNotReportDefectWhenCaretIsNecessary repeats the first test with the same input/assertion. Merge into one parametrized test or drop the duplicate to keep the suite lean.


1-13: Consider adding a motive test.

Add a simple check that new LtRedundantRho().motive() returns non-empty text and loads without exceptions. This guards resource regressions.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0f2b0eb and 3775eb8.

📒 Files selected for processing (2)
  • src/main/java/org/eolang/lints/LtRedundantRho.java (1 hunks)
  • src/test/java/org/eolang/lints/LtRedundantRhoTest.java (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: mvn (ubuntu-24.04, 11)
  • GitHub Check: mvn (macos-15, 11)
  • GitHub Check: mvn (windows-2022, 11)
  • GitHub Check: mvn (windows-2022, 21)
  • GitHub Check: mvn (ubuntu-24.04, 21)
  • GitHub Check: mvn (macos-15, 21)
  • GitHub Check: deep
🔇 Additional comments (1)
src/test/java/org/eolang/lints/LtRedundantRhoTest.java (1)

35-41: Rule id assertion LGTM.

Ensures stability of the lint’s external identifier.

Comment on lines +28 to +31
final List<Xnav> objs = xml
.path("//o[starts-with(@base,'ξ.ρ')]")
.collect(Collectors.toList());
for (final Xnav obj : objs) {
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Don't match bare ξ.ρ; restrict to attribute-qualified uses.

Bare ^ (base=ξ.ρ) can be required; we only want ^.<attr>. Tighten the selector or guard in code to avoid false positives.

Apply either of the following:

Option A (selector):

-            .path("//o[starts-with(@base,'ξ.ρ')]")
+            .path("//o[starts-with(@base,'ξ.ρ.')]")

Option B (guard):

-        for (final Xnav obj : objs) {
-            final String name = obj.attribute("name").text().orElse("");
+        for (final Xnav obj : objs) {
+            final String base = obj.attribute("base").text().orElse("");
+            if ("ξ.ρ".equals(base) || !base.startsWith("ξ.ρ.")) {
+                continue; // ignore bare ^ or malformed cases
+            }
📝 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
final List<Xnav> objs = xml
.path("//o[starts-with(@base,'ξ.ρ')]")
.collect(Collectors.toList());
for (final Xnav obj : objs) {
final List<Xnav> objs = xml
.path("//o[starts-with(@base,'ξ.ρ.')]")
.collect(Collectors.toList());
for (final Xnav obj : objs) {
🤖 Prompt for AI Agents
In src/main/java/org/eolang/lints/LtRedundantRho.java around lines 28 to 31, the
XPath selector currently matches any element whose base starts with "ξ.ρ", which
also hits bare ξ.ρ; restrict matches to attribute-qualified uses by either
tightening the XPath to require the dot (e.g. starts-with(@base,'ξ.ρ.') or
matching a pattern that includes a dot/attribute) or keep the XPath but add a
guard inside the loop that only processes objs whose @base contains a '.' after
"ξ.ρ" (e.g. check that the base string startsWith "ξ.ρ." or contains "ξ.ρ.").
Implement one of these changes so bare ξ.ρ is ignored.

Comment on lines +32 to +35
final String name = obj.attribute("name").text().orElse("");
final List<Xnav> matches = xml
.path(String.format("//o[@name='%s']", name))
.collect(Collectors.toList());
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Wrong identifier extracted; use attribute after ξ.ρ. instead of @name.

@name is the alias of the current <o>, not the target attribute referenced via ^. This causes incorrect matching and messages.

Apply this diff to extract the correct attribute:

-            final String name = obj.attribute("name").text().orElse("");
+            final String base = obj.attribute("base").text().orElse("");
+            final String attr = base.startsWith("ξ.ρ.") ? base.substring("ξ.ρ.".length()) : "";
+            if (attr.isEmpty()) {
+                continue;
+            }

Then replace subsequent usages of name in this method with attr (messages and queries).

🤖 Prompt for AI Agents
In src/main/java/org/eolang/lints/LtRedundantRho.java around lines 32 to 35, the
code extracts the current <o>'s @name but should extract the referenced
attribute after the ξ.ρ. expression; change the extraction to read the attribute
name from the ξ.ρ. reference (save it to a variable named attr) and then replace
all subsequent uses of the previous name variable in this method (queries, match
collection and message text) with attr so queries target the referenced
attribute and messages report the correct identifier.

Comment on lines +36 to +71
final List<Xnav> ancestors = obj
.path(String.format("ancestor::o[@name='%s']", name))
.collect(Collectors.toList());
if (matches.size() == 1) {
defects.add(
new Defect.Default(
this.name(),
Severity.WARNING,
new OnDefault(xmir).get(),
Integer.parseInt(obj.attribute("line").text().orElse("0")),
String.format(
"Redundant 'ξ.ρ' notation: '%s' can be accessed without it",
name
)
)
);
} else if (!ancestors.isEmpty() && matches.size() > 1) {
final Xnav target = ancestors.get(0);
for (final Xnav match : matches) {
if (match.equals(target)) {
defects.add(
new Defect.Default(
this.name(),
Severity.WARNING,
new OnDefault(xmir).get(),
Integer.parseInt(obj.attribute("line").text().orElse("0")),
String.format(
"Redundant 'ξ.ρ' notation: '%s' resolves to the same object without it",
name
)
)
);
break;
}
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Redundancy check ignores local shadowing; produces false positives.

Current logic counts all @name across the whole document and compares to ancestors, which will warn even when a local definition shadows the parent (a case where ^ is necessary).

Use a scope-aware check: warn only if the immediate scope doesn’t define attr locally and a parent scope does. Example refactor:

-            final List<Xnav> matches = xml
-                .path(String.format("//o[@name='%s']", name))
-                .collect(Collectors.toList());
-            final List<Xnav> ancestors = obj
-                .path(String.format("ancestor::o[@name='%s']", name))
-                .collect(Collectors.toList());
-            if (matches.size() == 1) {
-                defects.add(
-                    new Defect.Default(
-                        this.name(),
-                        Severity.WARNING,
-                        new OnDefault(xmir).get(),
-                        Integer.parseInt(obj.attribute("line").text().orElse("0")),
-                        String.format(
-                            "Redundant 'ξ.ρ' notation: '%s' can be accessed without it",
-                            name
-                        )
-                    )
-                );
-            } else if (!ancestors.isEmpty() && matches.size() > 1) {
-                final Xnav target = ancestors.get(0);
-                for (final Xnav match : matches) {
-                    if (match.equals(target)) {
-                        defects.add(
-                            new Defect.Default(
-                                this.name(),
-                                Severity.WARNING,
-                                new OnDefault(xmir).get(),
-                                Integer.parseInt(obj.attribute("line").text().orElse("0")),
-                                String.format(
-                                    "Redundant 'ξ.ρ' notation: '%s' resolves to the same object without it",
-                                    name
-                                )
-                            )
-                        );
-                        break;
-                    }
-                }
-            }
+            // Is there a local definition in the current scope that would shadow the parent?
+            final boolean hasLocal =
+                !obj.path(String.format("ancestor::o[1]/o[@name='%s']", attr))
+                    .collect(Collectors.toList()).isEmpty();
+            // Does the parent scope define the attribute?
+            final boolean parentHasAttr =
+                !obj.path(String.format("ancestor::o[2]/o[@name='%s']", attr))
+                    .collect(Collectors.toList()).isEmpty();
+            if (!hasLocal && parentHasAttr) {
+                defects.add(
+                    new Defect.Default(
+                        this.name(),
+                        Severity.WARNING,
+                        new OnDefault(xmir).get(),
+                        Integer.parseInt(obj.attribute("line").text().orElse("0")),
+                        String.format(
+                            "Redundant 'ξ.ρ' notation: '%s' can be accessed without it",
+                            attr
+                        )
+                    )
+                );
+            }

Note: If XMIR nesting differs in your project, adjust ancestor::o[1]/[2] accordingly. Add tests to cover shadowing and parent access.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/java/org/eolang/lints/LtRedundantRho.java around lines 36 to 71, the
current check counts all @name occurrences across the document and emits
warnings even when the attribute is shadowed locally; change the logic to be
scope-aware: first check the immediate scope (the current object's direct
children or attributes) for a local definition of the name and skip the warning
if present; only proceed to inspect the nearest parent scope (the closest
ancestor o node) and emit the redundant-ρ warning when the name is not defined
locally and it is defined in a parent scope (i.e., localMatches.isEmpty() &&
parentMatches.notEmpty()); implement this by selecting only same-scope attribute
nodes for the local check and only the nearest ancestor o for the parent check,
update the existing conditional branches to use these results, and add unit
tests covering local shadowing, parent access, and the no-parent case.

Comment on lines +78 to +86
return new UncheckedText(
new TextOf(
new ResourceOf(
String.format(
"org/eolang/motives/errors/%s.md", this.name()
)
)
)
).asString();
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

🧩 Analysis chain

Resource lookup: verify motive path and existence.

Path hardcodes errors/. Ensure resources/org/eolang/motives/errors/redundant-hat.md exists and is packaged. Add a unit test for motive() returning non-empty.


🏁 Script executed:

#!/bin/bash
# Verify motive file exists and is included.
fd -a 'redundant-hat.md' resources | sed -n '1p'
rg -n 'redundant-hat' -g resources/org/eolang/motives/**

# Also check the lint is registered in any aggregator/factory.
rg -nP '(redundant-hat|LtRedundantRho)' -g 'src/main/java/**'

Length of output: 435


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "PWD: $(pwd)"
echo
echo "Searching for 'redundant-hat.md' and occurrences..."
rg -n --hidden --no-ignore -S 'redundant-hat.md' || true
rg -n --hidden --no-ignore -S 'redundant-hat' || true
echo
echo "Searching for motives/errors md files..."
rg -n --hidden --no-ignore -S 'org/eolang/motives/errors' || true
echo
echo "Listing src/main/resources MD files (if present)..."
if [ -d src/main/resources ]; then
  find src/main/resources -type f -name '*.md' -print || true
else
  echo "src/main/resources not found"
fi
echo
echo "Showing LtRedundantRho.java (first 240 lines)..."
sed -n '1,240p' src/main/java/org/eolang/lints/LtRedundantRho.java || true
echo
echo "Searching repo for motive() usages and tests referencing LtRedundantRho or redundant-hat..."
rg -n --hidden --no-ignore -S 'motive\s*\(|LtRedundantRho|redundant-hat' || true

Length of output: 15067


Create missing motive resource and add a motive test

motive() loads "org/eolang/motives/errors/redundant-hat.md" but that file is not present under src/main/resources/org/eolang/motives/errors/ — add src/main/resources/org/eolang/motives/errors/redundant-hat.md (or change the path in src/main/java/org/eolang/lints/LtRedundantRho.java). Ensure the resource is packaged, and add/extend src/test/java/org/eolang/lints/LtRedundantRhoTest.java to assert new LtRedundantRho().motive() returns a non-empty string.

🤖 Prompt for AI Agents
In src/main/java/org/eolang/lints/LtRedundantRho.java around lines 78 to 86 the
motive() call expects a resource at "org/eolang/motives/errors/redundant-hat.md"
which is missing; either add the resource file
src/main/resources/org/eolang/motives/errors/redundant-hat.md (with the
appropriate message content) and ensure it is included in the build resources,
or change the String.format path in this file to point to an existing resource;
additionally add or extend
src/test/java/org/eolang/lints/LtRedundantRhoTest.java to instantiate new
LtRedundantRho() and assert that motive() returns a non-empty string so the
resource loading is validated.

Comment on lines +55 to +62
void reportsDefectWhenCaretIsRedundantWithMultipleMatches() throws IOException {
MatcherAssert.assertThat(
"Should report a defect when '^' is redundant among multiple matches",
new LtRedundantRho().defects(
new XMLDocument("<object><o name='foo' base='ξ.ρ' line='12'></o></object>")
),
Matchers.hasSize(1)
);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

🧩 Analysis chain

Test name/content mismatch; second branch not covered.

The test claims “multiple matches” but provides a single <o>; it only exercises the single‑match branch. Either rename the test or extend XML to include both a parent attribute and the ξ.ρ.<attr> usage.

Suggested test data to cover the intended case (adjust if your XMIR differs):

-                new XMLDocument("<object><o name='foo' base='ξ.ρ' line='12'></o></object>")
+                new XMLDocument(
+                  "<object>" +
+                    "<o name='foo'/>" +
+                    "<o name='inner'>" +
+                      "<o name='bar' base='ξ.ρ.foo' line='12'/>" +
+                    "</o>" +
+                  "</object>"
+                )

And add a negative test to prevent false positives when local shadowing exists:

@Test
void doesNotReportWhenLocalShadowingRequiresHat() throws IOException {
  MatcherAssert.assertThat(
    new LtRedundantRho().defects(
      new XMLDocument(
        "<object>" +
          "<o name='foo'/>" +
          "<o name='inner'>" +
            "<o name='foo'/>" +                       // local shadow
            "<o name='bar' base='ξ.ρ.foo' line='7'/>" +
          "</o>" +
        "</object>"
      )
    ),
    Matchers.emptyIterable()
  );
}

🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "Searching for LtRedundantRhoTest..."
rg -n --hidden -S "LtRedundantRhoTest" || true
echo "---- Showing file (if exists) ----"
sed -n '1,240p' src/test/java/org/eolang/lints/LtRedundantRhoTest.java || true
echo "---- Searching for related test names ----"
rg -n --hidden -S "reportsDefectWhenCaretIsRedundantWithMultipleMatches|doesNotReportWhenLocalShadowingRequiresHat|doesNotReport|reportsDefect" src/test/java/org/eolang/lints || true

Length of output: 2734


Test name/content mismatch — second branch not covered.

The test reportsDefectWhenCaretIsRedundantWithMultipleMatches (src/test/java/org/eolang/lints/LtRedundantRhoTest.java:55-62) supplies a single (base='ξ.ρ') and only exercises the single-match branch; rename the test or extend the XML to produce multiple matches.

Suggested change:

-                new XMLDocument("<object><o name='foo' base='ξ.ρ' line='12'></o></object>")
+                new XMLDocument(
+                  "<object>" +
+                    "<o name='foo'/>" +
+                    "<o name='inner'>" +
+                      "<o name='bar' base='ξ.ρ.foo' line='12'/>" +
+                    "</o>" +
+                  "</object>"
+                )

Add a negative test to avoid false positives with local shadowing:

@Test
void doesNotReportWhenLocalShadowingRequiresHat() throws IOException {
  MatcherAssert.assertThat(
    new LtRedundantRho().defects(
      new XMLDocument(
        "<object>" +
          "<o name='foo'/>" +
          "<o name='inner'>" +
            "<o name='foo'/>" +                       // local shadow
            "<o name='bar' base='ξ.ρ.foo' line='7'/>" +
          "</o>" +
        "</object>"
      )
    ),
    Matchers.emptyIterable()
  );
}
🤖 Prompt for AI Agents
In src/test/java/org/eolang/lints/LtRedundantRhoTest.java around lines 55-62,
the test named reportsDefectWhenCaretIsRedundantWithMultipleMatches only
supplies a single <o> so it covers the single-match branch; update the test to
actually produce multiple matches or add the suggested negative test to cover
local shadowing: either extend the XML input to include multiple <o> elements
with bases that will generate more than one match (so the caret-redundant
multi-match path is exercised) or add the new
doesNotReportWhenLocalShadowingRequiresHat test that constructs a nested object
with a locally shadowed name and asserts no defects; ensure assertions use
Matchers.hasSize(1) for the multi-match case or Matchers.emptyIterable() for the
negative case and keep line attributes present for accurate diagnostics.

@0crat
Copy link

0crat commented Sep 24, 2025

@Snehakb1219-christ Hello! I've reviewed your branch named "master" and it doesn't follow our naming convention policy. As per our team guidelines, this approach isn't recommended, so I'm deducting -6 points according to our policy. For future branches, please use the ticket number as the branch name - in this case, "753" would have been perfect. Thanks for understanding! 👍

@yegor256
Copy link
Member

@h1alexbel please, check this one

Copy link
Member

@h1alexbel h1alexbel left a comment

Choose a reason for hiding this comment

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

@Snehakb1219-christ please check

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Linter doesn't warn about unnecessary usage of ^ in code

4 participants