Skip to content

feat(#4983): enable WPA caching in MjLint#4986

Merged
yegor256 merged 3 commits into
objectionary:masterfrom
volodya-lombrozo:4983-wpa-cache
Apr 15, 2026
Merged

feat(#4983): enable WPA caching in MjLint#4986
yegor256 merged 3 commits into
objectionary:masterfrom
volodya-lombrozo:4983-wpa-cache

Conversation

@volodya-lombrozo

@volodya-lombrozo volodya-lombrozo commented Apr 14, 2026

Copy link
Copy Markdown
Member

This PR enables the WPA cache in MjLintTest.java.

Fixes #4983

Summary by CodeRabbit

  • Improvements
    • Enhanced caching mechanism for linting analysis with improved defect collection and handling
    • Added performance notifications when caching is disabled during linting operations

Copilot AI review requested due to automatic review settings April 14, 2026 06:07
@coderabbitai

coderabbitai Bot commented Apr 14, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

The PR introduces injectable file-filtering predicates to the Cache class and refactors MjLint's linting workflow to cache defect results in XMIR format. Cache hashing logic converts from static to instance methods, enabling directory walks to apply custom filters. MjLint now collects and persists linting defects via XMIR caching, recovering defect data across runs through a new XMIR parsing method.

Changes

Cohort / File(s) Summary
Cache Filter Support
eo-maven-plugin/src/main/java/org/eolang/maven/Cache.java
Added injectable Predicate<Path> filter parameter to constructor; converted static sha/dirSha methods to instance methods to apply filters during directory traversal; existing constructor delegates with default all-inclusive predicate.
MjLint Defect Caching
eo-maven-plugin/src/main/java/org/eolang/maven/MjLint.java
Refactored lintAll to collect List<Defect> results; added read(Path) method to parse defects from XMIR /defects/error paths; enabled caching of XMIR outputs and recovery of defect data; changed wpa to return List<Defect> instead of pkg.size().
Test Enablement & Assertions
eo-maven-plugin/src/test/java/org/eolang/maven/MjLintTest.java
Removed @Disabled annotation from savesForWholeProgramAnalysisResultsToCache test; replaced WPA-error setup with deterministic successful execution; updated cache path assertion to use simpler wpa.xmir path under cache/MjLint.CACHE; refactored hash supplier inline.

Sequence Diagram(s)

sequenceDiagram
    participant MjLint
    participant Cache
    participant XMIR
    participant Defect as Defect List

    MjLint->>MjLint: lintAll() invoked
    alt Cache enabled
        MjLint->>Cache: Apply with filter predicate
        Cache->>Cache: sha(dir) with filtered files
        Cache->>XMIR: Write XMIR with defects
        MjLint->>XMIR: read(Path)
        XMIR->>Defect: Parse /defects/error
        Defect->>MjLint: Return List<Defect>
    else Cache disabled
        MjLint->>MjLint: wpa(pkg)
        MjLint->>Defect: Collect List<Defect>
        Defect->>MjLint: Return results
    end
    MjLint->>MjLint: Update counts from Defect
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • yegor256

🐰 A filter's whisper, a cache's delight,
Defects now persist through each compile night,
Predicates dance, and paths find their way,
XMIR remembers what lint had to say!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 72.73% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(#4983): enable WPA caching in MjLint' directly and clearly describes the main objective of the changeset - enabling WPA (whole program analysis) caching functionality in the MjLint component.
Linked Issues check ✅ Passed The PR successfully addresses issue #4983 by enabling WPA caching in MjLint and removing the puzzle text from MjLintTest.java, meeting the stated acceptance criteria.
Out of Scope Changes check ✅ Passed All changes are directly related to enabling WPA caching: Cache.java adds filtering support, MjLint.java implements caching logic with defect collection, and MjLintTest.java enables and updates the related test.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

Review ran into problems

🔥 Problems

Timed out fetching pipeline failures after 30000ms


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Performance Analysis

All benchmarks are within the acceptable range. No critical degradation detected (threshold is 100%). Please refer to the detailed report for more information.

Click to see the detailed report
Test Base Score PR Score Change % Change Unit Mode
benchmarks.XmirBench.xmirToEO 193.846 204.518 10.672 5.51% ms/op Average Time

⚠️ Performance loss: benchmarks.XmirBench.xmirToEO is slower by 10.672 ms/op (5.51%)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to enable and validate Whole Program Analysis (WPA) caching for MjLint by turning on the previously disabled cache-related test and adding cache support in the WPA linting path.

Changes:

  • Removed the TODO puzzle and enabled the WPA cache test in MjLintTest.
  • Added WPA caching logic to MjLint.lintAll() and introduced parsing of cached WPA defects.
  • Extended Cache to support hashing directories with a configurable file filter.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

File Description
eo-maven-plugin/src/test/java/org/eolang/maven/MjLintTest.java Enables the WPA cache test and updates expectations for cached WPA output.
eo-maven-plugin/src/main/java/org/eolang/maven/MjLint.java Implements WPA caching in lintAll() and adds cached defect parsing.
eo-maven-plugin/src/main/java/org/eolang/maven/Cache.java Adds a Predicate<Path> filter to directory hashing to support selective cache invalidation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +450 to +459
private static List<Defect> read(final Path path) {
return new Xnav(path).path("/defects/error").map(
node -> new Defect.Default(
node.attribute("check").text().orElseThrow(),
Severity.parsed(node.attribute("severity").text().orElseThrow()),
"",
0,
""
)
).collect(Collectors.toList());

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

read() uses orElseThrow() for required attributes, which will throw a generic NoSuchElementException if the cache XMIR is corrupted/partial. Elsewhere in this class (see existing()), missing required attributes throw an exception with a clear message.

It would be more diagnosable to throw with an explicit message here too (e.g., "Cached WPA defect must contain 'check'/'severity' attribute").

Copilot uses AI. Check for mistakes.
Comment on lines 67 to 148
@@ -61,7 +82,7 @@ final class Cache {
*/
public void apply(final Path source, final Path target, final Path tail) {
try {
final String sha = Cache.sha(source);
final String sha = this.sha(source);
final Path hash = this.hash(tail);
final Path cache = this.base.resolve(tail);
if (
@@ -99,10 +120,10 @@ private Path hash(final Path tail) {
* @param any File or directory path
* @return Base64-encoded SHA-256 hash of the file or directory contents
*/
private static String sha(final Path any) {
private String sha(final Path any) {
final String result;
if (Files.isDirectory(any)) {
result = Cache.dirSha(any);
result = this.dirSha(any);
} else if (Files.isRegularFile(any)) {
result = Cache.fileSha(any);
} else {
@@ -118,11 +139,12 @@ private static String sha(final Path any) {
* @param dir Directory path.
* @return Base64-encoded SHA-256 hash of the directory contents.
*/
private static String dirSha(final Path dir) {
private String dirSha(final Path dir) {
try {
final MessageDigest digest = MessageDigest.getInstance("SHA-256");
try (Stream<Path> stream = Files.walk(dir)) {
stream.filter(Files::isRegularFile)
.filter(this.filter::test)
.sorted(Comparator.comparing(Path::toString))

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A new Cache feature was added (directory hashing filter via Predicate<Path>), but there’s no unit test coverage to confirm that excluded files don’t affect the computed hash and that included files do. Since this is now relied on by WPA caching, a focused test would help prevent regressions (e.g., create a directory with two files, exclude one via filter, modify it, and assert the cache does not recompile).

Copilot uses AI. Check for mistakes.
Comment on lines +212 to +227
final Path wpa = Path.of("wpa.xmir");
final Path target = this.targetDir.toPath().resolve(MjLint.DIR).resolve(wpa);
new Cache(
this.cache.toPath().resolve(MjLint.CACHE),
root -> {
Logger.info(this, "Linting a package");
final Directives all = new Directives().add("defects");
for (final Defect defect : this.wpa(pkg)) {
MjLint.embedded(all, defect);
}
all.up();
return new Xembler(all).xmlQuietly();
},
p -> p.getFileName().toString().endsWith(".xmir")
&& !p.getFileName().equals(wpa)
).apply(this.sourcesDir.toPath(), target, wpa);

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the WPA cached branch, the cache key is computed from this.sourcesDir but the directory hash is filtered to only include .xmir files (excluding wpa.xmir). sourcesDir defaults to src/main/eo, so the hash will likely be computed from an empty file set (or at least ignore the real WPA inputs), causing stale cache reuse across source changes and potential cross-project cache collisions.

Consider hashing the actual WPA inputs (e.g., the XMIR directory under targetDir, or the EO sources with a .eo filter), and/or using a CachePath that includes plugin version + a project/package-specific component so wpa.xmir doesn’t live at a global fixed location.

Copilot uses AI. Check for mistakes.
Comment on lines +210 to +238
final List<Defect> defects;
if (this.cacheEnabled) {
final Path wpa = Path.of("wpa.xmir");
final Path target = this.targetDir.toPath().resolve(MjLint.DIR).resolve(wpa);
new Cache(
this.cache.toPath().resolve(MjLint.CACHE),
root -> {
Logger.info(this, "Linting a package");
final Directives all = new Directives().add("defects");
for (final Defect defect : this.wpa(pkg)) {
MjLint.embedded(all, defect);
}
all.up();
return new Xembler(all).xmlQuietly();
},
p -> p.getFileName().toString().endsWith(".xmir")
&& !p.getFileName().equals(wpa)
).apply(this.sourcesDir.toPath(), target, wpa);
defects = MjLint.read(target);
} else {
Logger.info(
this,
"Linting a package without cache, this might be slow, consider enabling cache"
);
defects = this.wpa(pkg);
}
for (final Defect defect : defects) {
counts.compute(defect.severity(), (sev, before) -> before + 1);
}

Copilot AI Apr 14, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With cache enabled, WPA defects are only logged during a cache miss (inside the compilation lambda via this.wpa(pkg)). On a cache hit you read cached defects and update counts, but nothing is logged, which makes output depend on cache state (unlike lintOne, which logs every run even when cached).

If consistent logging is desired, log defects after loading them from cache as well. That likely requires caching/restoring the full defect details (object/line/text) instead of dropping them in read().

Copilot uses AI. Check for mistakes.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@eo-maven-plugin/src/main/java/org/eolang/maven/MjLint.java`:
- Around line 445-460: The read method currently discards defect details (only
using check and severity); update it to extract and preserve the defect's
optional attributes and content from the Xnav node: read
node.attribute("line").text().map(Integer::parseInt).orElse(0) for the line
number, node.text().orElse("") for the defect message, and if present
node.attribute("file").text().orElse("") for the file/path, then pass these
values into the Defect.Default constructor (while keeping Severity.parsed(...)
for severity) so cached XMIR defects retain full fidelity; use safe parsing with
defaults to avoid exceptions in read/Xnav handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0da3e241-e505-4d30-9923-ceb07b400a56

📥 Commits

Reviewing files that changed from the base of the PR and between 132d112 and 9676631.

📒 Files selected for processing (3)
  • eo-maven-plugin/src/main/java/org/eolang/maven/Cache.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/MjLint.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/MjLintTest.java

Comment on lines +445 to +460
/**
* Read defects from XMIR.
* @param path Path to XMIR
* @return Collection of defects
*/
private static List<Defect> read(final Path path) {
return new Xnav(path).path("/defects/error").map(
node -> new Defect.Default(
node.attribute("check").text().orElseThrow(),
Severity.parsed(node.attribute("severity").text().orElseThrow()),
"",
0,
""
)
).collect(Collectors.toList());
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Defect data is discarded when reading from cache.

The read method only extracts check and severity, but ignores line attribute and text content that are actually persisted by embedded(). While this works for the current counting-only usage, it loses defect fidelity.

Consider extracting available data to preserve full defect information:

Proposed fix to preserve defect data
     private static List<Defect> read(final Path path) {
         return new Xnav(path).path("/defects/error").map(
             node -> new Defect.Default(
                 node.attribute("check").text().orElseThrow(),
                 Severity.parsed(node.attribute("severity").text().orElseThrow()),
                 "",
-                0,
-                ""
+                Integer.parseInt(node.attribute("line").text().orElse("0")),
+                node.text().orElse("")
             )
         ).collect(Collectors.toList());
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eo-maven-plugin/src/main/java/org/eolang/maven/MjLint.java` around lines 445
- 460, The read method currently discards defect details (only using check and
severity); update it to extract and preserve the defect's optional attributes
and content from the Xnav node: read
node.attribute("line").text().map(Integer::parseInt).orElse(0) for the line
number, node.text().orElse("") for the defect message, and if present
node.attribute("file").text().orElse("") for the file/path, then pass these
values into the Defect.Default constructor (while keeping Severity.parsed(...)
for severity) so cached XMIR defects retain full fidelity; use safe parsing with
defaults to avoid exceptions in read/Xnav handling.

@volodya-lombrozo

Copy link
Copy Markdown
Member Author

@yegor256 could you review this one, please?

@yegor256
yegor256 merged commit 31da08e into objectionary:master Apr 15, 2026
28 of 29 checks passed
@0crat

0crat commented Apr 15, 2026

Copy link
Copy Markdown

@volodya-lombrozo Great contribution! 🎉 You've earned +12 points (+16 base, -4 for 122 lines of code exceeding our 100-line guideline). Keep the quality contributions flowing - your running score is now +272! Don't forget to check your Zerocracy account for updates.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enablement of MjLintTests Fails due to WPA Cache Issues in MjLintTest.java

4 participants