Skip to content

Introduce UnvalidatedEnumValueOfInvocation check#2031

Open
ghokun wants to merge 41 commits into
masterfrom
ghokun/enum-value-of-superset
Open

Introduce UnvalidatedEnumValueOfInvocation check#2031
ghokun wants to merge 41 commits into
masterfrom
ghokun/enum-value-of-superset

Conversation

@ghokun

@ghokun ghokun commented Dec 12, 2025

Copy link
Copy Markdown
Member

Context

Enum.valueOf method usage may lead to runtime exceptions when the provided argument is not checked against all possible values of method owner.

Example:

class Test {
  enum A {
    ONE, TWO, THREE
  }
  
  enum B {
    ONE, TWO, THREE, FOUR
  }
  
  // Will throw a runtime exception if b is equal to FOUR.
  A convertUnsafe(B b) {
    return A.valueOf(b.name());
  }
}

Scope

Of course it is not able to capture all possible checks on b before supplying it into A.valueOf. Therefore scope will be limited to following checks.

// BUG: Raw string
A convertUnsafe(String raw) {
  return A.valueOf(raw);
}

// BUG: From invalid string constant
A convertUnsafe() {
  return A.valueOf("FOUR");
}

// BUG: Where B has values that A do not have
A convertUnsafe(B b) {
  return A.valueOf(b.name());
}

// BUG: Value from case is not present in A
A convertUnsafe(B b) {
  return switch(b) { case FOUR -> A.valueOf(b.name()); };
}

// NOT BUG: Value from case is present in A
A convertUnsafe(B b) {
  return switch(b) { case ONE -> A.valueOf(b.name()); };
}

// NOT BUG: From valid string constant
A convertSafe() {
  return A.valueOf("ONE");
}

Above checks should also consider the polymorphic variant of valueOf:

public static <T extends Enum<T>> T valueOf(Class<T> enumClass, String name)

@github-actions

github-actions Bot commented Dec 12, 2025

Copy link
Copy Markdown

Suggested commit message:

Introduce `UnvalidatedEnumValueOfInvocation` check (#2031)

@github-actions

Copy link
Copy Markdown
  • Surviving mutants in this change: 3
  • Killed mutants in this change: 7
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.EnumValueOfSuperSet 3 7

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

@rickie rickie self-requested a review December 17, 2025 11:53
@rickie rickie added this to the 0.28.0 milestone Dec 17, 2025
@github-actions

Copy link
Copy Markdown
  • Surviving mutants in this change: 2
  • Killed mutants in this change: 7
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.EnumValueOfSuperSet 2 7

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

@github-actions

Copy link
Copy Markdown
  • Surviving mutants in this change: 2
  • Killed mutants in this change: 7
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.EnumValueOfSuperSet 2 7

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

@github-actions

Copy link
Copy Markdown
  • Surviving mutants in this change: 12
  • Killed mutants in this change: 16
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.EnumValueOfUsage 12 16

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

@ghokun ghokun marked this pull request as ready for review December 22, 2025 15:45
@github-actions

Copy link
Copy Markdown
  • Surviving mutants in this change: 6
  • Killed mutants in this change: 24
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.EnumValueOfUsage 6 24

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

@github-actions

Copy link
Copy Markdown
  • Surviving mutants in this change: 12
  • Killed mutants in this change: 33
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.EnumValueOfUsage 12 33

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

@github-actions

Copy link
Copy Markdown
  • Surviving mutants in this change: 12
  • Killed mutants in this change: 33
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.EnumValueOfUsage 12 33

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

@ghokun

ghokun commented Dec 23, 2025

Copy link
Copy Markdown
Member Author

I will try to resolve mutation test reports when I have the time.

@github-actions

Copy link
Copy Markdown
  • Surviving mutants in this change: 3
  • Killed mutants in this change: 40
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.UnvalidatedEnumValueOfInvocation 3 40

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

" }",
" }",
"",
" if (d instanceof D(B deconstructedB)) {",

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

There are also potential cases which I ignored on purpose such as:

enum A {ONE};
enum B {ONE, TWO};
record D(B b) {}

D d = new D(B.ONE);
switch (d) {
  case D(B deconstructedB) when B == ONE -> ..
  case D(B deconstructedB) when B == TWO -> ..
};

I think this will complicate things even further. Should we place it inside the ignored part?

@github-actions

Copy link
Copy Markdown
  • Surviving mutants in this change: 8
  • Killed mutants in this change: 50
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.UnvalidatedEnumValueOfInvocation 8 50

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

" Enum.valueOf(null, getClass().getSimpleName());",
" List.of(\"\").stream().map(A::valueOf);",
"",
" // Colon-style fall-through: `case FOUR` reaches the invocation, but only the",

@ghokun ghokun May 19, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Placed here to be explicit about the ignored case, if you see an easy way to implement it or that it is possible, let me know.

@github-actions

Copy link
Copy Markdown
  • Surviving mutants in this change: 8
  • Killed mutants in this change: 50
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.UnvalidatedEnumValueOfInvocation 8 50

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

" // BUG: Diagnostic matches: MISSING_VALUE",
" consume(A.valueOf(B.FOUR.name()));",
" // BUG: Diagnostic matches: MISSING_VALUE",
" consume(Enum.valueOf(A.class, B.FIVE.name()));",

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

  • Switch Cases are valid
  • Types match
  • Symbols do not match
  • Value from the provided enum is a MemberSelectTree which is not present in A.class

@github-actions

Copy link
Copy Markdown
  • Surviving mutants in this change: 8
  • Killed mutants in this change: 52
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.UnvalidatedEnumValueOfInvocation 8 52

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

@sonarqubecloud

Copy link
Copy Markdown

@github-actions

Copy link
Copy Markdown
  • Surviving mutants in this change: 8
  • Killed mutants in this change: 52
class surviving killed
🧟tech.picnic.errorprone.bugpatterns.UnvalidatedEnumValueOfInvocation 8 52

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

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