From 35066077d35ad05dfc8848e9f37482cd44165857 Mon Sep 17 00:00:00 2001 From: h1alexbel Date: Thu, 13 Feb 2025 12:52:28 +0300 Subject: [PATCH 1/3] feat(#322): unlint-non-existing-defect --- .../lints/LtUnlintNonExistingDefect.java | 115 ++++++++++++++++ src/main/java/org/eolang/lints/PkMono.java | 3 +- .../lints/LtUnlintNonExistingDefectTest.java | 126 ++++++++++++++++++ 3 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/eolang/lints/LtUnlintNonExistingDefect.java create mode 100644 src/test/java/org/eolang/lints/LtUnlintNonExistingDefectTest.java diff --git a/src/main/java/org/eolang/lints/LtUnlintNonExistingDefect.java b/src/main/java/org/eolang/lints/LtUnlintNonExistingDefect.java new file mode 100644 index 00000000..07f0a4c1 --- /dev/null +++ b/src/main/java/org/eolang/lints/LtUnlintNonExistingDefect.java @@ -0,0 +1,115 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 Objectionary.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.eolang.lints; + +import com.github.lombrozo.xnav.Xnav; +import com.jcabi.xml.XML; +import java.io.IOException; +import java.util.Collection; +import java.util.LinkedList; +import java.util.Set; +import java.util.stream.Collectors; +import org.cactoos.list.ListOf; + +/** + * Lints. + * @since 0.0.0 + */ +final class LtUnlintNonExistingDefect implements Lint { + + /** + * Lints. + */ + private final Iterable> lints; + + /** + * Ctor. + * + * @param lnts Lints + */ + LtUnlintNonExistingDefect(final Iterable> lnts) { + this.lints = lnts; + } + + @Override + public String name() { + return "unlint-non-existing-defect"; + } + + @Override + public Collection defects(final XML xmir) throws IOException { + final Collection defects = new LinkedList<>(); + final Collection present = new ListOf<>(); + this.lints.forEach( + lint -> { + try { + present.addAll( + lint.defects(xmir).stream() + .map(Defect::rule) + .collect(Collectors.toList()) + ); + } catch (final IOException exception) { + throw new IllegalStateException(exception); + } + } + ); + final Xnav xml = new Xnav(xmir.inner()); + final Set unlints = xml.path("/program/metas/meta[head='unlint']/tail") + .map(xnav -> xnav.text().get()) + .collect(Collectors.toSet()); + unlints.stream() + .filter(unlint -> !present.contains(unlint)) + .forEach( + unlint -> + xml.path( + String.format( + "program/metas/meta[head='unlint' and tail='%s']/@line", + unlint + ) + ).map(xnav -> xnav.text().get()) + .collect(Collectors.toList()) + .forEach( + line -> + defects.add( + new Defect.Default( + this.name(), + Severity.WARNING, + xml.element("program").attribute("name").text().orElse("unknown"), + Integer.parseInt(line), + String.format( + "Unlinting rule '%s' doesn't make sense, since there are no defects with it", + unlint + ) + ) + ) + ) + ); + return defects; + } + + @Override + public String motive() throws IOException { + throw new UnsupportedOperationException("#motive()"); + } +} diff --git a/src/main/java/org/eolang/lints/PkMono.java b/src/main/java/org/eolang/lints/PkMono.java index d15408d8..e035b8fe 100644 --- a/src/main/java/org/eolang/lints/PkMono.java +++ b/src/main/java/org/eolang/lints/PkMono.java @@ -67,8 +67,9 @@ final class PkMono extends IterableEnvelope> { */ PkMono() { super( - new Joined>( + new Joined<>( PkMono.LINTS, + List.of(new LtUnlintNonExistingDefect(PkMono.LINTS)), List.of( new LtIncorrectUnlint( new Mapped<>( diff --git a/src/test/java/org/eolang/lints/LtUnlintNonExistingDefectTest.java b/src/test/java/org/eolang/lints/LtUnlintNonExistingDefectTest.java new file mode 100644 index 00000000..527c219b --- /dev/null +++ b/src/test/java/org/eolang/lints/LtUnlintNonExistingDefectTest.java @@ -0,0 +1,126 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2016-2025 Objectionary.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.eolang.lints; + +import java.io.IOException; +import java.util.stream.Collectors; +import org.cactoos.list.ListOf; +import org.eolang.parser.EoSyntax; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; + +/** + * Tests for {@link LtUnlintNonExistingDefect}. + * + * @since 0.0.0 + */ +final class LtUnlintNonExistingDefectTest { + + @Test + void reportsDefects() throws IOException { + MatcherAssert.assertThat( + "Defects are empty, but they should not", + new LtUnlintNonExistingDefect( + new ListOf<>(new LtAsciiOnly()) + ).defects( + new EoSyntax( + String.join( + "\n", + "+unlint ascii-only", + "# first", + "# second", + "[] > bar" + ) + ).parsed() + ), + Matchers.hasSize(Matchers.greaterThan(0)) + ); + } + + @Test + void reportsDefectsForEachUnlintWithDifferentLines() throws IOException { + MatcherAssert.assertThat( + "Defects should be reported for each line with unlint, but it's not", + new LtUnlintNonExistingDefect( + new ListOf<>(new LtAsciiOnly()) + ).defects( + new EoSyntax( + String.join( + "\n", + "+unlint ascii-only", + "+unlint ascii-only", + "# first", + "# second", + "[] > bar" + ) + ).parsed() + ).stream() + .map(Defect::line) + .collect(Collectors.toList()), + Matchers.equalTo( + new ListOf<>(1, 2) + ) + ); + } + + @Test + void allowsUnlintingOfExistingDefects() throws IOException { + MatcherAssert.assertThat( + "Defects are not empty, but they should", + new LtUnlintNonExistingDefect( + new ListOf<>(new LtAsciiOnly()) + ).defects( + new EoSyntax( + String.join( + "\n", + "+unlint ascii-only", + "# 程式分析是我的熱愛", + "[] > bar" + ) + ).parsed() + ), + Matchers.emptyIterable() + ); + } + + @Test + void allowsNoUnlints() throws IOException { + MatcherAssert.assertThat( + "Defects are not empty, but they should", + new LtUnlintNonExistingDefect( + new ListOf<>(new LtAsciiOnly()) + ).defects( + new EoSyntax( + String.join( + "\n", + "# тук-тук", + "[] > bar" + ) + ).parsed() + ), + Matchers.emptyIterable() + ); + } +} From b57671bae3b8fbeb4f199c9e52611d370e50086d Mon Sep 17 00:00:00 2001 From: h1alexbel Date: Thu, 13 Feb 2025 14:56:48 +0300 Subject: [PATCH 2/3] feat(#322): clean for qulice --- .../lints/LtUnlintNonExistingDefect.java | 58 ++++++++++++------- src/main/java/org/eolang/lints/PkMono.java | 27 +++++---- .../misc/unlint-non-existing-defect.md | 23 ++++++++ .../java/org/eolang/lints/ProgramTest.java | 1 + .../resources/org/eolang/lints/canonical.eo | 1 - 5 files changed, 77 insertions(+), 33 deletions(-) create mode 100644 src/main/resources/org/eolang/motives/misc/unlint-non-existing-defect.md diff --git a/src/main/java/org/eolang/lints/LtUnlintNonExistingDefect.java b/src/main/java/org/eolang/lints/LtUnlintNonExistingDefect.java index 07f0a4c1..2420de5d 100644 --- a/src/main/java/org/eolang/lints/LtUnlintNonExistingDefect.java +++ b/src/main/java/org/eolang/lints/LtUnlintNonExistingDefect.java @@ -30,7 +30,10 @@ import java.util.LinkedList; import java.util.Set; import java.util.stream.Collectors; +import org.cactoos.io.ResourceOf; import org.cactoos.list.ListOf; +import org.cactoos.text.IoCheckedText; +import org.cactoos.text.TextOf; /** * Lints. @@ -60,20 +63,7 @@ public String name() { @Override public Collection defects(final XML xmir) throws IOException { final Collection defects = new LinkedList<>(); - final Collection present = new ListOf<>(); - this.lints.forEach( - lint -> { - try { - present.addAll( - lint.defects(xmir).stream() - .map(Defect::rule) - .collect(Collectors.toList()) - ); - } catch (final IOException exception) { - throw new IllegalStateException(exception); - } - } - ); + final Collection present = this.existingDefects(xmir); final Xnav xml = new Xnav(xmir.inner()); final Set unlints = xml.path("/program/metas/meta[head='unlint']/tail") .map(xnav -> xnav.text().get()) @@ -83,10 +73,9 @@ public Collection defects(final XML xmir) throws IOException { .forEach( unlint -> xml.path( - String.format( - "program/metas/meta[head='unlint' and tail='%s']/@line", - unlint - ) + String.format( + "program/metas/meta[head='unlint' and tail='%s']/@line", unlint + ) ).map(xnav -> xnav.text().get()) .collect(Collectors.toList()) .forEach( @@ -95,7 +84,10 @@ public Collection defects(final XML xmir) throws IOException { new Defect.Default( this.name(), Severity.WARNING, - xml.element("program").attribute("name").text().orElse("unknown"), + xml.element("program") + .attribute("name") + .text() + .orElse("unknown"), Integer.parseInt(line), String.format( "Unlinting rule '%s' doesn't make sense, since there are no defects with it", @@ -110,6 +102,32 @@ public Collection defects(final XML xmir) throws IOException { @Override public String motive() throws IOException { - throw new UnsupportedOperationException("#motive()"); + return new IoCheckedText( + new TextOf( + new ResourceOf( + String.format( + "org/eolang/motives/misc/%s.md", this.name() + ) + ) + ) + ).asString(); + } + + private Collection existingDefects(final XML xmir) { + final Collection existing = new ListOf<>(); + this.lints.forEach( + lint -> { + try { + existing.addAll( + lint.defects(xmir).stream() + .map(Defect::rule) + .collect(Collectors.toList()) + ); + } catch (final IOException exception) { + throw new IllegalStateException(exception); + } + } + ); + return existing; } } diff --git a/src/main/java/org/eolang/lints/PkMono.java b/src/main/java/org/eolang/lints/PkMono.java index e035b8fe..b502715d 100644 --- a/src/main/java/org/eolang/lints/PkMono.java +++ b/src/main/java/org/eolang/lints/PkMono.java @@ -51,13 +51,10 @@ final class PkMono extends IterableEnvelope> { * All XML-based lints. */ private static final Iterable> LINTS = new Shuffled<>( - new Mapped>( - LtUnlint::new, - new Joined>( - new PkByXsl(), - List.of( - new LtAsciiOnly() - ) + new Joined>( + new PkByXsl(), + List.of( + new LtAsciiOnly() ) ) ); @@ -67,16 +64,22 @@ final class PkMono extends IterableEnvelope> { */ PkMono() { super( - new Joined<>( - PkMono.LINTS, - List.of(new LtUnlintNonExistingDefect(PkMono.LINTS)), + new Joined>( + new Mapped>( + LtUnlint::new, + new Joined>( + PkMono.LINTS, + List.of(new LtUnlintNonExistingDefect(PkMono.LINTS)) + ) + ), List.of( new LtIncorrectUnlint( new Mapped<>( Lint::name, - new Joined>( + new Joined<>( new PkWpa(), - PkMono.LINTS + PkMono.LINTS, + List.of(new LtUnlintNonExistingDefect(PkMono.LINTS)) ) ) ) diff --git a/src/main/resources/org/eolang/motives/misc/unlint-non-existing-defect.md b/src/main/resources/org/eolang/motives/misc/unlint-non-existing-defect.md new file mode 100644 index 00000000..c857aedb --- /dev/null +++ b/src/main/resources/org/eolang/motives/misc/unlint-non-existing-defect.md @@ -0,0 +1,23 @@ +# `+unlint` Of Non-Existing Defect + +Special `+unlint` meta should be used only to suppress an existing defects. + +Incorrect (since there is no duplicate metas): + +```eo ++unlint duplicate-metas + +[] > foo + 42 > @ +``` + +Correct: + +```eo ++unlint duplicate-metas ++architect jeff ++architect foo + +[] > foo + 42 > @ +``` diff --git a/src/test/java/org/eolang/lints/ProgramTest.java b/src/test/java/org/eolang/lints/ProgramTest.java index 9f9f789b..307f2868 100644 --- a/src/test/java/org/eolang/lints/ProgramTest.java +++ b/src/test/java/org/eolang/lints/ProgramTest.java @@ -105,6 +105,7 @@ void suppressesManyLints() throws IOException { new InputOf( String.join( "\n", + "+unlint unlint-non-existing-defect", "+unlint object-does-not-match-filename", "+unlint empty-object", "+unlint mandatory-home", diff --git a/src/test/resources/org/eolang/lints/canonical.eo b/src/test/resources/org/eolang/lints/canonical.eo index f6a99505..ed032edc 100644 --- a/src/test/resources/org/eolang/lints/canonical.eo +++ b/src/test/resources/org/eolang/lints/canonical.eo @@ -23,7 +23,6 @@ +architect yegor256@gmail.com +home https://www.eolang.org +package canonical -+unlint empty-object +unlint object-does-not-match-filename +version 0.0.0 From 8a7e6530069e1edf69108022e2f5b8d0015ba523 Mon Sep 17 00:00:00 2001 From: h1alexbel Date: Tue, 18 Feb 2025 18:06:20 +0300 Subject: [PATCH 3/3] feat(#322): patch up --- .../java/org/eolang/lints/LtUnlintNonExistingDefect.java | 6 ++++-- .../org/eolang/lints/LtUnlintNonExistingDefectTest.java | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/eolang/lints/LtUnlintNonExistingDefect.java b/src/main/java/org/eolang/lints/LtUnlintNonExistingDefect.java index 2420de5d..dbe4be15 100644 --- a/src/main/java/org/eolang/lints/LtUnlintNonExistingDefect.java +++ b/src/main/java/org/eolang/lints/LtUnlintNonExistingDefect.java @@ -37,7 +37,8 @@ /** * Lints. - * @since 0.0.0 + * + * @since 0.0.40 */ final class LtUnlintNonExistingDefect implements Lint { @@ -76,7 +77,8 @@ public Collection defects(final XML xmir) throws IOException { String.format( "program/metas/meta[head='unlint' and tail='%s']/@line", unlint ) - ).map(xnav -> xnav.text().get()) + ) + .map(xnav -> xnav.text().get()) .collect(Collectors.toList()) .forEach( line -> diff --git a/src/test/java/org/eolang/lints/LtUnlintNonExistingDefectTest.java b/src/test/java/org/eolang/lints/LtUnlintNonExistingDefectTest.java index 527c219b..96b04272 100644 --- a/src/test/java/org/eolang/lints/LtUnlintNonExistingDefectTest.java +++ b/src/test/java/org/eolang/lints/LtUnlintNonExistingDefectTest.java @@ -34,7 +34,7 @@ /** * Tests for {@link LtUnlintNonExistingDefect}. * - * @since 0.0.0 + * @since 0.0.40 */ final class LtUnlintNonExistingDefectTest {