Repository navigation
Improve code quality by enforcing more Error Prone checks - #77
Merged
Merged
Conversation
…itch The switch handles every `GrantObjectType` value. Without the `default`, `MissingCasesInEnumSwitch` fails the build when a new value is added, instead of an `UnsupportedOperationException` at runtime. Assisted-by: Claude:claude-opus-5-5
Assisted-by: Claude:claude-opus-5-5
…Interval` Assisted-by: Claude:claude-opus-5-5
Assisted-by: Claude:claude-opus-5-5
Assisted-by: Claude:claude-opus-5-5
The extension of `@EnableKubernetesMockClient` writes a static field once per class, and an instance field before each test, from a new mock server. It starts the per-test server in both cases, so each test now gets a fresh server at no extra cost. Assisted-by: Claude:claude-opus-5-5
ThoSap
force-pushed
the
promote-disabled-error-prone-checks
branch
from
October 22, 2026 16:44
fa961e7 to
d02fd10
Compare
The try blocks call only jOOQ and fabric8, which throw unchecked exceptions. If a later change adds a call with a checked exception, the compiler now forces a decision instead of a silent catch-all. Assisted-by: Claude:claude-opus-5-5
ThoSap
force-pushed
the
promote-disabled-error-prone-checks
branch
from
October 22, 2026 16:47
d02fd10 to
a0a01de
Compare
…ithoutTransaction` The other reconcilers call this step `reconcileInTransaction`. The database step runs outside a transaction, because PostgreSQL does not allow `create database` in one. Assisted-by: Claude:claude-opus-5-5
Each check reports zero findings on all three source sets. Nine of them needed the fixes of the previous commits: `UnnecessaryDefaultInEnumSwitch`, `SuppressWarningsWithoutExplanation`, `PreferJavaTimeOverload`, `MethodCanBeStatic`, `YodaCondition`, `NonFinalStaticField`, `CatchingUnchecked`, `UngroupedOverloads` and `InconsistentOverloads`. Assisted-by: Claude:claude-opus-5-5
ThoSap
force-pushed
the
promote-disabled-error-prone-checks
branch
from
October 22, 2026 17:00
a0a01de to
1f83dfe
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #66, #67, #68 and #69.
Error Prone 2.51.0 disables 151 checks by default. This PR measured every one of them on all three source sets and promotes 57 to ERROR.
148 checks now run at ERROR, up from 91.
48 checks had zero findings. The other 9 needed a small fix first, and each fix is its own commit before the promotion.
The fixes do not change the behavior of the operator.
Method
DISABLED_CHECKSset inBuiltInCheckerSuppliersof 2.51.0: 151 checks.errorprone.argsalready set 36 of them, andEmptyIfStatementis the same check asEmptyIf. That leaves 114 candidates.-XepAllDisabledChecksAsWarningsonoperatormain,operatortest andgenerated. Then every candidate with findings was read finding by finding.The fixes
default -> throw new UnsupportedOperationException(...)from the exhaustiveGrantObjectTypeswitch inGrantService.MissingCasesInEnumSwitchnow fails the build for a new enum value, instead of an exception at runtimeUnnecessaryDefaultInEnumSwitch@SuppressWarnings("unchecked")cast inHelmTestSuppressWarningsWithoutExplanationrescheduleAfter(60, TimeUnit.SECONDS)→rescheduleAfter(Duration.ofSeconds(60)), and the same for AwaitilityatMostandpollInterval. 39 places, 22 in main and 17 in test. The fabric8withTimeout(..., TimeUnit)calls stay, as they have noDurationoverloadPreferJavaTimeOverloadstatic, 1 in main and 7 in testMethodCanBeStatic"string".equals(x)→Objects.equals(x, "string")at 4 places. This is the null-safe fix of the check.Boolean.TRUEbecametruebecause ofBooleanLiteralYodaConditionKubernetesClientinKubernetesServiceTestandPasswordFingerprintServiceTestis an instance field instead ofstatic. See the review notesNonFinalStaticFieldcatch (Exception e)→catch (RuntimeException e)in the reconcilers andPostgreSQLInstanceReadinessCheck. Thetryblocks call only jOOQ and fabric8, which throw unchecked exceptions, so the same exceptions are caught as before. The 3 catches inPostgreSQLAuthenticationServicestay, because they catch checked exceptionsCatchingUncheckedreconcile(DSLContext, Database, CRStatus)ofDatabaseReconcilerbecomesreconcileWithoutTransaction. It overloaded the publicreconcile(Database, Context). The other reconcilers call this stepreconcileInTransaction, and the database step runs outside a transaction because PostgreSQL does not allowcreate databasein oneUngroupedOverloads,InconsistentOverloadsThe 57 promoted checks
The groups are the groups of
errorprone.args. Two groups are new: "Security and platform" and "Dependency injection".Javadoc (1)
UrlInSee[text](url), not in@seeImports (2)
NonCanonicalStaticMemberImportUnnecessarilyFullyQualified@io.fabric8.generator.annotation.Nullablenext to the JSpecifyNullablestays allowedNaming and source layout (5)
ClassNamedLikeTypeParameterTorE2reads like a type parameterDifferentNameButSameFoo.Barnext toBarNegativeBooleanbooleanis not namedno…ornot…, so that conditions do not read as double negationsPublicApiNamedStreamShouldReturnStreamstream()must return a streamUngroupedOverloadsBlocks, statements and switches (2)
ForEachIterableforloop instead of a manualIteratorloopUnnecessaryDefaultInEnumSwitchdefaultin an exhaustive enum switch hides a missing case fromMissingCasesInEnumSwitch.MissingDefaultexempts exhaustive enum switches, so the two agreeClass design (11)
AvoidObjectArraysImmutableList;Listworks the same hereBuilderReturnThisFieldCanBeFinal@Injectfields (alsojakarta) and fields that the code never assigns, such as@TempDirand@InjectMockFieldCanBeLocalFieldCanBeStaticInconsistentOverloadsMethodCanBeStaticstaticNonFinalStaticFieldPreferredInterfaceTypeIterableorCollectionto the interfaceListorSet, andCharSequencetoString, forfinalfields, locals and non-overridable return typesRecordComponentAccessorAnnotationConflictUnnecessarilyVisible@Inject(alsojakarta) is called by the framework only, so package-private is enoughCorrectness (13)
AssertFalse-ea, soassert falsedoes nothingCatchingUncheckedcatch (Exception e)that can only catch unchecked exceptions becomescatch (RuntimeException e). If a later change adds a call with a checked exception, the compiler forces a decision instead of a silent catch-allCheckedExceptionNotThrownthrowsforces callers to write deadcatchblocksComparisonContractViolatedIllegalArgumentExceptioninTimSortEqualsBrokenForNullequals(null)must returnfalseby contractInterruptedExceptionSwallowedInterruptionFuture.cancel(true)can break JDBC connectionsIterablePathParameterPathis itself anIterable<Path>. Relevant because of the file-based credentialsPreferJavaTimeOverloadDurationkeeps the value and the unit togetherStronglyTypeTimeDurationfield instead of alongwith a unit in its nameTimeUnitMismatchUnsafeLocaleUsageLocaleconstructors andtoStringgive wrong locale tagsUseCorrectAssertInTests-ea, a Javaassertin a test checks nothing. AssertJ always checksSecurity and platform (6)
BanClassLoaderBanSerializableReadInsecureCryptoUsageSunApisun.*APIs break in a later JDK. Also replaces the CheckstyleIllegalImportthat #66 lostSystemExitOutsideMainSystem.exitskips the shutdown hooksSystemOutDependency injection (3)
All three know the
jakarta.injectannotations that Quarkus uses.MissingRuntimeRetentionRUNTIMEretention, CDI ignores a qualifier or scope without an errorMoreThanOneQualifierQualifierWithTypeUseRedundant code and house style (14)
ConstantPatternCompileDeduplicateConstantsInitializeInlinevarandVarifierLambdaFunctionalInterfaceIntFunctioninstead ofFunction<Integer, …>in private code, without boxingParameterComment/* name= */comment must name the right parameterRedundantOverridesuperis dead codeRedundantThrowsthrows IOException, FileNotFoundExceptiondeclares one subtype too manyStringFormatWithLiteralformat()with literal arguments only is one literalSuppressWarningsWithoutExplanationdeprecation,rawtypesandunchecked, the javac categories from #67. With-Werror, such a suppression hides a build failure, so it needs a reasonThrowSpecificExceptionsthrow new RuntimeException(...). The code usesIllegalStateExceptionand similar typesTransientMisusetransienthas no effect on a static fieldUnnecessarilyUsedValueunusedvariable. Goes withCheckReturnValueandUnnamedVariableUnnecessaryOptionalGetopt.get()insideopt.map(...)YodaConditionObjects.equals(x, CONSTANT)The checks that stay off
With findings (13)
UnnecessarySemicolon@Getter/@Setter, and its fix removes the;of real fields. Reported as google/error-prone#6233Java8ApiCheckerAddNullMarkedToClass@NullMarkeddirectly on every top-level class. All 27 findings are generated jOOQ classes, which ignore the check's suppression. Not part of this PR;RequireExplicitNullMarkingalready requires explicit null markingVarWithPrimitivevar. Forbidding it for primitives is a style change with 33 editsMissingJavadocIdentifierNamePostgreSQL,SQLandCRin names. A fix would rename public classes such asCRStatusCanIgnoreReturnValueSuggester@CanIgnoreReturnValue.error_prone_annotationsis only on the test classpathVar@Varfrom the same artifact on every reassigned variableDefaultLocale"...".formatted(...)withString.format(Locale.ROOT, ...). Decided against that rewriteBooleanParameter/* name= */comments on boolean literalsThrowsUncheckedExceptionthrows DataAccessExceptioninPostgreSQLContextFactorydocuments the jOOQ failure on purposeTypeParameterNamingCRTinstead ofCRinBaseReconciler.CRis the domain termPreferPreconditionsWith zero findings, but a reason (10)
TraditionalJavadocToMarkdownRedundantNullChecknullat runtime. The check would remove valid defensive checksMockitoDoSetup@InjectSpy. For such a spy, its fixwhen(spy.m())calls the real methodCannotMockFinalClassCannotMockMethodFieldMissingNullableParameterMissingNullableReturnMissingNullableReturnsNullCollectionVoidMissingNullableNot applicable (33)
Each check needs a library or platform that the project does not have.
JUnit 4, Truth, Dagger, AutoValue, Flogger, protobuf, JSR 305 and TestParameterInjector are on no compile classpath.
Guava and Guice are only transitive test dependencies of
quarkus-junit, and no source file imports them.BinderIdentityRestoredDangerously,HardCodedSdCardPath,StaticOrDefaultInterfaceMethodAssistedInjectAndInjectOnConstructors,BindingToUnqualifiedCommonType,MutableGuiceModule,PrivateConstructorForNoninstantiableModule,RefersToDaggerCodegen,ScopeOnModuleAutoFactoryAtInject,ExtendsAutoValue,AnnotationMirrorToString,AnnotationValueToStringFloggerLogWithCause,FloggerMessageFormat,FloggerRedundantIsEnabled,FloggerRequiredModifiers,FloggerWithCause,FloggerWithoutCauseImmutableMemberCollection,ImmutableSetForContains,UnnecessarySetDefaultImmutableRefactoring,UsingJsr305CheckReturnValueImplementAssertionWithChaining,TruthContainsExactlyElementsInUsageExpectedExceptionChecker,TestExceptionChecker,UnnecessaryTestMethodPrefix,TryFailRefactoringSimplifyTestParameterStronglyTypeByteString@NoAllocationNoAllocationReview notes
@EnableKubernetesMockClientwrites a static field once per class inbeforeAll, and an instance field inbeforeEach, from a new mock server. It starts the per-test server in both cases, so the old setup started a server it never used. Each test now gets a fresh server at no extra cost. The@BeforeEach clearSecrets()methods are now redundant, but they stay to keep the change small.DefaultLocalestays off.%dinformatted(...)uses the digits of the default locale, but its fix replaces"...".formatted(...)withString.format(Locale.ROOT, ...). We decided against that rewrite.CatchingUncheckedchanges no behavior today. The compiler proves that the 12tryblocks can only throw unchecked exceptions, and the project has no@SneakyThrows. The only difference would be a checked exception that a library throws in a hidden way: it would now leave the reconciler, and the Java Operator SDK would log it and retry with backoff. The CR status would not showERROR, because the reconcilers do not implementErrorStatusHandler.MethodCanBeStatic,YodaCondition,PreferredInterfaceTypeandCatchingUncheckedwere first classified as "keep off". A second look at their sources showed that the reasons were wrong:YodaConditionoffers a null-safe fix,PreferredInterfaceTypenarrows to interfaces, not to implementation types, and the "hidden checked exception" againstCatchingUncheckeddoes not occur in this project.Test scope
./gradlew --rerun-tasks :operator:compileJava :operator:compileTestJava :generated:compileJava- BUILD SUCCESSFUL, 0 warnings, 0 findings, with 148 checks at ERROR and-Werror.[SystemOut],[StringFormatWithLiteral],[NonFinalStaticField],[PreferredInterfaceType],[MethodCanBeStatic],[YodaCondition],[CatchingUnchecked],[UngroupedOverloads],[InconsistentOverloads],[AvoidObjectArrays]and[NegativeBoolean].KubernetesServiceTestandPasswordFingerprintServiceTest: 32 tests, 0 failures, after the instance-field change.