Skip to content

Commit

Permalink
Merge pull request #3627 from objectionary/3626
Browse files Browse the repository at this point in the history
lint all as a package
  • Loading branch information
yegor256 authored Dec 9, 2024
2 parents 5efbcb6 + 07977d0 commit 31b6b9c
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 60 deletions.
113 changes: 78 additions & 35 deletions eo-maven-plugin/src/main/java/org/eolang/maven/LintMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,17 @@
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.cactoos.iterable.Filtered;
import org.cactoos.list.ListOf;
import org.eolang.lints.Defect;
import org.eolang.lints.Program;
import org.eolang.lints.Programs;
import org.eolang.lints.Severity;
import org.eolang.maven.footprint.FpDefault;
import org.eolang.maven.tojos.ForeignTojo;
Expand Down Expand Up @@ -83,39 +85,41 @@ public final class LintMojo extends SafeMojo {
private boolean failOnWarning;

@Override
void exec() {
void exec() throws IOException {
final Collection<ForeignTojo> tojos = this.scopedTojos().withShaken();
final ConcurrentHashMap<Severity, Integer> counts = new ConcurrentHashMap<>();
counts.putIfAbsent(Severity.CRITICAL, 0);
counts.putIfAbsent(Severity.ERROR, 0);
counts.putIfAbsent(Severity.WARNING, 0);
final Collection<ForeignTojo> must = new ListOf<>(
new Filtered<>(
ForeignTojo::notLinted,
tojos
)
);
final int passed = new Threaded<>(
must,
tojos,
tojo -> this.lintOne(tojo, counts)
).total();
if (must.isEmpty()) {
Logger.info(this, "No XMIR programs out of %d linted", tojos.size());
} else if (tojos.isEmpty()) {
Logger.info(this, "There are no XMIR programs, nothing to lint");
} else {
if (tojos.isEmpty()) {
Logger.info(this, "There are no XMIR programs, nothing to lint individually");
}
Logger.info(
this,
"Also, %d XMIR programs linted as a package",
this.lintAll(counts)
);
if (counts.get(Severity.ERROR) > 0 || counts.get(Severity.CRITICAL) > 0
|| counts.get(Severity.WARNING) > 0 && this.failOnWarning) {
final String sum = LintMojo.summary(counts);
Logger.info(
this,
"Linted %d out of %d XMIR program(s) that needed this (out of %d total programs): %s",
passed, must.size(), tojos.size(), sum
passed, tojos.size(), tojos.size(), sum
);
throw new IllegalStateException(
String.format("In %d XMIR files, we found %s", tojos.size(), sum)
);
} else {
Logger.info(
this,
"Linted %d out of %d XMIR program(s) that needed this, no problems found",
passed, tojos.size()
);
if (counts.get(Severity.ERROR) > 0 || counts.get(Severity.CRITICAL) > 0
|| counts.get(Severity.WARNING) > 0 && this.failOnWarning) {
throw new IllegalStateException(
String.format("In %d XMIR files, we found %s", must.size(), sum)
);
}
}
}

Expand Down Expand Up @@ -145,6 +149,32 @@ private int lintOne(final ForeignTojo tojo,
return 1;
}

/**
* Lint all XMIR files together.
* @param counts Counts of errors, warnings, and critical
* @return Amount of seen XMIR files
* @throws IOException If failed to lint
*/
private int lintAll(final ConcurrentHashMap<Severity, Integer> counts) throws IOException {
final Map<String, Path> paths = new HashMap<>();
for (final ForeignTojo tojo : this.scopedTojos().withLinted()) {
paths.put(tojo.identifier(), tojo.linted());
}
final Map<String, XML> pkg = new HashMap<>();
for (final Map.Entry<String, Path> ent : paths.entrySet()) {
pkg.put(ent.getKey(), new XMLDocument(ent.getValue()));
}
final Collection<Defect> defects = new Programs(pkg).defects();
for (final Defect defect : defects) {
counts.compute(defect.severity(), (sev, before) -> before + 1);
LintMojo.embed(
pkg.get(defect.program()),
new ListOf<>(defect)
);
}
return pkg.size();
}

/**
* Log errors of XMIR.
* @param xml XMIR.
Expand Down Expand Up @@ -226,7 +256,7 @@ private static String summary(final ConcurrentHashMap<Severity, Integer> counts)
}

/**
* Find all possible linting defects.
* Find all possible linting defects and add them to the XMIR.
* @param xmir The XML before linting
* @return XML after linting
* @throws IOException If fails
Expand All @@ -236,25 +266,38 @@ private static XML lint(final XML xmir) throws IOException {
final Collection<Defect> defects = new Program(xmir).defects();
if (!defects.isEmpty()) {
dirs.xpath("/program").addIf("errors").strict(1);
for (final Defect defect : defects) {
if (LintMojo.suppressed(xmir, defect)) {
continue;
}
dirs.add("error")
.attr("check", defect.rule())
.attr("severity", defect.severity().mnemo())
.set(defect.text());
if (defect.line() > 0) {
dirs.attr("line", defect.line());
}
dirs.up();
}
LintMojo.embed(xmir, defects);
}
final Node node = xmir.inner();
new Xembler(dirs).applyQuietly(node);
return new XMLDocument(node);
}

/**
* Inject defect into XMIR.
* @param xmir The XML before linting
* @param defects The defects to inject
*/
private static void embed(final XML xmir, final Collection<Defect> defects) {
final Directives dirs = new Directives();
dirs.xpath("/program").addIf("errors").strict(1);
for (final Defect defect : defects) {
if (LintMojo.suppressed(xmir, defect)) {
continue;
}
dirs.add("error")
.attr("check", defect.rule())
.attr("severity", defect.severity().mnemo())
.set(defect.text());
if (defect.line() > 0) {
dirs.attr("line", defect.line());
}
dirs.up();
}
final Node node = xmir.inner();
new Xembler(dirs).applyQuietly(node);
}

/**
* This defect is suppressed?
* @param xmir The XMIR
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,14 @@ public Path shaken() {
return Paths.get(this.attribute(ForeignTojos.Attribute.SHAKEN));
}

/**
* The tojo linted xmir.
* @return The shaken xmir.
*/
public Path linted() {
return Paths.get(this.attribute(ForeignTojos.Attribute.LINTED));
}

/**
* The tojo eo object.
* @return The eo object.
Expand Down Expand Up @@ -176,24 +184,6 @@ public boolean notShaken() {
return res;
}

/**
* Checks if tojo was not already verified.
*
* @return True if optimization is required, false otherwise.
*/
public boolean notLinted() {
final Path src = this.xmir();
boolean res = true;
if (this.delegate.exists(ForeignTojos.Attribute.LINTED.getKey())) {
final Path tgt = this.optimized();
if (tgt.toFile().lastModified() >= src.toFile().lastModified()) {
Logger.debug(this, "Already verified %[file]s to %[file]s", src, tgt);
res = false;
}
}
return res;
}

/**
* Check if the given tojo has not been parsed.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,14 @@ public Collection<ForeignTojo> withShaken() {
return this.select(row -> row.exists(Attribute.SHAKEN.getKey()));
}

/**
* Get the tojos that have corresponding linted XMIR.
* @return The tojos.
*/
public Collection<ForeignTojo> withLinted() {
return this.select(row -> row.exists(Attribute.LINTED.getKey()));
}

/**
* Get the tojos that doesn't have dependency.
* @return The tojos.
Expand Down
31 changes: 31 additions & 0 deletions eo-maven-plugin/src/test/java/org/eolang/maven/LintMojoTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.jcabi.xml.XMLDocument;
import com.yegor256.Mktmp;
import com.yegor256.MktmpResolver;
import com.yegor256.farea.Farea;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
Expand All @@ -49,8 +50,38 @@
*/
@SuppressWarnings({"PMD.AvoidDuplicateLiterals", "PMD.TooManyMethods"})
@ExtendWith(MktmpResolver.class)
@ExtendWith(RandomProgramResolver.class)
final class LintMojoTest {

@Test
void lintsAgainAfterModification(@Mktmp final Path temp, @RandomProgram final String program)
throws Exception {
new Farea(temp).together(
f -> {
f.clean();
f.files().file("src/main/eo/foo.eo").write(program.getBytes());
f.build()
.plugins()
.appendItself()
.execution()
.goals("register", "parse", "optimize", "shake", "lint");
f.exec("process-classes");
final long before = f.files().file(
"target/eo/6-lint/foo.xmir"
).path().toFile().lastModified();
f.files().file("src/main/eo/foo.eo").write(program.getBytes());
f.exec("process-classes");
MatcherAssert.assertThat(
"the .xmir file is re-generated",
f.files().file(
"target/eo/6-lint/foo.xmir"
).path().toFile().lastModified(),
Matchers.not(Matchers.equalTo(before))
);
}
);
}

@Test
void doesNotFailWithNoErrorsAndWarnings(@Mktmp final Path temp) {
Assertions.assertDoesNotThrow(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,6 @@
@ExtendWith(MktmpResolver.class)
@ExtendWith(RandomProgramResolver.class)
final class PhiMojoTest {
/**
* Comment.
*/
private static final String COMMENT =
"# No comments.";

@Test
void convertsSimpleObjectToPhi(@Mktmp final Path temp, @RandomProgram final String program)
Expand Down Expand Up @@ -172,7 +167,7 @@ void createsFiles(@Mktmp final Path temp) throws Exception {
),
new FakeMaven(temp)
.withProgram(
PhiMojoTest.COMMENT,
"No comments.",
"[] > cart",
" memory 0 > total",
" [i] > add",
Expand Down Expand Up @@ -218,7 +213,7 @@ void doesNotFailOnError(@Mktmp final Path temp) {
Assertions.assertDoesNotThrow(
() -> new FakeMaven(temp)
.withProgram(
PhiMojoTest.COMMENT,
"No comments.",
"[] > without-name",
" true"
)
Expand Down

0 comments on commit 31b6b9c

Please sign in to comment.