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..6e1a9267c 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()
+ .orElseGet(() -> 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)
);