Skip to content

Comments

Introduce LexicographicalPermitsListing check#2017

Open
mohamedsamehsalah wants to merge 11 commits intomasterfrom
mohamedsamehsalah/lexico-sealed-permits-listing
Open

Introduce LexicographicalPermitsListing check#2017
mohamedsamehsalah wants to merge 11 commits intomasterfrom
mohamedsamehsalah/lexico-sealed-permits-listing

Conversation

@mohamedsamehsalah
Copy link
Contributor

Suggested commit message

Introduce `LexicographicalSealedInterfacePermitsListing` check

@github-actions
Copy link

github-actions bot commented Dec 3, 2025

Suggested commit message:

Introduce `LexicographicalPermitsListing` check (#2017)

Add a new check that enforces lexicographical ordering of sealed
`permits` listings to keep these declarations consistent and reduce
merge conflicts.

@github-actions
Copy link

github-actions bot commented Dec 3, 2025

  • Surviving mutants in this change: 8
  • Killed mutants in this change: 11
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.LexicographicalSealedInterfacePermitsListing 8 11

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

1 similar comment
@github-actions
Copy link

github-actions bot commented Dec 3, 2025

  • Surviving mutants in this change: 8
  • Killed mutants in this change: 11
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.LexicographicalSealedInterfacePermitsListing 8 11

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@sonarqubecloud
Copy link

sonarqubecloud bot commented Dec 3, 2025

@github-actions
Copy link

github-actions bot commented Dec 3, 2025

  • Surviving mutants in this change: 8
  • Killed mutants in this change: 11
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.LexicographicalSealedInterfacePermitsListing 8 11

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@Stephan202 Stephan202 modified the milestones: 0.28.0, 0.29.0 Jan 9, 2026
@mohamedsamehsalah mohamedsamehsalah force-pushed the mohamedsamehsalah/lexico-sealed-permits-listing branch from 15653b8 to 942753a Compare January 20, 2026 08:32
@mohamedsamehsalah mohamedsamehsalah force-pushed the mohamedsamehsalah/lexico-sealed-permits-listing branch from 942753a to fb46fbe Compare January 20, 2026 08:33
@github-actions
Copy link

  • Surviving mutants in this change: 3
  • Killed mutants in this change: 11
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.LexicographicalSealedInterfacePermitsListing 3 11

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

1 similar comment
@github-actions
Copy link

  • Surviving mutants in this change: 3
  • Killed mutants in this change: 11
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.LexicographicalSealedInterfacePermitsListing 3 11

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@github-actions
Copy link

  • Surviving mutants in this change: 2
  • Killed mutants in this change: 12
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.LexicographicalSealedInterfacePermitsListing 2 12

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

Copy link
Contributor Author

@mohamedsamehsalah mohamedsamehsalah left a comment

Choose a reason for hiding this comment

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

@rickie, should be ready for review now 🙏


@Override
public Description matchClass(ClassTree tree, VisitorState state) {
if (!IS_INTERFACE.matches(tree, state)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@rickie I thought this would be addressed by adding the class G in the unit tests, but I am probably missing something 🤔

Copy link
Member

Choose a reason for hiding this comment

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

Fixed it in my previous commit :)

I think we can actually also support classes, right?

.replace(original, SourceCode.treeToString(replacement, state)))
.reduce(SuggestedFix.Builder::merge)
.map(SuggestedFix.Builder::build)
.orElseThrow(() -> new VerifyException("No clauses were provided"));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should this be an IllegalStateException? After all the compiler will complain if no clauses were provided.

@github-actions
Copy link

Looks good. All 13 mutations in this change were killed.

class surviving killed
🎉tech.picnic.errorprone.bugpatterns.LexicographicalSealedInterfacePermitsListing 0 13

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@rickie rickie force-pushed the mohamedsamehsalah/lexico-sealed-permits-listing branch from 37489d8 to b4c75c3 Compare January 31, 2026 08:56
@github-actions
Copy link

Looks good. All 13 mutations in this change were killed.

class surviving killed
🎉tech.picnic.errorprone.bugpatterns.LexicographicalSealedInterfacePermitsListing 0 13

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@coderabbitai
Copy link

coderabbitai bot commented Jan 31, 2026

📝 Walkthrough

Walkthrough

Introduces a new BugChecker that validates lexicographic ordering of permitted non-sealed interfaces in Java interface declarations. When permits are unordered, the checker reports a diagnostic and provides an automated fix to reorder them lexicographically. Includes comprehensive test coverage for diagnostic detection and source refactoring scenarios.

Changes

Cohort / File(s) Summary
Bug Checker Implementation
error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/LexicographicalSealedInterfacePermitsListing.java
New BugChecker extending ClassTreeMatcher that enforces lexicographic ordering of permits in sealed interfaces. Includes matchClass method for validation, sort method for ordering permits by their string representation, and fixOrdering method to generate multi-part SuggestedFix.
Test Suite
error-prone-contrib/src/test/java/tech/picnic/errorprone/bugpatterns/LexicographicalSealedInterfacePermitsListingTest.java
Comprehensive test class with three test methods: identificationIgnoresClasses (verifies inner class exclusion), identification (validates diagnostic detection with mixed interface/class permits), and replacement (verifies source refactoring correctly reorders permits).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ⚠️ Warning The PR title 'Introduce LexicographicalPermitsListing check' is partially related to the changeset but uses an incomplete class name. The actual implementation is named LexicographicalSealedInterfacePermitsListing, making the title misleading and imprecise. Update the PR title to 'Introduce LexicographicalSealedInterfacePermitsListing check' to accurately reflect the actual class name and feature being introduced.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description provides the commit message which relates to the changeset by naming the new check being introduced, though it lacks implementation details.

✏️ 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
  • Commit unit tests in branch mohamedsamehsalah/lexico-sealed-permits-listing

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

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/LexicographicalSealedInterfacePermitsListing.java`:
- Around line 26-31: Update the class Javadoc for
LexicographicalSealedInterfacePermitsListing to accurately describe its scope:
state that the checker verifies the lexicographic ordering of the entire sealed
interface "permits" list (not only "permitted non-sealed interfaces") and that
it flags out-of-order entries in that full permits list; keep the existing
explanation about why sorted ordering helps conflict resolution but change the
wording to reference the entire permits list rather than only non-sealed
permitted interfaces.

Comment on lines 26 to 31
/**
* A {@link BugChecker} that flags permitted non-sealed interfaces that are not lexicographically
* sorted.
*
* <p>The idea behind this checker is that maintaining a sorted sequence simplifies conflict
* resolution, and can even avoid it if two branches add the same annotation.
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Clarify the checker’s scope in Javadoc.

The implementation sorts the entire permits list for sealed interfaces, not just “permitted non-sealed interfaces.” Tighten the wording to avoid misleading readers.

✏️ Suggested wording update
- * A {`@link` BugChecker} that flags permitted non-sealed interfaces that are not lexicographically
- * sorted.
+ * A {`@link` BugChecker} that flags sealed interfaces whose {`@code` permits} clause is not
+ * lexicographically sorted.
🤖 Prompt for AI Agents
In
`@error-prone-contrib/src/main/java/tech/picnic/errorprone/bugpatterns/LexicographicalSealedInterfacePermitsListing.java`
around lines 26 - 31, Update the class Javadoc for
LexicographicalSealedInterfacePermitsListing to accurately describe its scope:
state that the checker verifies the lexicographic ordering of the entire sealed
interface "permits" list (not only "permitted non-sealed interfaces") and that
it flags out-of-order entries in that full permits list; keep the existing
explanation about why sorted ordering helps conflict resolution but change the
wording to reference the entire permits list rather than only non-sealed
permitted interfaces.

Copy link
Member

@rickie rickie left a comment

Choose a reason for hiding this comment

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

Didn't write all comments on the PR as I was on the ✈️ , but I think it's in a good shape!

I added support for also sorting classes. Now we need to rename the check 🤔.


@Override
public Description matchClass(ClassTree tree, VisitorState state) {
if (!IS_INTERFACE.matches(tree, state)) {
Copy link
Member

Choose a reason for hiding this comment

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

Fixed it in my previous commit :)

I think we can actually also support classes, right?

final class LexicographicalSealedInterfacePermitsListingTest {
@Test
void identificationIgnoresClasses() {
CompilationTestHelper.newInstance(
Copy link
Member

Choose a reason for hiding this comment

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

I'll add this to the identification test :)

CompilationTestHelper.newInstance(
LexicographicalSealedInterfacePermitsListing.class, getClass())
.addSourceLines(
"pkg/A.java",
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
"pkg/A.java",
"A.java",

We can omit this :)

* sorted.
*
* <p>The idea behind this checker is that maintaining a sorted sequence simplifies conflict
* resolution, and can even avoid it if two branches add the same annotation.
Copy link
Member

Choose a reason for hiding this comment

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

Let's drop the last (copied 😉) sentence.

@github-actions
Copy link

github-actions bot commented Feb 1, 2026

Looks good. All 10 mutations in this change were killed.

class surviving killed
🎉tech.picnic.errorprone.bugpatterns.LexicographicalSealedInterfacePermitsListing 0 10

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@rickie rickie changed the title Introduce LexicographicalSealedInterfacePermitsListing check Introduce LexicographicalSealedPermitsListing check Feb 9, 2026
@github-actions
Copy link

github-actions bot commented Feb 9, 2026

Looks good. All 10 mutations in this change were killed.

class surviving killed
🎉tech.picnic.errorprone.bugpatterns.LexicographicalSealedPermitsListing 0 10

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

Copy link
Member

@rickie rickie left a comment

Choose a reason for hiding this comment

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

Nice one @mohamedsamehsalah 🚀

Thanks for the beautiful PR :)

@Stephan202 Stephan202 force-pushed the mohamedsamehsalah/lexico-sealed-permits-listing branch from a44fe61 to d2d621d Compare February 22, 2026 20:26
Copy link
Member

@Stephan202 Stephan202 left a comment

Choose a reason for hiding this comment

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

I rebased and added a commit. Suggested commit message:

Introduce `LexicographicalPermitsListing` check (#2017)

As this new check is structurally similar to
`LexicographicalAnnotationListing`, the shared logic is factored out and 
exposed through `SourceCode#sortTrees`.

While there, optimize both checks for the common case in which no 
violation is found, by reducing the number of allocations for compliant
code.


@Override
public Description matchClass(ClassTree tree, VisitorState state) {
List<? extends Tree> originalOrderPermitClauses = tree.getPermitsClause();
Copy link
Member

Choose a reason for hiding this comment

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

We can make this a bit shorter:

Suggested change
List<? extends Tree> originalOrderPermitClauses = tree.getPermitsClause();
List<? extends Tree> originalClauses = tree.getPermitsClause();

@Override
public Description matchClass(ClassTree tree, VisitorState state) {
List<? extends Tree> originalOrderPermitClauses = tree.getPermitsClause();
ImmutableList<? extends Tree> sortedPermitClauses = sort(originalOrderPermitClauses, state);
Copy link
Member

Choose a reason for hiding this comment

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

Like in LexicographicalAnnotationListing we can skip the check if there are fewer than two clauses.

Edit actually, perhaps we should use Comparators.isInOrder. Quite some logic is duplicated now, so let's factor this out in a helper method.

Comment on lines 25 to 27
/**
* A {@link BugChecker} that flags permitted non-sealed interfaces and classes that are not
* lexicographically sorted.
Copy link
Member

Choose a reason for hiding this comment

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

The permitted clause lists permitted direct subtypes; whether those are non-sealed interfaces has nothing to do with it.

linkType = CUSTOM,
severity = SUGGESTION,
tags = STYLE)
public final class LexicographicalSealedPermitsListing extends BugChecker
Copy link
Member

Choose a reason for hiding this comment

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

Let's just call it LexicographicalPermitsListing.

" class G {}",
"}")
.addSourceLines(
"Foo.java",
Copy link
Member

Choose a reason for hiding this comment

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

We generally use one top-level class.

"import pkg.A.C;",
"import pkg.A.D;",
"",
"// BUG: Diagnostic contains:",
Copy link
Member

Choose a reason for hiding this comment

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

Generally we list the non-violating cases first.

Comment on lines 14 to 18
"package pkg;",
"",
"import pkg.A.B;",
"import pkg.A.C;",
"import pkg.A.D;",
Copy link
Member

Choose a reason for hiding this comment

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

We can omit this package statement and the imports.

@Stephan202 Stephan202 changed the title Introduce LexicographicalSealedPermitsListing check Introduce LexicographicalPermitsListing check Feb 22, 2026
@sonarqubecloud
Copy link

@github-actions
Copy link

  • Surviving mutants in this change: 2
  • Killed mutants in this change: 15
class surviving killed
🧟tech.picnic.errorprone.utils.SourceCode 1 5
🧟tech.picnic.errorprone.bugpatterns.LexicographicalAnnotationListing 1 6
🎉tech.picnic.errorprone.bugpatterns.LexicographicalPermitsListing 0 4

Mutation testing report by Pitest. Review any surviving mutants by inspecting the line comments under Files changed.

@Stephan202
Copy link
Member

The two reported mutants are unkillable: both branches exist for performance reasons only.

Comment on lines +40 to +49
public Description matchClass(ClassTree tree, VisitorState state) {
SuggestedFix fix =
SourceCode.sortTrees(
tree.getPermitsClause(),
comparing(annotation -> SourceCode.treeToString(annotation, state)),
state);
return fix.isEmpty()
? Description.NO_MATCH
: describeMatch(tree.getPermitsClause().getFirst(), fix);
}
Copy link
Member

Choose a reason for hiding this comment

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

ClassTrees are way less common than ModifierTrees, so I omitted the if (tree.getPermitsClause().size() < 2) case here.

@mohamedsamehsalah
Copy link
Contributor Author

Thanks for review and suggestions @Stephan202 🙏

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

Development

Successfully merging this pull request may close these issues.

3 participants