Skip to content
Open
Show file tree
Hide file tree
Changes from 8 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
82 changes: 82 additions & 0 deletions src/main/java/org/eolang/lints/LtRedundantHat.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2016-2025 Objectionary.com
* SPDX-License-Identifier: MIT
*/
package org.eolang.lints;

import com.github.lombrozo.xnav.Xnav;
import com.jcabi.xml.XML;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import org.eolang.parser.OnDefault;

/**
* Lint that warns if a redundant {@code ^} is used.
* @since 0.0.59
*/
final class LtRedundantHat implements Lint<XML> {
Comment thread
Snehakb1219-christ marked this conversation as resolved.
Outdated
Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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?

@Override
public Collection<Defect> defects(final XML xmir) throws IOException {
final Collection<Defect> defects = new ArrayList<>(0);
final Xnav xml = new Xnav(xmir.inner());
final List<Xnav> objs = xml
.path("//o[@base='^']")
Copy link
Copy Markdown
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.

.collect(Collectors.toList());
for (final Xnav obj : objs) {
final String name = obj.attribute("name").text().orElse("");
final List<Xnav> matches = xml
.path(String.format("//o[@name='%s']", name))
.collect(Collectors.toList());
Comment thread
Snehakb1219-christ marked this conversation as resolved.
final List<Xnav> ancestors = obj
.path(String.format("ancestor::o[@name='%s']", name))
.collect(Collectors.toList());
Comment thread
Snehakb1219-christ marked this conversation as resolved.
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;
}
}
}
}
return defects;
}

@Override
public String motive() throws IOException {
return "The '^' notation is redundant and can be omitted for brevity.";
Comment thread
Snehakb1219-christ marked this conversation as resolved.
Outdated
}

@Override
public String name() {
return "redundant-hat";
}
}
83 changes: 83 additions & 0 deletions src/test/java/org/eolang/lints/LtRedundantHatTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* SPDX-FileCopyrightText: Copyright (c) 2016-2025 Objectionary.com
* SPDX-License-Identifier: MIT
*/
package org.eolang.lints;

import com.jcabi.xml.XML;
import com.jcabi.xml.XMLDocument;
import java.io.IOException;
import java.util.Collection;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;

/**
* Test for {@link LtRedundantHat}.
*
* @since 0.0.1
*/
final class LtRedundantHatTest {

@Test
void reportsNoDefectsForNecessaryHat() throws IOException {
final XML xml = new XMLDocument(
Comment thread
Snehakb1219-christ marked this conversation as resolved.
Outdated
"<program><object name='foo' line='10'><o name='+test' line='12'></o></object></program>"
);
final LtRedundantHat lint = new LtRedundantHat();
final Collection<Defect> defects = lint.defects(xml);
MatcherAssert.assertThat(
"Should not report a defect when '^' is necessary to resolve ambiguity",
defects,
Matchers.empty()
);
}

@Test
void containsGuidanceInMotive() throws IOException {
final LtRedundantHat lint = new LtRedundantHat();
MatcherAssert.assertThat(
"Motive must explain when to omit redundant '^'",
lint.motive(),
Matchers.containsString("notation is redundant and can be omitted for brevity")
);
}

@Test
void namesStableId() {
final LtRedundantHat lint = new LtRedundantHat();
Comment thread
Snehakb1219-christ marked this conversation as resolved.
Outdated
MatcherAssert.assertThat(
"Rule id must be 'redundant-hat'",
lint.name(),
Matchers.equalTo("redundant-hat")
);
}

@Test
void doesNotReportDefectWhenCaretIsNecessary() throws IOException {
final String xml = "<object><o name='bar' line='13'></o></object>";
final XML doc = new XMLDocument(xml);
final LtRedundantHat lint = new LtRedundantHat();
final Collection<Defect> defects = lint.defects(doc);
MatcherAssert.assertThat(
"Should not report a defect when '^' is necessary due to ambiguity",
defects,
Matchers.empty()
);
}

@Test
void reportsDefectWhenCaretIsRedundantWithMultipleMatches() throws IOException {
final String xml =
"<object><o name='foo' base='^' line='12'></o></object>";
final XML doc = new XMLDocument(xml);
final LtRedundantHat lint = new LtRedundantHat();
final Collection<Defect> defects = lint.defects(doc);
MatcherAssert.assertThat(
"Should report a defect when '^' is redundant among multiple matches",
defects,
Matchers.hasSize(1)
);
}

}