From 4e0568a461b4ac91b78c17ef84137f434f0e4db8 Mon Sep 17 00:00:00 2001 From: VasilevNStas Date: Sun, 9 Aug 2026 15:07:39 +0300 Subject: [PATCH] fix(#364): run only referenced lints in unlint-non-existing-defect --- .../lints/LtUnlintNonExistingDefect.java | 55 ++++++++++++----- .../lints/LtUnlintNonExistingDefectTest.java | 60 +++++++++++++++++++ 2 files changed, 99 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/eolang/lints/LtUnlintNonExistingDefect.java b/src/main/java/org/eolang/lints/LtUnlintNonExistingDefect.java index a9da877da..d0fdf0424 100644 --- a/src/main/java/org/eolang/lints/LtUnlintNonExistingDefect.java +++ b/src/main/java/org/eolang/lints/LtUnlintNonExistingDefect.java @@ -7,9 +7,11 @@ 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.Map; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.StreamSupport; import org.cactoos.list.ListOf; @@ -55,10 +57,18 @@ public String name() { @Override public Collection defects(final XML xmir) throws IOException { - return new Xnav(xmir.inner()).path("/object/metas/meta[head='unlint']/tail") + final List unlints = new Xnav(xmir.inner()) + .path("/object/metas/meta[head='unlint']/tail") .map(xnav -> xnav.text().get()) .distinct() - .filter(new DefectMissing(this.existingDefects(xmir), this.excluded)::apply).flatMap( + .collect(Collectors.toList()); + final Collection messages; + if (unlints.isEmpty()) { + messages = new ArrayList<>(0); + } else { + messages = unlints.stream().filter( + new DefectMissing(this.existing(unlints, xmir), this.excluded)::apply + ).flatMap( unlint -> new Xnav(xmir.inner()).path( String.format( "object/metas/meta[head='unlint' and tail=%s]/@line", @@ -76,6 +86,8 @@ public Collection defects(final XML xmir) throws IOException { ) ) ).collect(Collectors.toList()); + } + return messages; } @Override @@ -105,20 +117,31 @@ private static String quoted(final String value) { return result; } - private Map> existingDefects(final XML xmir) { - return StreamSupport.stream(this.lints.spliterator(), false).flatMap( - lint -> { - try { - return lint.defects(xmir).stream(); - } catch (final IOException exception) { - throw new IllegalStateException(exception); + /** + * Build a map of existing defects, checking only lints referenced by + * +unlint metas. + * @param unlints Tails of all +unlint metas in the document + * @param xmir The XMIR document + * @return Existing defects grouped by rule and line + */ + private Map> existing(final List unlints, final XML xmir) { + final Set names = unlints.stream() + .map(unlint -> unlint.split(":", -1)[0]) + .collect(Collectors.toSet()); + return StreamSupport.stream(this.lints.spliterator(), false) + .filter(lint -> names.contains(lint.name())).flatMap( + lint -> { + try { + return lint.defects(xmir).stream(); + } catch (final IOException exception) { + throw new IllegalStateException(exception); + } } - } - ).collect( - Collectors.groupingBy( - Defect::rule, - Collectors.mapping(Defect::line, Collectors.toList()) - ) - ); + ).collect( + Collectors.groupingBy( + Defect::rule, + Collectors.mapping(Defect::line, Collectors.toList()) + ) + ); } } diff --git a/src/test/java/org/eolang/lints/LtUnlintNonExistingDefectTest.java b/src/test/java/org/eolang/lints/LtUnlintNonExistingDefectTest.java index 7c1ea63c5..456835855 100644 --- a/src/test/java/org/eolang/lints/LtUnlintNonExistingDefectTest.java +++ b/src/test/java/org/eolang/lints/LtUnlintNonExistingDefectTest.java @@ -4,8 +4,10 @@ */ package org.eolang.lints; +import com.jcabi.xml.XML; import fixtures.EoProgram; import java.io.IOException; +import java.util.Collection; import java.util.stream.Collectors; import org.cactoos.list.ListOf; import org.hamcrest.MatcherAssert; @@ -187,4 +189,62 @@ void catchesUnlintWithApostrophe() throws IOException { Matchers.iterableWithSize(1) ); } + + @Test + void doesNotRunLintsWithoutUnlints() throws IOException { + MatcherAssert.assertThat( + "Lints should not be executed when there are no +unlint metas", + new LtUnlintNonExistingDefect( + new ListOf<>(new LtUnlintNonExistingDefectTest.Boom()), + new ListOf<>() + ).defects( + new EoProgram("org/eolang/lints/non-ascii-bar.eo").parse() + ), + Matchers.emptyIterable() + ); + } + + @Test + void doesNotInvokeUnreferencedLint() throws IOException { + MatcherAssert.assertThat( + "Only the referenced lint should be executed", + new LtUnlintNonExistingDefect( + new ListOf<>( + new LtAsciiOnly(), + new LtUnlintNonExistingDefectTest.Boom() + ), + new ListOf<>() + ).defects( + new EoProgram("org/eolang/lints/unlint-ascii-only-no-defect.eo").parse() + ), + Matchers.hasSize(Matchers.greaterThan(0)) + ); + } + + /** + * Fake lint that explodes when invoked. + * @since 0.0.40 + */ + private static final class Boom implements Lint { + + @Override + public String name() { + return "boom"; + } + + @Override + public Collection defects(final XML xmir) { + throw new IllegalStateException("this lint must not be executed"); + } + + @Override + public String motive() { + return ""; + } + + @Override + public Fix fix() { + return new FxEmpty(); + } + } }