Skip to content

build(#4884): upgrade qulice to version 0.25.1 in eo-maven-plugin#4897

Merged
yegor256 merged 1 commit into
objectionary:masterfrom
volodya-lombrozo:4884-qulice-upgrade
Feb 25, 2026
Merged

build(#4884): upgrade qulice to version 0.25.1 in eo-maven-plugin#4897
yegor256 merged 1 commit into
objectionary:masterfrom
volodya-lombrozo:4884-qulice-upgrade

Conversation

@volodya-lombrozo

@volodya-lombrozo volodya-lombrozo commented Feb 25, 2026

Copy link
Copy Markdown
Member

This PR upgrades the qulice-maven-plugin to version 0.25.1 in eo-maven-plugin.

Related to #4884

Summary by CodeRabbit

Release Notes

  • Chores

    • Updated build configuration with explicit plugin version specification for improved build consistency.
  • Refactor

    • Improved code style and readability through code simplification and explicit type declarations throughout the codebase.
  • Tests

    • Restructured and reorganized test suite for better maintainability and coverage verification.

Copilot AI review requested due to automatic review settings February 25, 2026 13:34
@github-actions github-actions Bot added the core label Feb 25, 2026
@coderabbitai

coderabbitai Bot commented Feb 25, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This pull request applies widespread code quality refinements across the eo-maven-plugin module. Changes include adding/adjusting PMD suppression annotations, refactoring code to inline variables and remove intermediate assignments, changing type declarations from concrete to more generic types (e.g., ConcurrentHashMap to Map), adding @FunctionalInterface markers, and updating test assertions and structure. Additionally, the qulice-maven-plugin dependency is pinned to version 0.25.1.

Changes

Cohort / File(s) Summary
Build Configuration
eo-maven-plugin/pom.xml
Added explicit version 0.25.1 to qulice-maven-plugin declaration in the qulice profile.
Core Cache & Hash Implementation
eo-maven-plugin/src/main/java/org/eolang/maven/Cache.java, ChSource.java, CommitHash.java
Updated Cache stream typing to explicit InputStream, simplified hash return; added PMD suppression to ChSource.hash(); added @Override to CommitHash.value() method signature.
Concurrency & Synchronization
eo-maven-plugin/src/main/java/org/eolang/maven/Catalogs.java, ConcurrentCache.java
Introduced Map<Path, Tojos> interface type while retaining ConcurrentHashMap initialization; added class-level PMD.AvoidSynchronizedStatement suppression and TODO note on potential ReentrantLock refactoring.
Functional Interfaces & Core Abstractions
eo-maven-plugin/src/main/java/org/eolang/maven/Dependencies.java, Footprint.java, DpsDefault.java, DpsEachWithoutTransitive.java, DpsWithRuntime.java
Added @FunctionalInterface annotations; refactored to inline temporary variables in filter/mapping chains; changed DpsWithRuntime local variable from ListOf to List.
Maven Mojo Execution Methods
eo-maven-plugin/src/main/java/org/eolang/maven/MjAssemble.java, MjCompile.java, MjLint.java, MjParse.java, MjProbe.java, MjPull.java, MjResolve.java, MjTranspile.java
Added @SuppressWarnings("PMD.UnnecessaryLocalRule") annotations to public exec() and related methods; changed MjPull annotation from PMD.PrematureDeclaration to PMD.UnnecessaryLocalRule.
Maven Mojo Implementation & Utility
eo-maven-plugin/src/main/java/org/eolang/maven/MjPrint.java, MjRegister.java, MjSafe.java, MjUnplace.java, MjUnspile.java, Moja.java
Refactored to inline variable assignments; removed PMD.ImmutableField suppressions; consolidated timeout handling in MjSafe; adjusted suppression annotations (ImmutableField removed, UnnecessaryLocalRule added where needed).
Type Generification & Type Declarations
eo-maven-plugin/src/main/java/org/eolang/maven/DepDirs.java, Walk.java, TjForeign.java
Added PMD.LooseCoupling suppressions; changed ConcurrentHashMap to Map in function signatures; inlined PathMatcher usage; removed PMD.GodClass from TjForeign class annotation.
Objectionary & Remote Access
eo-maven-plugin/src/main/java/org/eolang/maven/OyRemote.java, Objectionary.java
Inlined URL retrieval in contains() and isDirectory() methods; removed @SuppressWarnings("PMD.AvoidCatchingGenericException") from Objectionary.Fake inner class.
Code Generation & Formatting
eo-maven-plugin/src/main/java/org/eolang/maven/PackageInfos.java, StMeasured.java
Inlined package string computation in loop; changed bytes conversion to explicit UTF-8 via StandardCharsets.UTF_8; updated PMD suppression from PrematureDeclaration to UnnecessaryLocalRule.
Cache & Hash Core Tests
eo-maven-plugin/src/test/java/org/eolang/maven/CacheTest.java, ChCachedTest.java, ChRemoteTest.java, ChTextTest.java, CommitHashesMapTest.java, ConcurrentCacheTest.java
Split and restructured cache tests; added PMD suppression annotations; introduced explicit type declarations; removed/reorganized assertions; inlined hash variable usage; added test for consistent cached values across multiple calls.
Maven Mojo Tests
eo-maven-plugin/src/test/java/org/eolang/maven/MjCleanTest.java, MjLintTest.java, MjParseTest.java, MjPlaceTest.java, MjPrintTest.java, MjProbeTest.java, MjPullTest.java, MjRegisterTest.java, MjResolveTest.java, MjTranspileTest.java, MjUnplaceTest.java, MjUnspileTest.java
Added method-level PMD suppressions (UnitTestContainsTooManyAsserts, UnnecessaryLocalRule); refactored assertions using Matchers.allOf; inlined FakeMaven instantiation; removed intermediate local variables; consolidated inline path construction; updated test methods with new assertions and suppression annotations; removed class-level TooManyMethods suppression where moved to method level.
Functional & Integration Tests
eo-maven-plugin/src/test/java/org/eolang/maven/DepDirsTest.java, FakeMaven.java, FileHashTest.java, FpAppliedWithCacheTest.java, FpIfReleasedTest.java, FpUpdateFromCacheTest.java, ObjectsIndexTest.java, OyCachedTest.java, OyRemoteTest.java, PackageInfosTest.java, ProbesTest.java, ThreadedTest.java, TjsForeignTest.java
Consolidated assertions with Matchers.allOf; inlined local variable assignments; refactored test names (e.g., contains → runsContainsWithOnlyOneCallToDecoratedObject); added new test methods for edge cases; adjusted suppression annotations; reformatted CsvSource in parameterized tests; removed unused imports.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • yegor256

Poem

🐰 A flurry of suppressions and refactors neat,
Variables inlined make the code so fleet,
PMD warnings quieted with careful grace,
Qulice pinned at last to its rightful place!
Tests reorganized, assertions consolidated—
Quality refined, no bugs awaited! 🎯

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.28% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the primary change: upgrading qulice-maven-plugin to version 0.25.1 in eo-maven-plugin, which is the main focus of the changeset.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

github-actions Bot commented Feb 25, 2026

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 174.773 192.315 17.542 10.04% ms/op Average Time

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

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 upgrades the qulice-maven-plugin to 0.25.1 for eo-maven-plugin and applies the resulting formatting/refactoring changes across production and test code to satisfy updated static-analysis rules.

Changes:

  • Bumped qulice-maven-plugin version to 0.25.1.
  • Refactored multiple classes/tests (mostly local-variable inlining, matcher consolidation, and added/adjusted @SuppressWarnings) to satisfy updated Qulice/PMD checks.
  • Minor API/annotation tweaks (e.g., @FunctionalInterface) and small logic simplifications.

Reviewed changes

Copilot reviewed 63 out of 63 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
eo-maven-plugin/pom.xml Upgrades qulice-maven-plugin to 0.25.1.
eo-maven-plugin/src/main/java/org/eolang/maven/Walk.java Minor refactor in glob matching implementation.
eo-maven-plugin/src/main/java/org/eolang/maven/TjForeign.java Simplifies equals and adjusts suppressions.
eo-maven-plugin/src/main/java/org/eolang/maven/StMeasured.java Uses explicit UTF-8 when writing measures; updates suppression.
eo-maven-plugin/src/main/java/org/eolang/maven/PackageInfos.java Inlines package name computation into logging/saving flow.
eo-maven-plugin/src/main/java/org/eolang/maven/OyRemote.java Inlines temporary variables in contains/isDirectory.
eo-maven-plugin/src/main/java/org/eolang/maven/Objectionary.java Removes a PMD suppression from Fake.
eo-maven-plugin/src/main/java/org/eolang/maven/Moja.java Expands suppressions for reflection-based field initialization.
eo-maven-plugin/src/main/java/org/eolang/maven/MjUnspile.java Removes/adjusts PMD suppressions; adds suppression on helper.
eo-maven-plugin/src/main/java/org/eolang/maven/MjUnplace.java Removes PMD suppressions; minor cleanup.
eo-maven-plugin/src/main/java/org/eolang/maven/MjTranspile.java Adds suppressions around locals flagged by PMD.
eo-maven-plugin/src/main/java/org/eolang/maven/MjSafe.java Removes a number of PMD.ImmutableField suppressions; minor timeout API cleanup.
eo-maven-plugin/src/main/java/org/eolang/maven/MjResolve.java Adds suppressions for locals; no functional change intended.
eo-maven-plugin/src/main/java/org/eolang/maven/MjRegister.java Inlines threaded registration total into log statement.
eo-maven-plugin/src/main/java/org/eolang/maven/MjPull.java Inlines OyIndexed/OyCached/OyRemote construction; adjusts suppression and local use.
eo-maven-plugin/src/main/java/org/eolang/maven/MjProbe.java Adds suppression for PMD local rule.
eo-maven-plugin/src/main/java/org/eolang/maven/MjPrint.java Inlines program local into Saved(...).
eo-maven-plugin/src/main/java/org/eolang/maven/MjParse.java Adds suppression for PMD local rule.
eo-maven-plugin/src/main/java/org/eolang/maven/MjLint.java Generalizes ConcurrentHashMap type to Map; adds suppression.
eo-maven-plugin/src/main/java/org/eolang/maven/MjCompile.java Adds suppression for PMD local rule.
eo-maven-plugin/src/main/java/org/eolang/maven/MjAssemble.java Adds suppression for PMD local rule.
eo-maven-plugin/src/main/java/org/eolang/maven/Footprint.java Marks interface as @FunctionalInterface.
eo-maven-plugin/src/main/java/org/eolang/maven/DpsWithRuntime.java Uses List<Dep> type; keeps ListOf implementation.
eo-maven-plugin/src/main/java/org/eolang/maven/DpsEachWithoutTransitive.java Refactors transitive filtering into String.join pipeline.
eo-maven-plugin/src/main/java/org/eolang/maven/DpsDefault.java Inlines head/runtime locals into return expression.
eo-maven-plugin/src/main/java/org/eolang/maven/Dependencies.java Marks interface @FunctionalInterface; minor refactor in fake dep generation.
eo-maven-plugin/src/main/java/org/eolang/maven/DepDirs.java Adds suppression for PMD local rule.
eo-maven-plugin/src/main/java/org/eolang/maven/ConcurrentCache.java Adds class suppression for synchronized block + TODO note.
eo-maven-plugin/src/main/java/org/eolang/maven/CommitHash.java Replaces redundant Javadoc with @Override marker.
eo-maven-plugin/src/main/java/org/eolang/maven/ChSource.java Inlines content local into digest loop; adds suppression.
eo-maven-plugin/src/main/java/org/eolang/maven/Catalogs.java Uses Map<Path, Tojos> type; adds suppression + TODO note.
eo-maven-plugin/src/main/java/org/eolang/maven/Cache.java Uses explicit InputStream type; minor digest return simplification.
eo-maven-plugin/src/test/java/org/eolang/maven/TjsForeignTest.java Formatting changes; refactors list creation and exception-message assertion.
eo-maven-plugin/src/test/java/org/eolang/maven/ThreadedTest.java Adds PMD suppression for exception-as-flow-control rule.
eo-maven-plugin/src/test/java/org/eolang/maven/ProbesTest.java Combines size + content assertions via allOf.
eo-maven-plugin/src/test/java/org/eolang/maven/PackageInfosTest.java Splits assertions into separate tests; improves test granularity.
eo-maven-plugin/src/test/java/org/eolang/maven/OyRemoteTest.java Inlines Objectionary creation in assertions.
eo-maven-plugin/src/test/java/org/eolang/maven/OyCachedTest.java Adds suppressions; refactors concurrent test logic.
eo-maven-plugin/src/test/java/org/eolang/maven/ObjectsIndexTest.java Splits behavior checks into separate tests; adds suppressions.
eo-maven-plugin/src/test/java/org/eolang/maven/MjUnspileTest.java Adds suppressions for PMD local rule.
eo-maven-plugin/src/test/java/org/eolang/maven/MjUnplaceTest.java Removes class-level suppression; adds per-test suppressions.
eo-maven-plugin/src/test/java/org/eolang/maven/MjTranspileTest.java Updates suppressions and inlines locals in disabled tests.
eo-maven-plugin/src/test/java/org/eolang/maven/MjResolveTest.java Inlines path building into assertion; adds suppression in one test.
eo-maven-plugin/src/test/java/org/eolang/maven/MjRegisterTest.java Inlines FakeMaven pipeline into assertions.
eo-maven-plugin/src/test/java/org/eolang/maven/MjPullTest.java Consolidates “offline mode” assertions using allOf; adds suppression to one test.
eo-maven-plugin/src/test/java/org/eolang/maven/MjProbeTest.java Removes unnecessary locals by inlining constants.
eo-maven-plugin/src/test/java/org/eolang/maven/MjPrintTest.java Inlines sources path local into configuration.
eo-maven-plugin/src/test/java/org/eolang/maven/MjPlaceTest.java Removes some intermediate assertions and locals; adds suppression.
eo-maven-plugin/src/test/java/org/eolang/maven/MjParseTest.java Adds suppressions and refactors locals; updates concurrency test loop.
eo-maven-plugin/src/test/java/org/eolang/maven/MjLintTest.java Reworks suppressions and per-test PMD settings.
eo-maven-plugin/src/test/java/org/eolang/maven/MjCleanTest.java Refactors directory creation into a single expression.
eo-maven-plugin/src/test/java/org/eolang/maven/FpUpdateFromCacheTest.java Splits behavior into two tests (side-effect vs return value).
eo-maven-plugin/src/test/java/org/eolang/maven/FpIfReleasedTest.java Inlines footprint application into assertion.
eo-maven-plugin/src/test/java/org/eolang/maven/FpAppliedWithCacheTest.java Adds suppressions for PMD local rule.
eo-maven-plugin/src/test/java/org/eolang/maven/FileHashTest.java Inlines path local into assertion.
eo-maven-plugin/src/test/java/org/eolang/maven/FakeMaven.java Removes coupling suppression; inlines locals in defaults and helpers.
eo-maven-plugin/src/test/java/org/eolang/maven/DepDirsTest.java Consolidates multiple assertions into one allOf.
eo-maven-plugin/src/test/java/org/eolang/maven/ConcurrentCacheTest.java Replaces var with explicit types; adds suppression.
eo-maven-plugin/src/test/java/org/eolang/maven/CommitHashesMapTest.java Removes redundant “has key” assertion; directly asserts fetched hash value.
eo-maven-plugin/src/test/java/org/eolang/maven/ChTextTest.java Adds suppressions for PMD local rule.
eo-maven-plugin/src/test/java/org/eolang/maven/ChRemoteTest.java Inlines hash local into assertions.
eo-maven-plugin/src/test/java/org/eolang/maven/ChCachedTest.java Splits cache invariants into separate tests; adds suppressions.
eo-maven-plugin/src/test/java/org/eolang/maven/CacheTest.java Reworks tests to reduce multiple assertions and uses explicit types.
Comments suppressed due to low confidence (4)

eo-maven-plugin/src/test/java/org/eolang/maven/OyCachedTest.java:88

  • cachesInConcurrentEnvironment only asserts that the origin was called once, but no longer asserts that all threads received the expected cached value (content). This can miss regressions where de-duplication works but the returned value is wrong. Consider asserting both invariants (value equality + single origin call), e.g., via one combined assertion.
    eo-maven-plugin/src/test/java/org/eolang/maven/ChCachedTest.java:66
  • Typo in test method name: cachesHashAndRemainsConsistenAcrossMultipleCalls should be ...Consistent... for readability/searchability.
    eo-maven-plugin/src/main/java/org/eolang/maven/Catalogs.java:28
  • Spelling in the TODO: ReentranLock should be ReentrantLock.
 * @todo #4884:30min Use ReentranLock instead of synchronized block in the code.
 *  It will be more efficient and will not cause deadlocks.

eo-maven-plugin/src/main/java/org/eolang/maven/ConcurrentCache.java:18

  • Spelling in the TODO: Dont' forget should be Don't forget.
 *  This is the suggestion from qulice.
 *  Dont' forget to remove the PMD suppression for AvoidSynchronizedStatement
 *  after that.

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

Comment on lines 194 to 198
for (int program = 0; program < total; ++program) {
MatcherAssert.assertThat(
"We have to parse concurrently, but we didn't",
res,
maven.execute(new FakeMaven.Parse()).result(),
Matchers.hasKey(

Copilot AI Feb 25, 2026

Copy link

Choose a reason for hiding this comment

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

The loop in parsesConcurrentlyWithLotsOfPrograms calls maven.execute(new FakeMaven.Parse()).result() on every iteration. That re-runs parsing up to total times (50 here), making the test much slower and potentially changing semantics (each run can mutate state/caches). Consider executing Parse once, storing the result map, and asserting on that single result inside the loop.

Copilot uses AI. Check for mistakes.
Comment on lines +36 to +41
index.contains(object);
index.contains(object);
MatcherAssert.assertThat(
message,
index.contains(object),
Matchers.is(true)
"The number of calls should be 1",
calls.get(),
Matchers.is(1)

Copilot AI Feb 25, 2026

Copy link

Choose a reason for hiding this comment

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

runsContainsWithOnlyOneCallToDecoratedObject ignores the return values of both index.contains(object) calls. The test can pass even if contains returns false; consider asserting the returned value as well (can be combined with the call-count check into a single assertion).

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: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
eo-maven-plugin/src/main/java/org/eolang/maven/DpsEachWithoutTransitive.java (1)

97-102: ⚠️ Potential issue | 🟡 Minor

Pre-existing typo: "compiled" should be "compile".

Line 101 checks for scope "compiled", which is not a valid Maven dependency scope. The valid scopes are compile, provided, runtime, test, system, and import. As written, any dependency with the standard "compile" scope fails this check, so isRuntimeRequired returns false for it — meaning compile-scoped transitive dependencies silently bypass the check in iterator() instead of being reported.

🐛 Proposed fix
-        || "compiled".equals(dep.getScope());
+        || "compile".equals(dep.getScope());
🤖 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/DpsEachWithoutTransitive.java`
around lines 97 - 102, The isRuntimeRequired method currently checks for
"compiled" which is a typo; update isRuntimeRequired (and any similar checks) to
use the correct Maven scope "compile" instead of "compiled" so compile-scoped
dependencies are treated as runtime-required; verify the logic in
isRuntimeRequired and the caller iterator() now correctly reports/handles
dependencies with scope == null, empty, "runtime" or "compile".
eo-maven-plugin/src/main/java/org/eolang/maven/DpsDefault.java (1)

168-178: ⚠️ Potential issue | 🟡 Minor

Bare .get() on line 175 yields an opaque NoSuchElementException when <part[2]> has no text.

The lambda in the outer .map() calls .text().get() on the second <part> element with no guard. A malformed XMIR where a +rt jvm meta exists but the coordinate part carries no text content will throw NoSuchElementException with no diagnostic context (no file path, no element trace).

🛡️ Proposed fix — replace bare `.get()` with `orElseThrow`
             .map(
                 meta -> meta
                     .elements(Filter.withName("part"))
                     .limit(2)
                     .reduce((first, second) -> second)
                     .get()
                     .text()
-                    .get()
+                    .orElseThrow(
+                        () -> new IllegalStateException(
+                            Logger.format("Missing text in second <part> of +rt meta in %[file]s", file)
+                        )
+                    )
             )
🤖 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/DpsDefault.java` around lines
168 - 178, Replace the bare Optional.get() on the second <part> element's text
with a guarded orElseThrow that produces a clear diagnostic: locate the
expression that calls
meta.elements(Filter.withName("part")).limit(2).reduce((first, second) ->
second).get().text().get() and change it to explicitly handle missing
element/text by using Optional.orElseThrow(...) on both the reduce/get and the
text() result, throwing an exception with a message that includes identifying
context (e.g., the meta element name/attributes or XMIR coordinate) so failures
report which meta entry is malformed instead of raising a
NoSuchElementException; use the same symbols Filter.withName("part"),
elements(...), reduce(...), text(), and orElseThrow to implement this change.
eo-maven-plugin/src/test/java/org/eolang/maven/MjParseTest.java (1)

186-208: ⚠️ Potential issue | 🟡 Minor

parsesConcurrentlyWithLotsOfPrograms now calls execute() 50 times instead of once.

Previously the result map was computed once before the assertion loop, making total key-lookups against a single execution. After the refactor, maven.execute(new FakeMaven.Parse()) is invoked on every iteration, running a full Maven parse round 50 times sequentially. This significantly increases test execution time and changes what is actually being exercised (re-parsing 50 times vs. one concurrent parse verified 50 times). Reverting to a single precomputed result preserves both performance and the original intent of testing concurrency.

🐛 Proposed fix
+        final Map<String, Path> result = maven.execute(new FakeMaven.Parse()).result();
         for (int program = 0; program < total; ++program) {
             MatcherAssert.assertThat(
                 "We have to parse concurrently, but we didn't",
-                maven.execute(new FakeMaven.Parse()).result(),
+                result,
                 Matchers.hasKey(
                     String.format(
                         "target/%s/foo/x/main%s.%s",
                         MjParse.DIR,
                         FakeMaven.suffix(program),
                         MjAssemble.XMIR
                     )
                 )
             );
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eo-maven-plugin/src/test/java/org/eolang/maven/MjParseTest.java` around lines
186 - 208, The test parsesConcurrentlyWithLotsOfPrograms currently calls
maven.execute(new FakeMaven.Parse()) inside the loop, causing 50 sequential
parse runs; change it to run maven.execute(new FakeMaven.Parse()) once before
the assertion loop, store its result (e.g., final Map<String, ?> result =
maven.execute(new FakeMaven.Parse()).result()), and then use that stored result
in the MatcherAssert.assertThat checks (still referencing
FakeMaven.suffix(program), MjParse.DIR and MjAssemble.XMIR) so you verify the
same single parse output across all iterations without re-executing the parse.
🧹 Nitpick comments (13)
eo-maven-plugin/src/main/java/org/eolang/maven/Catalogs.java (1)

62-62: Widening to Map loses the thread-safety contract at the declaration site.

this.all is initialized as a ConcurrentHashMap, so the computeIfAbsent calls at lines 77 and 89 are safe today — JVM dispatch resolves to ConcurrentHashMap.computeIfAbsent, not the non-thread-safe default on Map. However, the weakened declared type Map<Path, Tojos> no longer communicates this requirement. A future refactor replacing the initializer with a plain HashMap would silently introduce a race condition on those two call sites.

Since the TODO on line 27 already signals that this block is under active churn, keeping the stronger type avoids an easy footgun:

♻️ Suggested fix
-    private final Map<Path, Tojos> all;
+    private final ConcurrentHashMap<Path, Tojos> all;
🤖 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/Catalogs.java` at line 62, The
field declaration weakens the thread-safety contract by typing "all" as
Map<Path, Tojos> while it is initialized and used as a concurrent map
(computeIfAbsent in class Catalogs); change the declaration to a concurrent type
(e.g., ConcurrentMap<Path, Tojos> or ConcurrentHashMap<Path, Tojos>) so the
declared API matches the actual thread-safe behavior used by the computeIfAbsent
calls and prevents future regressions.
eo-maven-plugin/src/main/java/org/eolang/maven/Walk.java (1)

56-57: Consider fixing the coupling root cause instead of suppressing.

PMD.LooseCoupling is almost certainly triggered by the private constructor Walk(Path, List<Path>) accepting a concrete List<Path> rather than Collection<Path> (or Iterable<Path>). The includes/excludes methods are only the call sites of that constructor (via collect(Collectors.toList())). Suppressing at the call site masks the real issue.

Widening the private constructor's parameter to Collection<Path> eliminates the violation at its source and removes the need for both suppressions:

♻️ Proposed refactor
-    private Walk(final Path dir, final List<Path> list) {
-        super(list);
+    private Walk(final Path dir, final Collection<Path> paths) {
+        super(new java.util.ArrayList<>(paths));
         this.home = dir;
     }

Then drop both @SuppressWarnings("PMD.LooseCoupling") annotations from includes and excludes.

Also applies to: 75-76

🤖 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/Walk.java` around lines 56 -
57, The PMD.LooseCoupling suppression should be removed by widening the private
constructor parameter type: change the private constructor Walk(Path,
List<Path>) to accept a Collection<Path> (or Iterable<Path>) instead, adjust its
internal handling (e.g., copy into an internal List if ordering is required),
and update any internal references accordingly; then remove the
`@SuppressWarnings`("PMD.LooseCoupling") annotations on the includes and excludes
methods (Walk includes(...) and Walk excludes(...)) since the root cause is
fixed.
eo-maven-plugin/src/main/java/org/eolang/maven/Dependencies.java (1)

114-114: Magic literal 3 is silently coupled to the array size.

rand.nextInt(3) must stay in sync with the three-element array literal above it. Adding or removing a scope string without updating the bound produces an ArrayIndexOutOfBoundsException or silently excludes the new entry.

🛠️ Proposed fix — derive the bound from the array itself
+        final String[] scopes = {"test", "compiled", "runtime"};
         return Dependencies.Fake.dep(
             UUID.randomUUID().toString(),
             UUID.randomUUID().toString(),
             String.valueOf(rand.nextInt(Integer.MAX_VALUE)),
-                new String[]{"test", "compiled", "runtime"}[rand.nextInt(3)]
+            scopes[rand.nextInt(scopes.length)]
         );
🤖 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/Dependencies.java` at line
114, Replace the hard-coded bound by deriving it from the scopes array: in
Dependencies (the code that builds new
String[]{"test","compiled","runtime"}[rand.nextInt(3)]), extract the array into
a local variable (e.g., String[] scopes) and call rand.nextInt(scopes.length)
and index scopes with that result so the bound always matches the array size and
adding/removing scope strings won't break indexing.
eo-maven-plugin/src/main/java/org/eolang/maven/MjRegister.java (1)

93-127: Extract Threaded.total() into a local variable before passing to Logger.info().

Embedding substantial side-effectful logic — file registration, hash computation, and exception throwing — directly inside a Logger.info(...) argument list is atypical and makes the code harder to reason about, trace through a debugger, and test in isolation. A local variable keeps the registration step clearly separated from the logging step.

♻️ Proposed refactor
-        Logger.info(
-            this,
-            "Registered %d EO sources from %[file]s to %[file]s, included %s, excluded %s",
-            new Threaded<>(
-                new Walk(this.sourcesDir.toPath())
-                    .includes(this.includeSources)
-                    .excludes(this.excludeSources),
-                file -> {
-                    if (
-                        this.strictFileNames
-                            && !MjRegister.PATTERN.matcher(file.getFileName().toString()).matches()
-                    ) {
-                        throw new IllegalArgumentException(
-                            String.format(
-                                "Incorrect name found: '%s'. EO name must match '%s'",
-                                file.getFileName().toString(),
-                                MjRegister.PATTERN
-                            )
-                        );
-                    }
-                    final String name = unplace.make(file);
-                    if (this.scopedTojos().contains(name)) {
-                        Logger.debug(this, "EO source %s already registered", name);
-                    } else {
-                        this.scopedTojos()
-                            .add(name)
-                            .withSource(file.toAbsolutePath())
-                            .withHash(new ChSource(file));
-                        Logger.debug(this, "EO source %s registered", name);
-                    }
-                    return 1;
-                }
-            ).total(),
-            this.sourcesDir,
-            this.foreign,
-            this.includeSources,
-            this.excludeSources
-        );
+        final int registered = new Threaded<>(
+            new Walk(this.sourcesDir.toPath())
+                .includes(this.includeSources)
+                .excludes(this.excludeSources),
+            file -> {
+                if (
+                    this.strictFileNames
+                        && !MjRegister.PATTERN.matcher(file.getFileName().toString()).matches()
+                ) {
+                    throw new IllegalArgumentException(
+                        String.format(
+                            "Incorrect name found: '%s'. EO name must match '%s'",
+                            file.getFileName().toString(),
+                            MjRegister.PATTERN
+                        )
+                    );
+                }
+                final String name = unplace.make(file);
+                if (this.scopedTojos().contains(name)) {
+                    Logger.debug(this, "EO source %s already registered", name);
+                } else {
+                    this.scopedTojos()
+                        .add(name)
+                        .withSource(file.toAbsolutePath())
+                        .withHash(new ChSource(file));
+                    Logger.debug(this, "EO source %s registered", name);
+                }
+                return 1;
+            }
+        ).total();
+        Logger.info(
+            this,
+            "Registered %d EO sources from %[file]s to %[file]s, included %s, excluded %s",
+            registered,
+            this.sourcesDir,
+            this.foreign,
+            this.includeSources,
+            this.excludeSources
+        );
🤖 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/MjRegister.java` around lines
93 - 127, Extract the result of new Threaded<>(new
Walk(...).includes(...).excludes(...), file -> { ... }).total() into a local
variable (e.g., int total = ... .total()) before calling Logger.info so the
side-effectful registration logic inside the Threaded/Walk lambda (which uses
MjRegister.PATTERN, unplace.make(file), scopedTojos(), ChSource, etc.) is
executed and completed separately from the logging call; then pass that local
total variable to Logger.info(), keeping the registration/hash/exception logic
out of the Logger.info(...) argument list.
eo-maven-plugin/src/main/java/org/eolang/maven/MjLint.java (1)

84-84: Map type hides the thread-safety contract of counts.

The counts map is accessed concurrently via Threaded<>lintOnelintedcounts.compute(...). While the runtime instance is still a ConcurrentHashMap (so correctness is unbroken today), widening the declared type to Map<Severity, Integer> silently discards the requirement that this map must be thread-safe. A future refactor substituting any non-concurrent Map (a perfectly valid Map implementation) would cause a data race with no compile-time warning.

Consider keeping the declared type as ConcurrentHashMap<Severity, Integer> (and the private method parameters accordingly), or at minimum add a comment that a concurrent implementation is required.

♻️ Proposed change
-        final Map<Severity, Integer> counts = new ConcurrentHashMap<>();
+        final ConcurrentHashMap<Severity, Integer> counts = new ConcurrentHashMap<>();

And restore the parameter types in lintOne and linted to match:

-    private int lintOne(
-        final TjForeign tojo,
-        final Map<Severity, Integer> counts,
+    private int lintOne(
+        final TjForeign tojo,
+        final ConcurrentHashMap<Severity, Integer> counts,
-    private XML linted(
-        final String program,
-        final XML xmir,
-        final Map<Severity, Integer> counts,
+    private XML linted(
+        final String program,
+        final XML xmir,
+        final ConcurrentHashMap<Severity, Integer> counts,
🤖 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` at line 84,
Declaration of counts widens a concurrent requirement by typing it as
Map<Severity,Integer>; change the declaration to
ConcurrentHashMap<Severity,Integer> (and update any parameter types in methods
lintOne and linted that accept this map) so the thread-safety contract is
preserved, or at minimum add a clear comment that a concurrent implementation is
required; specifically update the field `counts` in class MjLint and the
signatures/usages in `lintOne` and `linted` (and any Threaded-related call
sites) to reflect `ConcurrentHashMap<Severity,Integer>`.
eo-maven-plugin/src/test/java/org/eolang/maven/FakeMaven.java (1)

349-349: Consider extracting the hardcoded EO version to a constant.

Line [349] embeds "0.25.0" directly; centralizing this in a private static final field would reduce churn across future test updates.

♻️ Proposed refactor
 final class FakeMaven {
+    /**
+     * Default EO object version for test tojos.
+     */
+    private static final String DEFAULT_EO_VERSION = "0.25.0";
@@
         this.foreignTojos()
             .add(object)
             .withScope(scope)
-            .withVersion("0.25.0")
+            .withVersion(FakeMaven.DEFAULT_EO_VERSION)
             .withSource(src);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eo-maven-plugin/src/test/java/org/eolang/maven/FakeMaven.java` at line 349,
The test hardcodes the EO version string "0.25.0" inline; add a private static
final String constant (e.g., EO_VERSION) to the FakeMaven class and replace the
inline literal in the .withVersion(...) call with that constant (and update any
other occurrences in the same class to use EO_VERSION) so future version bumps
require changing a single field.
eo-maven-plugin/src/test/java/org/eolang/maven/MjResolveTest.java (1)

232-235: Avoid brittle chained cause dereference in assertion.

At Line 235, getCause().getCause() can make this test fail for the wrong reason. Prefer extracting root cause safely, then assert the message.

🛠️ Safer pattern
-        MatcherAssert.assertThat(
-            "Expected that conflicting dependencies were found, but they were not",
-            Assertions.assertThrows(
-                IllegalStateException.class,
-                () -> maven.execute(new FakeMaven.Resolve())
-            ).getCause().getCause().getMessage(),
+        final IllegalStateException thrown = Assertions.assertThrows(
+            IllegalStateException.class,
+            () -> maven.execute(new FakeMaven.Resolve())
+        );
+        Throwable root = thrown;
+        while (root.getCause() != null) {
+            root = root.getCause();
+        }
+        MatcherAssert.assertThat(
+            "Expected that conflicting dependencies were found, but they were not",
+            root.getMessage(),
             Matchers.containsString(
                 "1 conflicting dependencies are found: {org.eolang:eo-runtime:jar:=[0.22.0, 0.22.1]}"
             )
         );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eo-maven-plugin/src/test/java/org/eolang/maven/MjResolveTest.java` around
lines 232 - 235, The assertion in MjResolveTest uses brittle chained
getCause().getCause() on the result of Assertions.assertThrows around
maven.execute(new FakeMaven.Resolve()), so change the test to capture the thrown
Throwable from Assertions.assertThrows into a variable (e.g., thrown), then
safely walk the cause chain (using a loop or utility) to find the root cause or
the first non-null cause, and assert that its getMessage() matches the expected
message; update the assertion references to use the captured variable (from
maven.execute / FakeMaven.Resolve) and ensure you compare the message from the
safely-resolved root cause rather than calling getCause().getCause() directly.
eo-maven-plugin/src/test/java/org/eolang/maven/OyCachedTest.java (1)

82-82: Assert concurrent call results, not just side effects.

At Line 82, the test drops Together.asList() output. Keeping a value assertion here prevents false positives where call count is 1 but returned values are inconsistent.

✅ Suggested strengthening
-        new Together<>(30, thread -> objectionary.get("parallel")).asList();
+        MatcherAssert.assertThat(
+            "All concurrent reads should return the same cached content",
+            new Together<>(30, thread -> objectionary.get("parallel")).asList(),
+            Matchers.everyItem(Matchers.equalTo(content))
+        );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eo-maven-plugin/src/test/java/org/eolang/maven/OyCachedTest.java` at line 82,
The test currently discards the result of new Together<>(30, thread ->
objectionary.get("parallel")).asList(), so update OyCachedTest to capture that
returned List from Together.asList() and add assertions against its contents
(e.g., size equals expected concurrency and all elements match the expected
value produced by objectionary.get("parallel")) instead of only checking side
effects; locate the Together instantiation and replace the dropped call with a
variable (List<?> results = new Together<>(30, thread ->
objectionary.get("parallel")).asList()) and add assertions that verify the list
length and that each element equals the expected returned value.
eo-maven-plugin/src/test/java/org/eolang/maven/ChCachedTest.java (1)

64-79: Simplify single-expression block lambda.

Lines 68–70 use a block body () -> { return "dummy"; } for a trivial constant return, consistent with the same nit in ObjectsIndexTest.java.

♻️ Proposed simplification
-        final ChCached cached = new ChCached(
-            () -> {
-                return "dummy";
-            }
-        );
+        final ChCached cached = new ChCached(() -> "dummy");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eo-maven-plugin/src/test/java/org/eolang/maven/ChCachedTest.java` around
lines 64 - 79, The test uses a block lambda with an explicit return in
cachesHashAndRemainsConsistentAcrossMultipleCalls; simplify the lambda passed to
the ChCached constructor to a single-expression lambda by replacing () -> {
return "dummy"; } with () -> "dummy" to make the code concise and consistent
with other tests like ObjectsIndexTest.
eo-maven-plugin/src/test/java/org/eolang/maven/ObjectsIndexTest.java (1)

46-60: Simplify single-expression block lambdas.

The lambdas on lines 53–55 and 68–70 use a block body for a trivial return expression. They can be simplified to expression lambdas.

♻️ Proposed simplification
-new ScalarOf<>(
-    () -> {
-        return Collections.singleton(object);
-    }
-)
+new ScalarOf<>(() -> Collections.singleton(object))
-new ScalarOf<>(
-    () -> {
-        return Collections.singleton("org.eolang.io.stderr");
-    }
-)
+new ScalarOf<>(() -> Collections.singleton("org.eolang.io.stderr"))

Also applies to: 62-75

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eo-maven-plugin/src/test/java/org/eolang/maven/ObjectsIndexTest.java` around
lines 46 - 60, In ObjectsIndexTest (method runsContainsSuccessfully) replace the
block lambda that returns a single expression (currently "() -> { return
Collections.singleton(object); }") with an expression lambda "() ->
Collections.singleton(object)"; do the same for the other similar block lambda
referenced around lines 62–75 so both ScalarOf<> suppliers use the concise
expression lambda form.
eo-maven-plugin/src/main/java/org/eolang/maven/MjSafe.java (1)

634-667: Auto-unboxing Integer timeout is functionally equivalent but suppresses an NPE signal.

this.timeout is a boxed Integer. The previous .longValue() call was explicit; the new code silently auto-unboxes it. Both paths NPE if timeout is null (e.g., Maven injects an explicit <timeout/> with no value). No regression is introduced since the field has a = Integer.MAX_VALUE default, but consider adding a null-guard or using int as the field type to make the NPE impossible at the declaration site.

♻️ Proposed change
-    `@Parameter`(property = "eo.timeout")
-    protected Integer timeout = Integer.MAX_VALUE;
+    `@Parameter`(property = "eo.timeout")
+    protected int timeout = Integer.MAX_VALUE;

Switching to primitive int eliminates the boxing entirely and makes the auto-widening to long in Future.get() and TimeUnit.toMillis() both explicit and NPE-safe.

🤖 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/MjSafe.java` around lines 634
- 667, The execWithTimeout method currently uses the boxed Integer field timeout
which can be null and silently auto-unboxes (e.g., in get(this.timeout,
TimeUnit.SECONDS) and TimeUnit.SECONDS.toMillis(this.timeout)), so change the
timeout field to a primitive int (or add an explicit null-guard that throws a
clear exception) to avoid NPEs: update the declaration of timeout to int (or
check this.timeout == null and throw IllegalStateException), and adjust any
places that relied on Integer methods (e.g., in execWithTimeout and any other
uses) so calls use the primitive value (auto-widening to long where needed) to
make the behavior NPE-safe; target symbols: field timeout and method
execWithTimeout.
eo-maven-plugin/src/test/java/org/eolang/maven/CacheTest.java (2)

57-62: Combined && in assertion hides which existence check fails.

When this assertion fails, the output reports expected: true but was: false with no indication of whether the cache file or the .sha256 file is the culprit. Splitting into two independent assertThat calls removes the short-circuit ambiguity and makes failures self-describing.

♻️ Proposed refactor
-        MatcherAssert.assertThat(
-            "Cache file must be created and hash file must be created",
-            Files.exists(base.resolve(tail))
-                && Files.exists(base.resolve(String.format("%s.sha256", tail))),
-            Matchers.is(true)
-        );
+        MatcherAssert.assertThat(
+            "Cache file must be created",
+            Files.exists(base.resolve(tail)),
+            Matchers.is(true)
+        );
+        MatcherAssert.assertThat(
+            "Hash file must be created",
+            Files.exists(base.resolve(String.format("%s.sha256", tail))),
+            Matchers.is(true)
+        );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eo-maven-plugin/src/test/java/org/eolang/maven/CacheTest.java` around lines
57 - 62, The combined boolean in the assertion hides which file is missing;
change the single MatcherAssert.assertThat call into two separate assertions:
one asserting Files.exists(base.resolve(tail)) with a message like "Cache file
must be created", and another asserting
Files.exists(base.resolve(String.format("%s.sha256", tail))) with a message like
"Hash file must be created" so failures pinpoint whether the cache file
(variable tail) or its .sha256 sidecar is absent (in CacheTest.java where base
and tail are used).

157-212: generatesCorrectHashForLargeFile and generatesCorrectHashForTinyFile are near-duplicates.

The two tests differ only in the source file name and content, but share all setup, assertion, and teardown logic. They are good candidates for a @ParameterizedTest to avoid the duplication.

♻️ Proposed refactor (optional)
-    `@Test`
-    void generatesCorrectHashForLargeFile(
-        `@Mktmp` final Path temp
-    ) throws IOException, NoSuchAlgorithmException {
-        ...
-    }
-
-    `@Test`
-    void generatesCorrectHashForTinyFile(
-        `@Mktmp` final Path temp
-    ) throws IOException, NoSuchAlgorithmException {
-        ...
-    }
+    `@ParameterizedTest`
+    `@MethodSource`("hashTestCases")
+    void generatesCorrectHashForContent(
+        final String content, final String filename, `@Mktmp` final Path temp
+    ) throws IOException, NoSuchAlgorithmException {
+        final Path cache = temp.resolve("cache");
+        Files.createDirectories(cache);
+        final Path source = temp.resolve(filename);
+        Files.writeString(source, content, StandardCharsets.UTF_8);
+        final Path tail = source.getFileName();
+        new Cache(cache, p -> content).apply(source, temp.resolve("out.txt"), tail);
+        MatcherAssert.assertThat(
+            "SHA-256 hash file has incorrect content",
+            Files.readString(cache.resolve(String.format("%s.sha256", tail)), StandardCharsets.UTF_8),
+            Matchers.equalTo(
+                Base64.getEncoder().encodeToString(
+                    MessageDigest.getInstance("SHA-256").digest(content.getBytes(StandardCharsets.UTF_8))
+                )
+            )
+        );
+    }
+
+    static Stream<Arguments> hashTestCases() {
+        final int lines = 100_000;
+        final StringBuilder builder = new StringBuilder(lines * 10);
+        IntStream.range(0, lines).forEach(i -> builder.append("Line ").append(i).append('\n'));
+        return Stream.of(
+            Arguments.of(builder.toString(), "large.txt"),
+            Arguments.of("x", "tiny.txt")
+        );
+    }

Note: @Mktmp injection with @ParameterizedTest needs to be verified against how MktmpResolver supports parameter injection in that context.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eo-maven-plugin/src/test/java/org/eolang/maven/CacheTest.java` around lines
157 - 212, Replace the two near-duplicate tests generatesCorrectHashForLargeFile
and generatesCorrectHashForTinyFile with a single `@ParameterizedTest` in
CacheTest that takes parameters (fileName, content) via `@CsvSource` or
`@MethodSource` and runs the shared setup/assertion logic once: create cache dir,
write content to temp file, compute tail, call new Cache(cache, p ->
content).apply(...), then assert the .sha256 file equals
Base64(SHA-256(content)). Ensure the parameterized test can still receive the
temp Path: if `@Mktmp` injection is incompatible with `@ParameterizedTest`, switch
to using JUnit's `@TempDir` or manually create a temp directory inside the test;
keep method name and references to Cache.apply, tail, and the SHA-256 assertion
so reviewers can locate the logic.
🤖 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/Catalogs.java`:
- Line 27: Fix the typo in the TODO comment inside Catalogs (change
"ReentranLock" to "ReentrantLock") so the referenced class name is correct;
update the TODO text in the Catalogs.java comment to "ReentrantLock" and, if
present elsewhere in comments, ensure consistent spelling to avoid confusion
when implementing the actual replacement of the synchronized block.

In `@eo-maven-plugin/src/main/java/org/eolang/maven/ConcurrentCache.java`:
- Around line 15-18: In the Javadoc TODO comment inside the ConcurrentCache
class update the typo "Dont'" to "Don't" so the maintenance note reads
correctly; locate the TODO block that starts with "@todo `#4884`:30min Use
ReentrantLock instead of synchronized block." and replace the malformed
apostrophe in the word Dont' with the proper contraction Don't (no other logic
changes required).

In `@eo-maven-plugin/src/main/java/org/eolang/maven/Dependencies.java`:
- Around line 20-21: Remove the misleading `@FunctionalInterface` annotation from
the Dependencies interface declaration (interface Dependencies extends
Iterable<Dep>) since the project only uses concrete implementations (e.g.,
DpsDefault, DpsWithRuntime, DpsDepgraph, Fake) and does not rely on lambda
construction; then replace the magic number usage in the random scope selection
(currently new String[]{"test","compiled","runtime"}[rand.nextInt(3)]) by
referencing the scopes array length (e.g., use a local scopes String[] and call
rand.nextInt(scopes.length)) or a named constant for the size so the index bound
stays in sync with the array.

In `@eo-maven-plugin/src/main/java/org/eolang/maven/MjLint.java`:
- Line 80: Remove the ineffective suppression annotation
`@SuppressWarnings`("PMD.UnnecessaryLocalRule") from the MjLint class (and
similarly from other files where it was added); locate the annotation near the
lint() method and delete it entirely since the rule name is invalid and the
applicable PMD rule UnnecessaryLocalBeforeReturn cannot apply to a void method
like lint(), so no suppression is needed.

In `@eo-maven-plugin/src/main/java/org/eolang/maven/MjProbe.java`:
- Line 55: The `@SuppressWarnings`("PMD.UnnecessaryLocalRule") annotation in
MjProbe.java is using a non-existent PMD rule; open the MjProbe class and locate
the annotation on the probe() method and either remove the `@SuppressWarnings`
entry entirely (preferred since probe() is void and locals are used) or change
the rule name to the correct PMD identifier
"@SuppressWarnings(\"PMD.UnnecessaryLocalBeforeReturn\")" if you truly intend to
suppress that specific check; update the annotation text accordingly so it
references a valid PMD rule or delete the annotation.

In `@eo-maven-plugin/src/main/java/org/eolang/maven/MjRegister.java`:
- Around line 93-127: The walker lambda currently returns 1 for both skipped and
newly-registered files so Threaded.total() overcounts and any
IllegalArgumentException thrown inside gets wrapped by Threaded.total(); fix by
returning 1 only when a file was newly registered and 0 when already present
(change the lambda return at the "already registered" branch to 0), and wrap the
Threaded.total() invocation call in a try/catch that catches
IllegalStateException (thrown by Threaded.total()) and, if its cause is an
instance of IllegalArgumentException, rethrow that cause to preserve the
original exception type; reference symbols: the lambda inside new
Threaded<>(...), MjRegister.PATTERN check, scopedTojos(), unplace.make(...), and
Threaded.total().
- Around line 93-127: The Threaded class currently wraps all thrown exceptions
into IllegalStateException in Threaded.total()/total() (and its try-catch around
the lambda), which hides intended exceptions like IllegalArgumentException
thrown from the MjRegister lambda; change the catch path in Threaded.total() so
it rethrows runtime exceptions (or at least IllegalArgumentException) unchanged
instead of wrapping them—e.g., in the catch block, if the caught Throwable is an
instance of RuntimeException (or specifically IllegalArgumentException) rethrow
it, otherwise wrap it in IllegalStateException—so exceptions from the lambda
(such as the IllegalArgumentException from MjRegister's file-name check)
propagate with their original type.

In `@eo-maven-plugin/src/main/java/org/eolang/maven/MjTranspile.java`:
- Line 125: The three `@SuppressWarnings` annotations using the invalid PMD rule
name "PMD.UnnecessaryLocalRule" (found in MjTranspile at the three annotated
locations) must be corrected so the suppressions actually work; replace each
occurrence of `@SuppressWarnings`("PMD.UnnecessaryLocalRule") with
`@SuppressWarnings`("PMD.UnnecessaryLocalBeforeReturn") (the valid PMD rule name)
for the annotations at the three spots (around lines with the MjTranspile
methods/blocks) to ensure PMD/Qulice will recognize and honor the suppression.

In `@eo-maven-plugin/src/main/java/org/eolang/maven/StMeasured.java`:
- Line 48: The `@SuppressWarnings`("PMD.UnnecessaryLocalRule") annotation is
invalid; update it to a correct PMD rule name (either
"PMD.UnnecessaryLocalBeforeReturn" for legacy PMD or "PMD.VariableCanBeInlined"
for newer PMD) on the `@SuppressWarnings` annotation in StMeasured (and replace
all other occurrences across the codebase), e.g., change the string literal in
the `@SuppressWarnings`(...) on the class/method where StMeasured is defined to
the chosen valid rule so PMD suppression actually takes effect.

In `@eo-maven-plugin/src/test/java/org/eolang/maven/CacheTest.java`:
- Line 32: Remove all four `@SuppressWarnings`("PMD.UnnecessaryLocalRule")
annotations from the CacheTest class (they are invalid and unnecessary) and
leave the tests unchanged otherwise; the local variables base, source, target,
counter, cache and tail are legitimately used multiple times so no suppression
is needed. Also replace the combined boolean assertion that checks two
conditions at once (the assertion covering two predicates on cache/state) with
two separate assertions/matchers so each condition is asserted independently
(e.g., assertThat(...) for the first predicate and assertThat(...) for the
second) to improve failure diagnostics.

In `@eo-maven-plugin/src/test/java/org/eolang/maven/MjCleanTest.java`:
- Around line 30-32: In MjCleanTest replace the incorrect use of
Files.createDirectories that creates a directory named "child.eo" (the variable
small) with code that ensures the "child" directory exists and then creates an
actual file "child.eo" (e.g. call Files.createDirectories(dir.resolve("child"))
then Files.createFile(dir.resolve("child").resolve("child.eo")) or equivalent);
update the variable small to refer to the created file path so the test truly
exercises cleaning a .eo file rather than a directory.

In `@eo-maven-plugin/src/test/java/org/eolang/maven/MjTranspileTest.java`:
- Line 220: The suppression annotation
`@SuppressWarnings`("PMD.UnnecessaryLocalRule") is invalid; replace it with a
recognized PMD rule such as "PMD.UnnecessaryLocalBeforeReturn" or the newer
"PMD.VariableCanBeInlined" in MjTranspileTest (and the other ~50 occurrences) so
the suppression takes effect; update the string in the `@SuppressWarnings`
annotations (e.g., in the class/method where MjTranspileTest contains that
annotation) across the codebase consistently to the chosen valid rule name.

---

Outside diff comments:
In `@eo-maven-plugin/src/main/java/org/eolang/maven/DpsDefault.java`:
- Around line 168-178: Replace the bare Optional.get() on the second <part>
element's text with a guarded orElseThrow that produces a clear diagnostic:
locate the expression that calls
meta.elements(Filter.withName("part")).limit(2).reduce((first, second) ->
second).get().text().get() and change it to explicitly handle missing
element/text by using Optional.orElseThrow(...) on both the reduce/get and the
text() result, throwing an exception with a message that includes identifying
context (e.g., the meta element name/attributes or XMIR coordinate) so failures
report which meta entry is malformed instead of raising a
NoSuchElementException; use the same symbols Filter.withName("part"),
elements(...), reduce(...), text(), and orElseThrow to implement this change.

In
`@eo-maven-plugin/src/main/java/org/eolang/maven/DpsEachWithoutTransitive.java`:
- Around line 97-102: The isRuntimeRequired method currently checks for
"compiled" which is a typo; update isRuntimeRequired (and any similar checks) to
use the correct Maven scope "compile" instead of "compiled" so compile-scoped
dependencies are treated as runtime-required; verify the logic in
isRuntimeRequired and the caller iterator() now correctly reports/handles
dependencies with scope == null, empty, "runtime" or "compile".

In `@eo-maven-plugin/src/test/java/org/eolang/maven/MjParseTest.java`:
- Around line 186-208: The test parsesConcurrentlyWithLotsOfPrograms currently
calls maven.execute(new FakeMaven.Parse()) inside the loop, causing 50
sequential parse runs; change it to run maven.execute(new FakeMaven.Parse())
once before the assertion loop, store its result (e.g., final Map<String, ?>
result = maven.execute(new FakeMaven.Parse()).result()), and then use that
stored result in the MatcherAssert.assertThat checks (still referencing
FakeMaven.suffix(program), MjParse.DIR and MjAssemble.XMIR) so you verify the
same single parse output across all iterations without re-executing the parse.

---

Nitpick comments:
In `@eo-maven-plugin/src/main/java/org/eolang/maven/Catalogs.java`:
- Line 62: The field declaration weakens the thread-safety contract by typing
"all" as Map<Path, Tojos> while it is initialized and used as a concurrent map
(computeIfAbsent in class Catalogs); change the declaration to a concurrent type
(e.g., ConcurrentMap<Path, Tojos> or ConcurrentHashMap<Path, Tojos>) so the
declared API matches the actual thread-safe behavior used by the computeIfAbsent
calls and prevents future regressions.

In `@eo-maven-plugin/src/main/java/org/eolang/maven/Dependencies.java`:
- Line 114: Replace the hard-coded bound by deriving it from the scopes array:
in Dependencies (the code that builds new
String[]{"test","compiled","runtime"}[rand.nextInt(3)]), extract the array into
a local variable (e.g., String[] scopes) and call rand.nextInt(scopes.length)
and index scopes with that result so the bound always matches the array size and
adding/removing scope strings won't break indexing.

In `@eo-maven-plugin/src/main/java/org/eolang/maven/MjLint.java`:
- Line 84: Declaration of counts widens a concurrent requirement by typing it as
Map<Severity,Integer>; change the declaration to
ConcurrentHashMap<Severity,Integer> (and update any parameter types in methods
lintOne and linted that accept this map) so the thread-safety contract is
preserved, or at minimum add a clear comment that a concurrent implementation is
required; specifically update the field `counts` in class MjLint and the
signatures/usages in `lintOne` and `linted` (and any Threaded-related call
sites) to reflect `ConcurrentHashMap<Severity,Integer>`.

In `@eo-maven-plugin/src/main/java/org/eolang/maven/MjRegister.java`:
- Around line 93-127: Extract the result of new Threaded<>(new
Walk(...).includes(...).excludes(...), file -> { ... }).total() into a local
variable (e.g., int total = ... .total()) before calling Logger.info so the
side-effectful registration logic inside the Threaded/Walk lambda (which uses
MjRegister.PATTERN, unplace.make(file), scopedTojos(), ChSource, etc.) is
executed and completed separately from the logging call; then pass that local
total variable to Logger.info(), keeping the registration/hash/exception logic
out of the Logger.info(...) argument list.

In `@eo-maven-plugin/src/main/java/org/eolang/maven/MjSafe.java`:
- Around line 634-667: The execWithTimeout method currently uses the boxed
Integer field timeout which can be null and silently auto-unboxes (e.g., in
get(this.timeout, TimeUnit.SECONDS) and
TimeUnit.SECONDS.toMillis(this.timeout)), so change the timeout field to a
primitive int (or add an explicit null-guard that throws a clear exception) to
avoid NPEs: update the declaration of timeout to int (or check this.timeout ==
null and throw IllegalStateException), and adjust any places that relied on
Integer methods (e.g., in execWithTimeout and any other uses) so calls use the
primitive value (auto-widening to long where needed) to make the behavior
NPE-safe; target symbols: field timeout and method execWithTimeout.

In `@eo-maven-plugin/src/main/java/org/eolang/maven/Walk.java`:
- Around line 56-57: The PMD.LooseCoupling suppression should be removed by
widening the private constructor parameter type: change the private constructor
Walk(Path, List<Path>) to accept a Collection<Path> (or Iterable<Path>) instead,
adjust its internal handling (e.g., copy into an internal List if ordering is
required), and update any internal references accordingly; then remove the
`@SuppressWarnings`("PMD.LooseCoupling") annotations on the includes and excludes
methods (Walk includes(...) and Walk excludes(...)) since the root cause is
fixed.

In `@eo-maven-plugin/src/test/java/org/eolang/maven/CacheTest.java`:
- Around line 57-62: The combined boolean in the assertion hides which file is
missing; change the single MatcherAssert.assertThat call into two separate
assertions: one asserting Files.exists(base.resolve(tail)) with a message like
"Cache file must be created", and another asserting
Files.exists(base.resolve(String.format("%s.sha256", tail))) with a message like
"Hash file must be created" so failures pinpoint whether the cache file
(variable tail) or its .sha256 sidecar is absent (in CacheTest.java where base
and tail are used).
- Around line 157-212: Replace the two near-duplicate tests
generatesCorrectHashForLargeFile and generatesCorrectHashForTinyFile with a
single `@ParameterizedTest` in CacheTest that takes parameters (fileName, content)
via `@CsvSource` or `@MethodSource` and runs the shared setup/assertion logic once:
create cache dir, write content to temp file, compute tail, call new
Cache(cache, p -> content).apply(...), then assert the .sha256 file equals
Base64(SHA-256(content)). Ensure the parameterized test can still receive the
temp Path: if `@Mktmp` injection is incompatible with `@ParameterizedTest`, switch
to using JUnit's `@TempDir` or manually create a temp directory inside the test;
keep method name and references to Cache.apply, tail, and the SHA-256 assertion
so reviewers can locate the logic.

In `@eo-maven-plugin/src/test/java/org/eolang/maven/ChCachedTest.java`:
- Around line 64-79: The test uses a block lambda with an explicit return in
cachesHashAndRemainsConsistentAcrossMultipleCalls; simplify the lambda passed to
the ChCached constructor to a single-expression lambda by replacing () -> {
return "dummy"; } with () -> "dummy" to make the code concise and consistent
with other tests like ObjectsIndexTest.

In `@eo-maven-plugin/src/test/java/org/eolang/maven/FakeMaven.java`:
- Line 349: The test hardcodes the EO version string "0.25.0" inline; add a
private static final String constant (e.g., EO_VERSION) to the FakeMaven class
and replace the inline literal in the .withVersion(...) call with that constant
(and update any other occurrences in the same class to use EO_VERSION) so future
version bumps require changing a single field.

In `@eo-maven-plugin/src/test/java/org/eolang/maven/MjResolveTest.java`:
- Around line 232-235: The assertion in MjResolveTest uses brittle chained
getCause().getCause() on the result of Assertions.assertThrows around
maven.execute(new FakeMaven.Resolve()), so change the test to capture the thrown
Throwable from Assertions.assertThrows into a variable (e.g., thrown), then
safely walk the cause chain (using a loop or utility) to find the root cause or
the first non-null cause, and assert that its getMessage() matches the expected
message; update the assertion references to use the captured variable (from
maven.execute / FakeMaven.Resolve) and ensure you compare the message from the
safely-resolved root cause rather than calling getCause().getCause() directly.

In `@eo-maven-plugin/src/test/java/org/eolang/maven/ObjectsIndexTest.java`:
- Around line 46-60: In ObjectsIndexTest (method runsContainsSuccessfully)
replace the block lambda that returns a single expression (currently "() -> {
return Collections.singleton(object); }") with an expression lambda "() ->
Collections.singleton(object)"; do the same for the other similar block lambda
referenced around lines 62–75 so both ScalarOf<> suppliers use the concise
expression lambda form.

In `@eo-maven-plugin/src/test/java/org/eolang/maven/OyCachedTest.java`:
- Line 82: The test currently discards the result of new Together<>(30, thread
-> objectionary.get("parallel")).asList(), so update OyCachedTest to capture
that returned List from Together.asList() and add assertions against its
contents (e.g., size equals expected concurrency and all elements match the
expected value produced by objectionary.get("parallel")) instead of only
checking side effects; locate the Together instantiation and replace the dropped
call with a variable (List<?> results = new Together<>(30, thread ->
objectionary.get("parallel")).asList()) and add assertions that verify the list
length and that each element equals the expected returned value.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8218129 and 3becdf3.

📒 Files selected for processing (63)
  • eo-maven-plugin/pom.xml
  • eo-maven-plugin/src/main/java/org/eolang/maven/Cache.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/Catalogs.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/ChSource.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/CommitHash.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/ConcurrentCache.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/DepDirs.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/Dependencies.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/DpsDefault.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/DpsEachWithoutTransitive.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/DpsWithRuntime.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/Footprint.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/MjAssemble.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/MjCompile.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/MjLint.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/MjParse.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/MjPrint.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/MjProbe.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/MjPull.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/MjRegister.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/MjResolve.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/MjSafe.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/MjTranspile.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/MjUnplace.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/MjUnspile.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/Moja.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/Objectionary.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/OyRemote.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/PackageInfos.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/StMeasured.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/TjForeign.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/Walk.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/CacheTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/ChCachedTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/ChRemoteTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/ChTextTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/CommitHashesMapTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/ConcurrentCacheTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/DepDirsTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/FakeMaven.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/FileHashTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/FpAppliedWithCacheTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/FpIfReleasedTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/FpUpdateFromCacheTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/MjCleanTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/MjLintTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/MjParseTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/MjPlaceTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/MjPrintTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/MjProbeTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/MjPullTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/MjRegisterTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/MjResolveTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/MjTranspileTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/MjUnplaceTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/MjUnspileTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/ObjectsIndexTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/OyCachedTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/OyRemoteTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/PackageInfosTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/ProbesTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/ThreadedTest.java
  • eo-maven-plugin/src/test/java/org/eolang/maven/TjsForeignTest.java
💤 Files with no reviewable changes (2)
  • eo-maven-plugin/src/main/java/org/eolang/maven/MjUnplace.java
  • eo-maven-plugin/src/main/java/org/eolang/maven/Objectionary.java

* All catalogs in one place, to avoid making multiple objects.
*
* @since 0.29
* @todo #4884:30min Use ReentranLock instead of synchronized block in the code.

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

Typo: ReentranLockReentrantLock

The TODO references the wrong class name; it's missing the second t.

📝 Proposed fix
- * `@todo` `#4884`:30min Use ReentranLock instead of synchronized block in the code.
+ * `@todo` `#4884`:30min Use ReentrantLock instead of synchronized block in the code.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
* @todo #4884:30min Use ReentranLock instead of synchronized block in the code.
* `@todo` `#4884`:30min Use ReentrantLock instead of synchronized block in the code.
🤖 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/Catalogs.java` at line 27, Fix
the typo in the TODO comment inside Catalogs (change "ReentranLock" to
"ReentrantLock") so the referenced class name is correct; update the TODO text
in the Catalogs.java comment to "ReentrantLock" and, if present elsewhere in
comments, ensure consistent spelling to avoid confusion when implementing the
actual replacement of the synchronized block.

Comment on lines +15 to +18
* @todo #4884:30min Use ReentrantLock instead of synchronized block.
* This is the suggestion from qulice.
* Dont' forget to remove the PMD suppression for AvoidSynchronizedStatement
* after that.

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

Fix typo in TODO Javadoc text.

Line 17 has a typo (Dont'). Please correct it to keep maintenance notes clear and professional.

✍️ Suggested edit
- *  Dont' forget to remove the PMD suppression for AvoidSynchronizedStatement
+ *  Don't forget to remove the PMD suppression for `AvoidSynchronizedStatement`
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
* @todo #4884:30min Use ReentrantLock instead of synchronized block.
* This is the suggestion from qulice.
* Dont' forget to remove the PMD suppression for AvoidSynchronizedStatement
* after that.
* `@todo` `#4884`:30min Use ReentrantLock instead of synchronized block.
* This is the suggestion from qulice.
* Don't forget to remove the PMD suppression for `AvoidSynchronizedStatement`
* after that.
🤖 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/ConcurrentCache.java` around
lines 15 - 18, In the Javadoc TODO comment inside the ConcurrentCache class
update the typo "Dont'" to "Don't" so the maintenance note reads correctly;
locate the TODO block that starts with "@todo `#4884`:30min Use ReentrantLock
instead of synchronized block." and replace the malformed apostrophe in the word
Dont' with the proper contraction Don't (no other logic changes required).

Comment on lines +20 to 21
@FunctionalInterface
interface Dependencies extends Iterable<Dep> {

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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let me find the exact file and read it to understand the context
cat -n eo-maven-plugin/src/main/java/org/eolang/maven/Dependencies.java

Repository: objectionary/eo

Length of output: 4656


🏁 Script executed:

# Check if Dependencies is ever instantiated via lambda or method reference
rg -n --type java 'Dependencies\s+[a-zA-Z_$][a-zA-Z0-9_$]*\s*=' eo-maven-plugin/src/main/java/org/eolang/maven/ -A 2 -B 2

Repository: objectionary/eo

Length of output: 578


🏁 Script executed:

# Also check for direct lambda instantiation patterns
rg -n --type java '\(\)\s*->\s*' eo-maven-plugin/src/main/java/org/eolang/maven/Dependencies.java -A 2 -B 2

Repository: objectionary/eo

Length of output: 41


🏁 Script executed:

# Find all classes implementing Dependencies
rg -n --type java 'implements\s+Dependencies' eo-maven-plugin/src/main/java/org/eolang/maven/

Repository: objectionary/eo

Length of output: 1189


🏁 Script executed:

# Check if there are any lambda assignments to Dependencies anywhere
rg -n --type java 'Dependencies\s+\w+\s*=\s*\(' eo-maven-plugin/src/main/java/org/eolang/maven/ -A 1

Repository: objectionary/eo

Length of output: 41


Remove @FunctionalInterface—it is misleading and unused.

@FunctionalInterface suggests clients may construct Dependencies via lambda expressions (e.g., Dependencies d = () -> list.iterator();). However, the entire codebase instantiates Dependencies exclusively through concrete class implementations (DpsDefault, DpsWithRuntime, DpsDepgraph, Fake, etc.) and never via lambda. The annotation is technically valid (one abstract method: iterator()) but actively misleading; it should be removed to avoid suggesting an unsupported pattern.

Additionally, extract the magic number at line 114:

new String[]{"test", "compiled", "runtime"}[rand.nextInt(3)]

The hardcoded 3 is decoupled from the array length. Use rand.nextInt(scopes.length) or a named constant to prevent them from falling out of sync if the scopes array is later extended.

🤖 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/Dependencies.java` around
lines 20 - 21, Remove the misleading `@FunctionalInterface` annotation from the
Dependencies interface declaration (interface Dependencies extends
Iterable<Dep>) since the project only uses concrete implementations (e.g.,
DpsDefault, DpsWithRuntime, DpsDepgraph, Fake) and does not rely on lambda
construction; then replace the magic number usage in the random scope selection
(currently new String[]{"test","compiled","runtime"}[rand.nextInt(3)]) by
referencing the scopes array length (e.g., use a local scopes String[] and call
rand.nextInt(scopes.length)) or a named constant for the size so the index bound
stays in sync with the array.

* Lint.
* @throws IOException If fails
*/
@SuppressWarnings("PMD.UnnecessaryLocalRule")

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

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the MjLint.java file around line 80 for full context
sed -n '70,120p' eo-maven-plugin/src/main/java/org/eolang/maven/MjLint.java

Repository: objectionary/eo

Length of output: 2065


🏁 Script executed:

# Search for "UnnecessaryLocalRule" in the entire codebase to check if it's a custom rule
rg "UnnecessaryLocalRule" --type java --type xml

Repository: objectionary/eo

Length of output: 6419


🏁 Script executed:

# Look for PMD configuration files
fd -e xml -e pom | xargs grep -l "PMD\|pmd\|ruleset" 2>/dev/null | head -10

Repository: objectionary/eo

Length of output: 82


🏁 Script executed:

# Check the main pom.xml for PMD configuration
head -200 eo-maven-plugin/pom.xml | grep -A 20 -B 5 "pmd\|qulice"

Repository: objectionary/eo

Length of output: 41


🏁 Script executed:

# Look for ruleset or PMD configuration files
find . -name "*.xml" | xargs grep -l "UnnecessaryLocalRule\|qulice" | head -5

Repository: objectionary/eo

Length of output: 166


🏁 Script executed:

# Check pom.xml more broadly for PMD plugin configuration
cat eo-maven-plugin/pom.xml | grep -A 30 "qulice\|pmd" | head -80

Repository: objectionary/eo

Length of output: 936


🏁 Script executed:

# Look for PMD ruleset XML files in the project
find . -name "*.xml" -type f | xargs grep -l "ruleset\|rule" 2>/dev/null | grep -E "(pmd|qulice|config)" | head -10

Repository: objectionary/eo

Length of output: 41


🏁 Script executed:

# Check if there's a custom PMD ruleset or configuration file
find . -name "*ruleset*" -o -name "*pmd*config*" 2>/dev/null

Repository: objectionary/eo

Length of output: 41


🏁 Script executed:

# Let's check the root pom.xml for any PMD or qulice rule configuration
cat pom.xml | grep -A 50 "qulice\|pmd" | head -100

Repository: objectionary/eo

Length of output: 2048


🏁 Script executed:

# Check qulice documentation or look for rule definitions in dependencies
# Let's search for any mention of how UnnecessaryLocalRule is used in context
grep -r "UnnecessaryLocalRule" --include="*.md" --include="*.txt" --include="*.html" 2>/dev/null

Repository: objectionary/eo

Length of output: 41


🏁 Script executed:

# Let's examine one of the other files that use this annotation to see if it makes sense
sed -n '1,30p' eo-parser/src/main/java/org/eolang/parser/OnDefault.java

Repository: objectionary/eo

Length of output: 806


🌐 Web query:

qulice maven plugin UnnecessaryLocalRule PMD custom rule

💡 Result:

Qulice’s Maven plugin doesn’t expose “use my PMD ruleset / add my PMD rules” configuration. With Qulice you basically have only these PMD-related options:

  1. Suppress a PMD rule in code (Qulice explicitly supports this approach):
`@SuppressWarnings`("PMD.UnnecessaryLocalBeforeReturn") // or the rule you need

(qulice.com)

  1. Exclude files/packages from PMD within Qulice (note the pmd: prefix):
<plugin>
  <groupId>com.qulice</groupId>
  <artifactId>qulice-maven-plugin</artifactId>
  <version>0.24.3</version>
  <configuration>
    <excludes>
      <exclude>pmd:.*/generated/.*</exclude>
    </excludes>
  </configuration>
</plugin>

(qulice.com)


About UnnecessaryLocalRule

In modern PMD, the rule you likely mean is UnnecessaryLocalBeforeReturn (and it’s deprecated in favor of VariableCanBeInlined), referenced like:

<rule ref="category/java/codestyle.xml/UnnecessaryLocalBeforeReturn" />

(pmd.github.io)


If you truly need a custom PMD rule/ruleset

Use maven-pmd-plugin alongside (or instead of) Qulice, and point it at your own ruleset XML:

<configuration>
  <rulesets>
    <ruleset>${project.basedir}/config/pmd/ruleset.xml</ruleset>
  </rulesets>
</configuration>

(pmd.github.io)

Citations:


@SuppressWarnings("PMD.UnnecessaryLocalRule") is not a valid PMD rule — the annotation has no effect.

The correct PMD rule for unnecessary local variables is UnnecessaryLocalBeforeReturn. Additionally, since lint() is a void method with no return statement, that rule cannot apply here regardless. This annotation appears in 40+ files across the codebase, suggesting it was added systematically but based on an incorrect rule name.

The annotation should be removed entirely, as it provides no actual suppression and contributes no value.

🐛 Proposed fix
-    `@SuppressWarnings`("PMD.UnnecessaryLocalRule")
     private void lint() throws IOException {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@SuppressWarnings("PMD.UnnecessaryLocalRule")
private void lint() throws IOException {
🤖 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` at line 80,
Remove the ineffective suppression annotation
`@SuppressWarnings`("PMD.UnnecessaryLocalRule") from the MjLint class (and
similarly from other files where it was added); locate the annotation near the
lint() method and delete it entirely since the rule name is invalid and the
applicable PMD rule UnnecessaryLocalBeforeReturn cannot apply to a void method
like lint(), so no suppression is needed.

/**
* Probe objects.
*/
@SuppressWarnings("PMD.UnnecessaryLocalRule")

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Test 1: Search for any definition or declaration of UnnecessaryLocalRule in the repo
echo "=== Test 1: Searching for UnnecessaryLocalRule definition ==="
rg -rn "UnnecessaryLocalRule" --type java --type xml -A 2 2>/dev/null || echo "(no results)"

# Test 2: Check for other `@SuppressWarnings` usages with PMD rules
echo -e "\n=== Test 2: Other `@SuppressWarnings` with PMD rules ==="
rg -rn 'SuppressWarnings.*PMD\.Unnecessary' --type java -C 1 2>/dev/null || echo "(no results)"

# Test 3: List all `@SuppressWarnings` in the file to see usage patterns
echo -e "\n=== Test 3: All `@SuppressWarnings` in MjProbe.java ==="
rg -n '@SuppressWarnings' eo-maven-plugin/src/main/java/org/eolang/maven/MjProbe.java 2>/dev/null || echo "(not found)"

Repository: objectionary/eo

Length of output: 33366


🏁 Script executed:

#!/bin/bash
# Test 4: Get the full context of the probe() method to verify locals usage
echo "=== Test 4: probe() method context (lines 55-82) ==="
sed -n '55,82p' eo-maven-plugin/src/main/java/org/eolang/maven/MjProbe.java

Repository: objectionary/eo

Length of output: 1260


🏁 Script executed:

#!/bin/bash
# Test 5: Check Qulice/Maven configuration files for custom rule definitions
echo "=== Test 5: Qulice configuration and custom rule definitions ==="
find . -name "pom.xml" -o -name "qulice.xml" -o -name "*rule*.xml" 2>/dev/null | head -20
echo -e "\n=== Checking pom.xml for Qulice dependency version ==="
rg -n "qulice-maven-plugin|qulice" pom.xml -A 2 2>/dev/null | head -30

Repository: objectionary/eo

Length of output: 585


🏁 Script executed:

#!/bin/bash
# Additional verification: Check if there's any Qulice/PMD configuration that might define this rule
echo "=== Searching for qulice or PMD rule configuration files ==="
find . -type f \( -name "*.xml" -o -name "*.properties" -o -name "qulice*" \) | xargs grep -l "UnnecessaryLocal" 2>/dev/null || echo "(no match found)"

# Check if there's a checkstyle or PMD config that mentions this rule
echo -e "\n=== Checking for Qulice/PMD config files ==="
find . -type f \( -name "checkstyle*.xml" -o -name "pmd*.xml" -o -name "*qulice*.xml" \) 2>/dev/null | head -10

Repository: objectionary/eo

Length of output: 182


🏁 Script executed:

#!/bin/bash
# Verify the exact annotation by examining the actual file
echo "=== Exact context of line 55 in MjProbe.java ==="
sed -n '54,60p' eo-maven-plugin/src/main/java/org/eolang/maven/MjProbe.java

Repository: objectionary/eo

Length of output: 432


Replace or remove the unrecognized PMD rule name PMD.UnnecessaryLocalRule.

The rule UnnecessaryLocalRule is not a recognized PMD rule. The standard PMD rule for unnecessary local variables is UnnecessaryLocalBeforeReturn (under category/java/codestyle.xml). No custom rule by the name UnnecessaryLocalRule exists in the project's Qulice configuration.

Furthermore, the probe() method does not match the pattern that would trigger UnnecessaryLocalBeforeReturn in any case: it is a void method (no return statement), and all three local variables—tojos, start, and probed—are each referenced multiple times within the method body.

Either correct the rule name to PMD.UnnecessaryLocalBeforeReturn if a specific rule check is intended, or remove the annotation entirely if no suppression is needed.

🤖 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/MjProbe.java` at line 55, The
`@SuppressWarnings`("PMD.UnnecessaryLocalRule") annotation in MjProbe.java is
using a non-existent PMD rule; open the MjProbe class and locate the annotation
on the probe() method and either remove the `@SuppressWarnings` entry entirely
(preferred since probe() is void and locals are used) or change the rule name to
the correct PMD identifier
"@SuppressWarnings(\"PMD.UnnecessaryLocalBeforeReturn\")" if you truly intend to
suppress that specific check; update the annotation text accordingly so it
references a valid PMD rule or delete the annotation.

private boolean transpileTests = true;

@Override
@SuppressWarnings("PMD.UnnecessaryLocalRule")

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 | 🟠 Major

🧩 Analysis chain

🌐 Web query:

PMD UnnecessaryLocalRule

💡 Result:

UnnecessaryLocalRule isn’t a current PMD rule name. In PMD’s Java ruleset, the relevant rule is UnnecessaryLocalBeforeReturn (reports locals that are assigned and then immediately returned), and it has been deprecated in PMD 7.17.0 in favor of VariableCanBeInlined (which also covers throw). [1]

If you’re trying to configure a ruleset, use one of these refs:

  • category/java/codestyle.xml/UnnecessaryLocalBeforeReturn (deprecated) [1]
  • category/java/codestyle.xml/VariableCanBeInlined (replacement) [2]

Rule behavior example:

int x = doSomething();
return x; // PMD suggests: return doSomething();

[1]

Sources:
[1] PMD Java Code Style rules (includes UnnecessaryLocalBeforeReturn + deprecation note) (pmd.github.io)
[2] PMD Java rules index (lists VariableCanBeInlined) (pmd.github.io)

Citations:


🌐 Web query:

qulice 0.25.1 PMD ruleset rules

💡 Result:

In Qulice 0.25.1, the PMD ruleset is defined in the qulice-pmd module at:

  • qulice-pmd/src/main/resources/com/qulice/pmd/ruleset.xml (tag 0.25.1) [1]

Qulice states that for PMD it uses “inherited rules” (i.e., PMD’s standard rules) plus its own preconfiguration/extra rules. [2]

From the ruleset.xml content, examples of Qulice-specific rules/messages include: [1]

  • Prefer Hamcrest matchers over plain JUnit Assert.assert*
  • Prefer String.isEmpty() over String.length() == 0
  • Avoid field initialization outside constructors (except when there is no constructor)
  • Avoid doing field initialization in multiple constructors; use one “main” constructor and delegate
  • Avoid doing work in constructors beyond field assignments (except this(...) / super(...))
  • Avoid direct access to static fields
  • Avoid accessing static fields/methods via this
  • Public static methods are prohibited
  • Files.createFile shouldn’t be used in tests

If you want, paste the full ruleset.xml here and I’ll extract the complete rule list (names/refs/priorities) in a clean table.


🌐 Web query:

site:github.com qulice pmd ruleset.xml 0.25.1 UnnecessaryLocalRule

💡 Result:

For Qulice 0.25.1, the PMD configuration lives in the yegor256/qulice repo under:

  • qulice-pmd/src/main/resources/com/qulice/pmd/ruleset.xml (tag 0.25.1, dated Jan 21, 2026) [1]

Raw file (easy to fetch/download):

https://raw.githubusercontent.com/yegor256/qulice/0.25.1/qulice-pmd/src/main/resources/com/qulice/pmd/ruleset.xml

[2]

Also: UnnecessaryLocalRule is not the PMD Java rule name. The PMD rule you likely mean is UnnecessaryLocalBeforeReturn (implementation class ...UnnecessaryLocalBeforeReturnRule), referenced in rulesets as category/java/codestyle.xml/UnnecessaryLocalBeforeReturn [3]. (PMD notes it’s deprecated in PMD 7.17.0 in favor of VariableCanBeInlined [3].)

Sources: [1] [2] [3]


🌐 Web query:

qulice PMD ruleset custom rules complete list

💡 Result:

Qulice’s custom PMD rules (i.e., rules not inherited from PMD itself) are defined in Qulice’s bundled PMD ruleset (qulice-pmd/src/main/resources/com/qulice/pmd/ruleset.xml). (github.com)

Complete list of Qulice custom PMD rules (as of the current master branch):

  1. UseHamcrestInsteadOfJUnitAssertions (prefer Hamcrest matchers over plain JUnit Assert.assert*)
  2. UseStringIsEmpty (prefer String.isEmpty() over length() == 0)
  3. AvoidFieldInitializationOutsideConstructor
  4. AvoidFieldInitializationInMultipleConstructors (one “main” constructor should do real initialization; others delegate)
  5. AvoidNonAssignmentInConstructor (constructors should only assign fields / call this(...) / super(...))
  6. AvoidDirectStaticAccess (don’t access static fields directly)
  7. AvoidStaticMemberAccessViaThis (don’t access static fields/methods through this)
  8. ProhibitPublicStaticMethods
  9. AvoidFilesCreateFileInTests (don’t use Files.createFile in tests) (raw.githubusercontent.com)

Citations:


Correct the invalid PMD rule name in all three @SuppressWarnings annotations.

UnnecessaryLocalRule is not a valid PMD rule name—neither in the standard PMD ruleset nor in Qulice 0.25.1's custom rules. The correct PMD rule is UnnecessaryLocalBeforeReturn (or its modern replacement VariableCanBeInlined as of PMD 7.17.0). Using the incorrect name means the @SuppressWarnings annotation silently suppresses nothing and will not prevent violations from surfacing in CI.

Update all three annotations:

  • Line 125
  • Line 165
  • Line 265

Change @SuppressWarnings("PMD.UnnecessaryLocalRule") to @SuppressWarnings("PMD.UnnecessaryLocalBeforeReturn").

🤖 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/MjTranspile.java` at line 125,
The three `@SuppressWarnings` annotations using the invalid PMD rule name
"PMD.UnnecessaryLocalRule" (found in MjTranspile at the three annotated
locations) must be corrected so the suppressions actually work; replace each
occurrence of `@SuppressWarnings`("PMD.UnnecessaryLocalRule") with
`@SuppressWarnings`("PMD.UnnecessaryLocalBeforeReturn") (the valid PMD rule name)
for the annotations at the three spots (around lines with the MjTranspile
methods/blocks) to ensure PMD/Qulice will recognize and honor the suppression.


@Override
@SuppressWarnings("PMD.PrematureDeclaration")
@SuppressWarnings("PMD.UnnecessaryLocalRule")

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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== 1) Current PMD suppressions in the repo ==="
rg -n --type=java '@SuppressWarnings\("PMD\.[^"]+"\)' | head -20

echo ""
echo "=== 2) Check for specific UnnecessaryLocal-related rules ==="
rg -n --type=java 'PMD\.UnnecessaryLocalRule|PMD\.UnnecessaryLocalBeforeReturn|PMD\.PrematureDeclaration|PMD\.UnusedLocalVariable'

echo ""
echo "=== 3) Check the specific file in question ==="
if [ -f "eo-maven-plugin/src/main/java/org/eolang/maven/StMeasured.java" ]; then
  echo "File found, showing context around line 48:"
  sed -n '40,60p' "eo-maven-plugin/src/main/java/org/eolang/maven/StMeasured.java" | cat -n
fi

echo ""
echo "=== 4) Search for PMD configuration files ==="
fd -e xml -e properties | xargs grep -l "UnnecessaryLocal\|pmd\.xml\|ruleset" 2>/dev/null | head -10

Repository: objectionary/eo

Length of output: 10227


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== 1) Search for PMD/Qulice configuration files ==="
find . -type f \( -name "pmd.xml" -o -name "ruleset.xml" -o -name ".qulice" -o -name "*.qulice" \) 2>/dev/null | head -20

echo ""
echo "=== 2) Check Maven POM files for PMD plugin configuration ==="
rg -l 'maven-pmd-plugin|qulice-maven-plugin' pom.xml 2>/dev/null || echo "No matches in top-level pom.xml"

echo ""
echo "=== 3) Find all pom.xml files and check for PMD/Qulice config ==="
fd 'pom.xml' | xargs rg -l 'maven-pmd-plugin|qulice-maven-plugin' 2>/dev/null | head -10

echo ""
echo "=== 4) Check Qulice documentation or setup files ==="
rg -i 'qulice|UnnecessaryLocal' --type=xml --type=properties | head -20

echo ""
echo "=== 5) Search in any config or properties files ==="
fd -e properties -e xml -e yml -e yaml | xargs rg 'UnnecessaryLocal' 2>/dev/null | head -10

Repository: objectionary/eo

Length of output: 453


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== 1) Check main pom.xml for Qulice config ==="
rg -A 10 -B 2 'qulice\|maven-pmd-plugin' pom.xml | head -40

echo ""
echo "=== 2) Check eo-maven-plugin/pom.xml ==="
rg -A 10 -B 2 'qulice\|maven-pmd-plugin' eo-maven-plugin/pom.xml | head -40

echo ""
echo "=== 3) Search for any Java files that mention rule definitions ==="
rg 'UnnecessaryLocal' --type=java | head -20

echo ""
echo "=== 4) Check if there's a .qulice-exclude file or similar ==="
find . -type f -name '.qulice*' -o -name '*qulice*' 2>/dev/null | grep -v '.git' | head -20

Repository: objectionary/eo

Length of output: 106


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== 1) Read main pom.xml (first 100 lines) ==="
head -100 pom.xml

echo ""
echo "=== 2) Search for 'pmd' in pom.xml ==="
grep -i 'pmd\|qulice' pom.xml | head -20

echo ""
echo "=== 3) Search for 'pmd' in eo-maven-plugin pom.xml ==="
grep -i 'pmd\|qulice' eo-maven-plugin/pom.xml | head -20

Repository: objectionary/eo

Length of output: 4090


🌐 Web query:

Qulice PMD UnnecessaryLocalRule rule naming convention

💡 Result:

In Qulice, PMD rules use PMD’s own rule names (not a special Qulice naming scheme). To suppress a PMD rule you typically write: @SuppressWarnings("PMD.<RuleName>"). [1]

For the “unnecessary local variable (before return)” check, the PMD rule name is:

  • UnnecessaryLocalBeforeReturn
    • PMD ruleset reference: category/java/codestyle.xml/UnnecessaryLocalBeforeReturn [2]

So the suppression looks like:

`@SuppressWarnings`("PMD.UnnecessaryLocalBeforeReturn")

Note: In newer PMD versions, UnnecessaryLocalBeforeReturn is deprecated in favor of VariableCanBeInlined. [2]


Replace PMD.UnnecessaryLocalRule with the correct PMD rule name.

PMD.UnnecessaryLocalRule is not a valid PMD rule. The correct rule is either:

  • PMD.UnnecessaryLocalBeforeReturn (current/legacy)
  • PMD.VariableCanBeInlined (newer PMD versions)

This invalid suppression is silently ineffective and does not suppress the intended warnings. Update line 48 and all other occurrences throughout the codebase.

🤖 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/StMeasured.java` at line 48,
The `@SuppressWarnings`("PMD.UnnecessaryLocalRule") annotation is invalid; update
it to a correct PMD rule name (either "PMD.UnnecessaryLocalBeforeReturn" for
legacy PMD or "PMD.VariableCanBeInlined" for newer PMD) on the `@SuppressWarnings`
annotation in StMeasured (and replace all other occurrences across the
codebase), e.g., change the string literal in the `@SuppressWarnings`(...) on the
class/method where StMeasured is defined to the chosen valid rule so PMD
suppression actually takes effect.

@Test
void compilesSourceAndAddsToCache(@Mktmp final Path temp) throws Exception {
final var base = temp.resolve("cache");
@SuppressWarnings("PMD.UnnecessaryLocalRule")

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

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the actual test file to see the code and annotations
cat -n eo-maven-plugin/src/test/java/org/eolang/maven/CacheTest.java | head -220

Repository: objectionary/eo

Length of output: 10043


🏁 Script executed:

# Search for PMD/qulice configuration files that might define custom rules
fd -e xml -e properties | grep -i -E "(pmd|qulice|checkstyle|ruleset)" | head -20

Repository: objectionary/eo

Length of output: 41


🏁 Script executed:

# Search for any references to UnnecessaryLocalRule in the project
rg "UnnecessaryLocalRule" --type xml --type java --type properties

Repository: objectionary/eo

Length of output: 93


🏁 Script executed:

# Also search for any custom rule definitions or imports
rg "PMD\." eo-maven-plugin/src/test/java/org/eolang/maven/CacheTest.java -A 2 -B 2

Repository: objectionary/eo

Length of output: 850


🏁 Script executed:

# Search for qulice or PMD configuration in pom.xml or related build files
rg "qulice|PMD" eo-maven-plugin/pom.xml -A 3 -B 1

Repository: objectionary/eo

Length of output: 335


🏁 Script executed:

# Search for any ruleset files
fd -e xml | xargs grep -l "ruleset\|UnnecessaryLocal" 2>/dev/null | head -10

Repository: objectionary/eo

Length of output: 41


🏁 Script executed:

# Check if there's a parent pom or qulice config
find . -name "pom.xml" -type f | head -10 | xargs grep -l "qulice" 2>/dev/null

Repository: objectionary/eo

Length of output: 166


🌐 Web query:

PMD UnnecessaryLocalRule rule name valid

💡 Result:

UnnecessaryLocalRule is not a valid PMD rule name in current PMD rule sets.

For “unnecessary local variable before returning”, the valid Java rule names are:

  • UnnecessaryLocalBeforeReturn (still exists but deprecated since PMD 7.17.0) [1]
  • Replacement: VariableCanBeInlined (added in PMD 7.17.0) — use it instead:
    <rule ref="category/java/codestyle.xml/VariableCanBeInlined" /> [1][2]

If you’re referencing UnnecessaryLocalRule in a custom ruleset, PMD won’t find it—update it to one of the rule refs above. [1]

Sources: [1] [2]


🌐 Web query:

PMD UnnecessaryLocalBeforeReturn rule documentation

💡 Result:

PMD’s UnnecessaryLocalBeforeReturn rule flags cases where a local variable is assigned only to be immediately returned, e.g.:

int x = doSomething();
return x; // prefer: return doSomething();

Status / replacement

  • Deprecated (since PMD 7.17.0); use VariableCanBeInlined instead (it also covers throw). [1][2]

Rule details

  • Category: Java → Code Style
  • Since: PMD 3.3
  • Priority: 3 (Medium)
  • Implementation class: net.sourceforge.pmd.lang.java.rule.codestyle.UnnecessaryLocalBeforeReturnRule [1]

Property

  • statementOrderMatters (default true): if false, the variable declaration and return don’t need to be consecutive; any variable used solely in a return statement may be reported. [1]

How to enable

<rule ref="category/java/codestyle.xml/UnnecessaryLocalBeforeReturn" />

[1]

Sources
[1] PMD Java Code Style rules (UnnecessaryLocalBeforeReturn section) (pmd.github.io)
[2] PMD Java rules index (mentions deprecation and replacement) (pmd.github.io)

Citations:


Remove the invalid PMD.UnnecessaryLocalRule suppressions — the rule does not exist.

PMD.UnnecessaryLocalRule is not a valid PMD rule name. The correct rule for unnecessary local variables is UnnecessaryLocalBeforeReturn (deprecated since PMD 7.17.0; replaced by VariableCanBeInlined). These four annotations silently have no effect.

Moreover, the local variables in these methods (base, source, target, counter, cache, tail) are all referenced multiple times, so even a correct suppression would be superfluous.

Remove the four @SuppressWarnings annotations at lines 32, 66, 89, and 112.

Also consolidate the boolean assertions at lines 59–60 into separate matchers for better failure diagnostics:

- MatcherAssert.assertThat(
-     "Cache file must be created and hash file must be created",
-     Files.exists(base.resolve(tail))
-         && Files.exists(base.resolve(String.format("%s.sha256", tail))),
-     Matchers.is(true)
- );
+ MatcherAssert.assertThat(
+     "Cache file must be created",
+     Files.exists(base.resolve(tail)),
+     Matchers.is(true)
+ );
+ MatcherAssert.assertThat(
+     "Hash file must be created",
+     Files.exists(base.resolve(String.format("%s.sha256", tail))),
+     Matchers.is(true)
+ );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eo-maven-plugin/src/test/java/org/eolang/maven/CacheTest.java` at line 32,
Remove all four `@SuppressWarnings`("PMD.UnnecessaryLocalRule") annotations from
the CacheTest class (they are invalid and unnecessary) and leave the tests
unchanged otherwise; the local variables base, source, target, counter, cache
and tail are legitimately used multiple times so no suppression is needed. Also
replace the combined boolean assertion that checks two conditions at once (the
assertion covering two predicates on cache/state) with two separate
assertions/matchers so each condition is asserted independently (e.g.,
assertThat(...) for the first predicate and assertThat(...) for the second) to
improve failure diagnostics.

Comment on lines +30 to +32
final Path small = Files.createDirectories(
Files.createDirectories(dir.resolve("child")).resolve("child.eo")
);

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

Files.createDirectories creates child.eo as a directory, not a file.

The outer Files.createDirectories(…) call receives the resolved path dir/child/child.eo and creates it as a directory, not a file. The variable name small and the .eo extension clearly imply the intent is to represent an EO source file. The test still passes because MjClean removes the entire target tree regardless, but the scenario exercised silently changed from "a .eo file is cleaned" to "a directory named child.eo is cleaned."

🐛 Proposed fix
-        final Path small = Files.createDirectories(
-            Files.createDirectories(dir.resolve("child")).resolve("child.eo")
-        );
+        final Path small = Files.createFile(
+            Files.createDirectories(dir.resolve("child")).resolve("child.eo")
+        );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
final Path small = Files.createDirectories(
Files.createDirectories(dir.resolve("child")).resolve("child.eo")
);
final Path small = Files.createFile(
Files.createDirectories(dir.resolve("child")).resolve("child.eo")
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eo-maven-plugin/src/test/java/org/eolang/maven/MjCleanTest.java` around lines
30 - 32, In MjCleanTest replace the incorrect use of Files.createDirectories
that creates a directory named "child.eo" (the variable small) with code that
ensures the "child" directory exists and then creates an actual file "child.eo"
(e.g. call Files.createDirectories(dir.resolve("child")) then
Files.createFile(dir.resolve("child").resolve("child.eo")) or equivalent);
update the variable small to refer to the created file path so the test truly
exercises cleaning a .eo file rather than a directory.


@Disabled
@Test
@SuppressWarnings("PMD.UnnecessaryLocalRule")

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

🧩 Analysis chain

🏁 Script executed:

# First, examine the file at line 220 with context
sed -n '210,230p' eo-maven-plugin/src/test/java/org/eolang/maven/MjTranspileTest.java

Repository: objectionary/eo

Length of output: 828


🏁 Script executed:

# Search for qulice configuration files
fd -e 'xml' -e 'properties' -e 'yml' -e 'yaml' | xargs grep -l 'qulice\|pmd' 2>/dev/null | head -20

Repository: objectionary/eo

Length of output: 156


🏁 Script executed:

# Search for other uses of UnnecessaryLocalRule or UnnecessaryLocalBeforeReturn in the codebase
rg 'UnnecessaryLocal' --type java

Repository: objectionary/eo

Length of output: 6419


🏁 Script executed:

# Check for .pmd or qulice configuration files
find . -name '.pmd*' -o -name 'qulice*' -o -name 'pmd-rules*' 2>/dev/null

Repository: objectionary/eo

Length of output: 89


🏁 Script executed:

# Examine the qulice workflow configuration
cat .github/workflows/qulice.yml

Repository: objectionary/eo

Length of output: 795


🏁 Script executed:

# Check the main pom.xml for qulice plugin configuration
head -100 pom.xml | grep -A 20 -i qulice

Repository: objectionary/eo

Length of output: 41


🏁 Script executed:

# Search for pmd ruleset or qulice configuration
find . -name 'qulice-*.xml' -o -name '.pmdrc*' -o -name 'ruleset*.xml' 2>/dev/null

Repository: objectionary/eo

Length of output: 41


🌐 Web query:

PMD UnnecessaryLocalRule official rule name documentation

💡 Result:

In PMD’s Java rules, there is no official rule named UnnecessaryLocalRule. The rule people often mean is:

  • Official rule name (ruleset ID): UnnecessaryLocalBeforeReturn (Java / Code Style category) [1]
    • Rule implementation class: net.sourceforge.pmd.lang.java.rule.codestyle.UnnecessaryLocalBeforeReturnRule [1]
    • Documentation: see the “UnnecessaryLocalBeforeReturn” section on the PMD Java Code Style rules page [1]

Note: UnnecessaryLocalBeforeReturn is deprecated since PMD 7.17.0 and PMD recommends VariableCanBeInlined instead [1].

Sources:
[1] PMD Java Code Style rules documentation (current) (pmd.github.io)

Citations:


🏁 Script executed:

# Search for qulice-maven-plugin in pom.xml files
rg 'qulice-maven-plugin' -A 10

Repository: objectionary/eo

Length of output: 3405


🏁 Script executed:

# Check if there's any custom ruleset definition
rg 'UnnecessaryLocalRule' --type xml -B 3 -A 3

Repository: objectionary/eo

Length of output: 41


🏁 Script executed:

# Look in pom.xml for any profile or plugin configuration related to PMD or qulice
rg 'qulice|pmd' pom.xml -i -B 2 -A 5 | head -60

Repository: objectionary/eo

Length of output: 499


🌐 Web query:

qulice UnnecessaryLocalRule custom PMD rule definition

💡 Result:

In Qulice, UnnecessaryLocalRule isn’t a special “Qulice-only” PMD rule name—this check corresponds to PMD’s built-in rule UnnecessaryLocalBeforeReturn (deprecated) or, in newer PMD versions, its replacement VariableCanBeInlined. PMD documents UnnecessaryLocalBeforeReturn as deprecated and says to use VariableCanBeInlined instead. [1]

Use the built-in PMD rule in your ruleset (recommended)

Create a PMD ruleset.xml (template is in PMD docs) [2] and reference the rule:

<?xml version="1.0"?>
<ruleset name="Custom Rules"
         xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 https://pmd.sourceforge.io/ruleset_2_0_0.xsd">
  <description>My custom rules</description>

  <!-- PMD 7+ replacement -->
  <rule ref="category/java/codestyle.xml/VariableCanBeInlined" />

  <!-- Legacy (deprecated) -->
  <!-- <rule ref="category/java/codestyle.xml/UnnecessaryLocalBeforeReturn" /> -->
</ruleset>

If you truly meant a custom XPath rule named UnnecessaryLocalRule

PMD supports custom XPath rules and (in PMD 7) uses XPath 3.1; the old version property was removed, and XPath rules should use net.sourceforge.pmd.lang.rule.xpath.XPathRule. [3]

Skeleton:

<rule name="UnnecessaryLocalRule"
      language="java"
      message="Unnecessary local variable; inline it"
      class="net.sourceforge.pmd.lang.rule.xpath.XPathRule">
  <priority>3</priority>
  <properties>
    <property name="xpath">
      <value><![CDATA[
        (: put your XPath here :)
      ]]></value>
    </property>
  </properties>
</rule>

If you paste an example snippet that Qulice/PMD flags (the exact pattern), I can provide a correct XPath that matches it.

Sources:
[1] PMD Java codestyle rules (UnnecessaryLocalBeforeReturn deprecated; replacement guidance) https://pmd.github.io/pmd/pmd_rules_java_codestyle.html
[2] PMD “Making rulesets” (ruleset.xml template + rule refs) https://pmd.github.io/pmd/pmd_userdocs_making_rulesets.html
[3] PMD “Writing XPath rules” / PMD 7 XPath changes + XPathRule usage https://pmd.github.io/pmd/pmd_userdocs_extending_writing_xpath_rules.html


PMD.UnnecessaryLocalRule is not a recognized PMD rule — the suppression annotation has no effect.

UnnecessaryLocalBeforeReturn is the standard PMD rule for unnecessary local variables (deprecated in PMD 7.17.0, with VariableCanBeInlined as the recommended replacement). The rule name UnnecessaryLocalRule does not exist in official PMD or Qulice.

Proposed fix
-    `@SuppressWarnings`("PMD.UnnecessaryLocalRule")
+    `@SuppressWarnings`("PMD.UnnecessaryLocalBeforeReturn")
     void recompilesIfExpired(`@Mktmp` final Path temp) throws IOException {

This pattern occurs in 50+ locations across the codebase. Consider a systematic update to use the correct rule name.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@SuppressWarnings("PMD.UnnecessaryLocalRule")
`@SuppressWarnings`("PMD.UnnecessaryLocalBeforeReturn")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@eo-maven-plugin/src/test/java/org/eolang/maven/MjTranspileTest.java` at line
220, The suppression annotation `@SuppressWarnings`("PMD.UnnecessaryLocalRule") is
invalid; replace it with a recognized PMD rule such as
"PMD.UnnecessaryLocalBeforeReturn" or the newer "PMD.VariableCanBeInlined" in
MjTranspileTest (and the other ~50 occurrences) so the suppression takes effect;
update the string in the `@SuppressWarnings` annotations (e.g., in the
class/method where MjTranspileTest contains that annotation) across the codebase
consistently to the chosen valid rule name.

@volodya-lombrozo

Copy link
Copy Markdown
Member Author

@yegor256 could you have a look, please?

@yegor256 yegor256 merged commit 9efc601 into objectionary:master Feb 25, 2026
25 of 26 checks passed
@0crat

0crat commented Mar 2, 2026

Copy link
Copy Markdown

@volodya-lombrozo Thanks for your contribution! You've earned +4 points for this submission: +16 as a basis, +16 for hits-of-code (849 * 0.05 capped at 16), -4 for exceeding 100 hits-of-code (849 >= 100), and -8 for exceeding 400 hits-of-code (849 >= 400), with the final score capped at the maximum of 24 points. Please keep them coming! Your running score is +455 - don't forget to check your Zerocracy account too.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants