From 8f2494a2d370160c985f3f89fa9ce40ceffbacd2 Mon Sep 17 00:00:00 2001 From: Marat-Tim Date: Sat, 10 May 2025 21:49:41 +0300 Subject: [PATCH 1/3] feat(#556): auto-fix architecture --- src/main/java/org/eolang/lints/Defect.java | 23 +++++- src/main/java/org/eolang/lints/DfContext.java | 7 ++ src/main/java/org/eolang/lints/LtByXsl.java | 18 ++++- .../lints/LtUnlintNonExistingDefect.java | 19 ++++- .../java/org/eolang/lints/ObjectSource.java | 20 ++++++ .../java/org/eolang/lints/ScopedDefects.java | 3 +- .../java/org/eolang/lints/fix/Create.java | 41 +++++++++++ .../java/org/eolang/lints/fix/Delete.java | 41 +++++++++++ src/main/java/org/eolang/lints/fix/Edit.java | 41 +++++++++++ src/main/java/org/eolang/lints/fix/File.java | 34 +++++++++ src/main/java/org/eolang/lints/fix/Fix.java | 71 +++++++++++++++++++ .../java/org/eolang/lints/fix/Position.java | 33 +++++++++ .../java/org/eolang/lints/fix/Rename.java | 46 ++++++++++++ .../org/eolang/lints/aliases/empty-alias.xsl | 17 ++++- src/test/java/matchers/DefectsMatcher.java | 6 +- .../java/org/eolang/lints/DefectTest.java | 4 +- 16 files changed, 412 insertions(+), 12 deletions(-) create mode 100644 src/main/java/org/eolang/lints/ObjectSource.java create mode 100644 src/main/java/org/eolang/lints/fix/Create.java create mode 100644 src/main/java/org/eolang/lints/fix/Delete.java create mode 100644 src/main/java/org/eolang/lints/fix/Edit.java create mode 100644 src/main/java/org/eolang/lints/fix/File.java create mode 100644 src/main/java/org/eolang/lints/fix/Fix.java create mode 100644 src/main/java/org/eolang/lints/fix/Position.java create mode 100644 src/main/java/org/eolang/lints/fix/Rename.java diff --git a/src/main/java/org/eolang/lints/Defect.java b/src/main/java/org/eolang/lints/Defect.java index 554bf7bdb..86c2c0b52 100644 --- a/src/main/java/org/eolang/lints/Defect.java +++ b/src/main/java/org/eolang/lints/Defect.java @@ -6,6 +6,7 @@ import com.jcabi.manifests.Manifests; import java.util.Objects; +import org.eolang.lints.fix.Fix; /** * A single defect found. @@ -102,6 +103,13 @@ public interface Defect { */ boolean experimental(); + /** + * Information on how to fix this defect. + * + * @return Fix info. + */ + Fix fix(); + /** * Default implementation of {@link Defect}. *

@@ -142,6 +150,11 @@ final class Default implements Defect { */ private final boolean experiment; + /** + * How to fix. + */ + private final Fix fix; + /** * Ctor. * @param rule Rule name @@ -155,7 +168,7 @@ public Default( final String rule, final Severity severity, final String object, final int line, final String text ) { - this(rule, severity, object, line, text, false); + this(rule, severity, object, line, text, false, new Fix()); } /** @@ -175,7 +188,7 @@ public Default( public Default( final String rule, final Severity severity, final String object, final int line, final String text, - final boolean exprmnt + final boolean exprmnt, final Fix fix ) { this.rle = rule; this.sev = severity; @@ -183,6 +196,7 @@ public Default( this.lineno = line; this.txt = text; this.experiment = exprmnt; + this.fix = fix; } @Override @@ -237,6 +251,11 @@ public boolean experimental() { return this.experiment; } + @Override + public Fix fix() { + return this.fix; + } + @Override public boolean equals(final Object obj) { final boolean result; diff --git a/src/main/java/org/eolang/lints/DfContext.java b/src/main/java/org/eolang/lints/DfContext.java index 2036b72f5..23ca32b0b 100644 --- a/src/main/java/org/eolang/lints/DfContext.java +++ b/src/main/java/org/eolang/lints/DfContext.java @@ -4,6 +4,8 @@ */ package org.eolang.lints; +import org.eolang.lints.fix.Fix; + /** * Defect with context. * @since 0.0.40 @@ -60,6 +62,11 @@ public String version() { return this.origin.version(); } + @Override + public Fix fix() { + return this.origin.fix(); + } + @Override public String context() { return this.ctxt.replace("<", "<") diff --git a/src/main/java/org/eolang/lints/LtByXsl.java b/src/main/java/org/eolang/lints/LtByXsl.java index bd06eacf8..832cab7da 100644 --- a/src/main/java/org/eolang/lints/LtByXsl.java +++ b/src/main/java/org/eolang/lints/LtByXsl.java @@ -20,6 +20,9 @@ import org.cactoos.io.ResourceOf; import org.cactoos.text.IoCheckedText; import org.cactoos.text.TextOf; +import org.eolang.lints.fix.Edit; +import org.eolang.lints.fix.File; +import org.eolang.lints.fix.Fix; import org.eolang.parser.ObjectName; /** @@ -98,6 +101,7 @@ public Collection defects(final XML xmir) { String.format("No severity reported by %s", this.rule) ); } + final Optional message = xml.element("message").text(); defects.add( new DfContext( new Defect.Default( @@ -105,8 +109,18 @@ public Collection defects(final XML xmir) { Severity.parsed(sever.get()), new ObjectName(xmir).get(), this.lineno(xml), - xml.text().get(), - LtByXsl.experimental(xml) + message.orElseGet(() -> xml.text().get()), + LtByXsl.experimental(xml), + message.map( + ignored -> + new Fix( + new File( + xmir.xpath("/@source/text()").get(0), + new Edit(xml.element("edit")) + ) + ) + ) + .orElseGet(Fix::new) ), xml.attribute("context").text().orElse("") ) diff --git a/src/main/java/org/eolang/lints/LtUnlintNonExistingDefect.java b/src/main/java/org/eolang/lints/LtUnlintNonExistingDefect.java index 5386565df..5edd781a5 100644 --- a/src/main/java/org/eolang/lints/LtUnlintNonExistingDefect.java +++ b/src/main/java/org/eolang/lints/LtUnlintNonExistingDefect.java @@ -19,6 +19,10 @@ import org.cactoos.list.ListOf; import org.cactoos.text.IoCheckedText; import org.cactoos.text.TextOf; +import org.eolang.lints.fix.Edit; +import org.eolang.lints.fix.File; +import org.eolang.lints.fix.Fix; +import org.eolang.lints.fix.Position; import org.eolang.parser.ObjectName; /** @@ -82,7 +86,7 @@ public Collection defects(final XML xmir) throws IOException { ) ) .map(xnav -> xnav.text().get()) - .collect(Collectors.toList()) + .map(Integer::parseInt) .forEach( line -> defects.add( @@ -90,10 +94,21 @@ public Collection defects(final XML xmir) throws IOException { this.name(), Severity.WARNING, new ObjectName(xmir).get(), - Integer.parseInt(line), + line, String.format( "Unlinting rule '%s' doesn't make sense, since there are no defects with it", unlint + ), + false, + new Fix( + new File( + new ObjectSource(xmir).get(), + new Edit( + new Position(line, 0), + new Position(line + 1, 0), + "" + ) + ) ) ) ) diff --git a/src/main/java/org/eolang/lints/ObjectSource.java b/src/main/java/org/eolang/lints/ObjectSource.java new file mode 100644 index 000000000..4c2ae934f --- /dev/null +++ b/src/main/java/org/eolang/lints/ObjectSource.java @@ -0,0 +1,20 @@ +/* + * 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; + +final class ObjectSource { + private final Xnav xnav; + + ObjectSource(XML xml) { + this.xnav = new Xnav(xml.inner()); + } + + String get() { + return xnav.attribute("source").text().orElse(""); + } +} diff --git a/src/main/java/org/eolang/lints/ScopedDefects.java b/src/main/java/org/eolang/lints/ScopedDefects.java index 2fe86a40b..c3d650f31 100644 --- a/src/main/java/org/eolang/lints/ScopedDefects.java +++ b/src/main/java/org/eolang/lints/ScopedDefects.java @@ -31,7 +31,8 @@ final class ScopedDefects extends CollectionEnvelope { defect.object(), defect.line(), defect.text(), - defect.experimental() + defect.experimental(), + defect.fix() ), defect.context() ), diff --git a/src/main/java/org/eolang/lints/fix/Create.java b/src/main/java/org/eolang/lints/fix/Create.java new file mode 100644 index 000000000..9b41a7f54 --- /dev/null +++ b/src/main/java/org/eolang/lints/fix/Create.java @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2016-2025 Objectionary.com + * SPDX-License-Identifier: MIT + */ +package org.eolang.lints.fix; + +import com.github.lombrozo.xnav.Xnav; + +public class Create { + private final String path; + + private final boolean overwrite; + + private final boolean ignoreIfExists; + + public Create(String path, boolean overwrite, boolean ignoreIfExists) { + this.path = path; + this.overwrite = overwrite; + this.ignoreIfExists = ignoreIfExists; + } + + public Create(Xnav xnav) { + this( + xnav.element("path").text().get(), + Boolean.parseBoolean(xnav.element("overwrite").text().orElse("")), + Boolean.parseBoolean(xnav.element("ignoreIfExists").text().orElse("")) + ); + } + + public String path() { + return this.path; + } + + public boolean overwrite() { + return this.overwrite; + } + + public boolean ignoreIfExists() { + return this.ignoreIfExists; + } +} diff --git a/src/main/java/org/eolang/lints/fix/Delete.java b/src/main/java/org/eolang/lints/fix/Delete.java new file mode 100644 index 000000000..e3e100056 --- /dev/null +++ b/src/main/java/org/eolang/lints/fix/Delete.java @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2016-2025 Objectionary.com + * SPDX-License-Identifier: MIT + */ +package org.eolang.lints.fix; + +import com.github.lombrozo.xnav.Xnav; + +public class Delete { + private final String path; + + private final boolean recursive; + + private final boolean ignoreIfNotExists; + + public Delete(String path, boolean recursive, boolean ignoreIfNotExists) { + this.path = path; + this.recursive = recursive; + this.ignoreIfNotExists = ignoreIfNotExists; + } + + public Delete(final Xnav xnav) { + this( + xnav.element("path").text().get(), + Boolean.parseBoolean(xnav.element("recursive").text().orElse("")), + Boolean.parseBoolean(xnav.element("ignoreIfNotExists").text().orElse("")) + ); + } + + public String path() { + return this.path; + } + + public boolean recursive() { + return this.recursive; + } + + public boolean ignoreIfNotExists() { + return this.ignoreIfNotExists; + } +} diff --git a/src/main/java/org/eolang/lints/fix/Edit.java b/src/main/java/org/eolang/lints/fix/Edit.java new file mode 100644 index 000000000..d0c2998cb --- /dev/null +++ b/src/main/java/org/eolang/lints/fix/Edit.java @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2016-2025 Objectionary.com + * SPDX-License-Identifier: MIT + */ +package org.eolang.lints.fix; + +import com.github.lombrozo.xnav.Xnav; + +public class Edit { + private final Position start; + + private final Position end; + + private final String newText; + + public Edit(final Position start, final Position end, final String newText) { + this.start = start; + this.end = end; + this.newText = newText; + } + + public Edit(Xnav xnav) { + this( + new Position(xnav.element("start")), + new Position(xnav.element("end")), + xnav.element("newText").text().get() + ); + } + + public Position start() { + return this.start; + } + + public Position end() { + return this.end; + } + + public String newText() { + return this.newText; + } +} diff --git a/src/main/java/org/eolang/lints/fix/File.java b/src/main/java/org/eolang/lints/fix/File.java new file mode 100644 index 000000000..eb1ccbaee --- /dev/null +++ b/src/main/java/org/eolang/lints/fix/File.java @@ -0,0 +1,34 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2016-2025 Objectionary.com + * SPDX-License-Identifier: MIT + */ +package org.eolang.lints.fix; + +import com.github.lombrozo.xnav.Xnav; +import java.util.Arrays; +import java.util.Collection; + +public class File { + private final String path; + private final Collection edits; + + public File(final String path, final Edit... edits) { + this.path = path; + this.edits = Arrays.asList(edits); + } + + public File(final Xnav xnav) { + this( + xnav.attribute("path").text().get(), + xnav.path("edit").map(Edit::new).toArray(Edit[]::new) + ); + } + + public String path() { + return this.path; + } + + public Collection edits() { + return this.edits; + } +} diff --git a/src/main/java/org/eolang/lints/fix/Fix.java b/src/main/java/org/eolang/lints/fix/Fix.java new file mode 100644 index 000000000..eaaa9446f --- /dev/null +++ b/src/main/java/org/eolang/lints/fix/Fix.java @@ -0,0 +1,71 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2016-2025 Objectionary.com + * SPDX-License-Identifier: MIT + */ +package org.eolang.lints.fix; + +import com.github.lombrozo.xnav.Xnav; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public class Fix { + private final Collection files; + + private final Collection creates; + + private final Collection deletes; + + private final Collection renames; + + public Fix(Collection files, Collection creates, Collection deletes, Collection renames) { + this.files = files; + this.creates = creates; + this.deletes = deletes; + this.renames = renames; + } + + public Fix() { + this( + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList() + ); + } + + public Fix(File file) { + this( + Collections.singletonList(file), + Collections.emptyList(), + Collections.emptyList(), + Collections.emptyList() + ); + } + + public Fix(Xnav fix) { + this( + fix.path("file").map(File::new).collect(Collectors.toList()), + fix.path("create").map(Create::new).collect(Collectors.toList()), + fix.path("delete").map(Delete::new).collect(Collectors.toList()), + fix.path("rename").map(Rename::new).collect(Collectors.toList()) + ); + } + + public Collection files() { + return this.files; + } + + public Collection creates() { + return this.creates; + } + + public Collection deletes() { + return this.deletes; + } + + public Collection renames() { + return this.renames; + } +} diff --git a/src/main/java/org/eolang/lints/fix/Position.java b/src/main/java/org/eolang/lints/fix/Position.java new file mode 100644 index 000000000..8bc4d1bc8 --- /dev/null +++ b/src/main/java/org/eolang/lints/fix/Position.java @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2016-2025 Objectionary.com + * SPDX-License-Identifier: MIT + */ +package org.eolang.lints.fix; + +import com.github.lombrozo.xnav.Xnav; + +public class Position { + private final int line; + + private final int character; + + public Position(final int line, final int character) { + this.line = line; + this.character = character; + } + + public Position(Xnav xnav) { + this( + Integer.parseInt(xnav.attribute("line").text().get()), + Integer.parseInt(xnav.attribute("character").text().get()) + ); + } + + public int line() { + return this.line; + } + + public int character() { + return this.character; + } +} diff --git a/src/main/java/org/eolang/lints/fix/Rename.java b/src/main/java/org/eolang/lints/fix/Rename.java new file mode 100644 index 000000000..7f70ef087 --- /dev/null +++ b/src/main/java/org/eolang/lints/fix/Rename.java @@ -0,0 +1,46 @@ +/* + * SPDX-FileCopyrightText: Copyright (c) 2016-2025 Objectionary.com + * SPDX-License-Identifier: MIT + */ +package org.eolang.lints.fix; + +import com.github.lombrozo.xnav.Xnav; + +public class Rename { + private final String oldPath; + private final String newPath; + private final boolean overwrite; + private final boolean ignoreIfExists; + + public Rename(String oldPath, String newPath, boolean overwrite, boolean ignoreIfExists) { + this.oldPath = oldPath; + this.newPath = newPath; + this.overwrite = overwrite; + this.ignoreIfExists = ignoreIfExists; + } + + public Rename(Xnav xnav) { + this( + xnav.element("oldPath").text().get(), + xnav.element("newPath").text().get(), + Boolean.parseBoolean(xnav.element("overwrite").text().orElse("")), + Boolean.parseBoolean(xnav.element("ignoreIfExists").text().orElse("")) + ); + } + + public String oldPath() { + return this.oldPath; + } + + public String newPath() { + return this.newPath; + } + + public boolean overwrite() { + return this.overwrite; + } + + public boolean ignoreIfExists() { + return this.ignoreIfExists; + } +} diff --git a/src/main/resources/org/eolang/lints/aliases/empty-alias.xsl b/src/main/resources/org/eolang/lints/aliases/empty-alias.xsl index 47edd70cb..3e2c2a776 100644 --- a/src/main/resources/org/eolang/lints/aliases/empty-alias.xsl +++ b/src/main/resources/org/eolang/lints/aliases/empty-alias.xsl @@ -10,7 +10,7 @@ - + @@ -23,8 +23,19 @@ error - The alias doesn't have a tail - + + The alias doesn't have a tail + + + + + + + + + + + diff --git a/src/test/java/matchers/DefectsMatcher.java b/src/test/java/matchers/DefectsMatcher.java index 33bd9316a..8041e1da8 100644 --- a/src/test/java/matchers/DefectsMatcher.java +++ b/src/test/java/matchers/DefectsMatcher.java @@ -30,13 +30,17 @@ public final class DefectsMatcher extends BaseMatcher { public boolean matches(final Object xml) { final Collection defects = new ArrayList<>(0); for (final XML defect : ((XML) xml).nodes("/defects/defect")) { + final String text = defect.xpath("text()") + .stream() + .findFirst() + .orElse(defect.xpath("message/text()").get(0)); defects.add( new Defect.Default( "unknown", Severity.parsed(defect.xpath("@severity").get(0)), "unknown", Integer.parseInt(defect.xpath("@line").get(0)), - defect.xpath("text()").get(0) + text ) ); } diff --git a/src/test/java/org/eolang/lints/DefectTest.java b/src/test/java/org/eolang/lints/DefectTest.java index a1602d6e2..17e5fccc4 100644 --- a/src/test/java/org/eolang/lints/DefectTest.java +++ b/src/test/java/org/eolang/lints/DefectTest.java @@ -4,6 +4,7 @@ */ package org.eolang.lints; +import org.eolang.lints.fix.Fix; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; @@ -72,7 +73,8 @@ void returnsExperimental() { "f.12", 42, "This is wrong", - true + true, + new Fix() ).experimental(), Matchers.equalTo(true) ); From 7d3a8ea22a3234434a93aa1c6896d7b38915f07a Mon Sep 17 00:00:00 2001 From: Marat-Tim Date: Sun, 11 May 2025 12:19:54 +0300 Subject: [PATCH 2/3] feat(#556): fix xsl tests --- src/test/java/matchers/DefectsMatcher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/matchers/DefectsMatcher.java b/src/test/java/matchers/DefectsMatcher.java index 8041e1da8..ce0199cf6 100644 --- a/src/test/java/matchers/DefectsMatcher.java +++ b/src/test/java/matchers/DefectsMatcher.java @@ -33,7 +33,7 @@ public boolean matches(final Object xml) { final String text = defect.xpath("text()") .stream() .findFirst() - .orElse(defect.xpath("message/text()").get(0)); + .orElseGet(() -> defect.xpath("/message/text()").get(0)); defects.add( new Defect.Default( "unknown", From 9240e703d57411889fa2efdf96bc0219629fbb2e Mon Sep 17 00:00:00 2001 From: Marat-Tim Date: Sun, 11 May 2025 12:29:27 +0300 Subject: [PATCH 3/3] feat(#556): fix xsl tests --- src/test/java/matchers/DefectsMatcher.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/matchers/DefectsMatcher.java b/src/test/java/matchers/DefectsMatcher.java index ce0199cf6..6e1a9267c 100644 --- a/src/test/java/matchers/DefectsMatcher.java +++ b/src/test/java/matchers/DefectsMatcher.java @@ -33,7 +33,7 @@ public boolean matches(final Object xml) { final String text = defect.xpath("text()") .stream() .findFirst() - .orElseGet(() -> defect.xpath("/message/text()").get(0)); + .orElseGet(() -> defect.xpath("message/text()").get(0)); defects.add( new Defect.Default( "unknown",