Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

OptionalNotPresentToIsEmpty rewrites `!isPresent()` on a non-`java.util.Optional` type, producing uncompilable `isEmpty()`

Abierto
#1,146 3 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
3/5
Tiempo estimado
1-2 días
Aptitud para principiantes
70/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Tranquilo
Stack tecnológico
java
Área
tooling

Línea de trabajo

Empieza en OptionalNotPresentToIsEmpty.java, especialmente con el MethodMatcher y la plantilla de reescritura a los que se hace referencia en las líneas 46 y 56. Reproduce la transformación con com.bazaarvoice.jolt.common.Optional y, después, añade una prueba de regresión que muestre que los receptores no resueltos o que no pertenecen al JDK permanecen sin cambios y que java.util.Optional se sigue reescribiendo correctamente.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

What version of OpenRewrite are you using?

  • Gradle plugin org.openrewrite:plugin v7.35.0
  • rewrite-migrate-java v3.38.0
  • Active recipe: org.openrewrite.java.migrate.UpgradeToJava21 (which pulls in org.openrewrite.java.migrate.util.OptionalNotPresentToIsEmpty)

How are you running OpenRewrite?

Gradle plugin, applied via an init script over a large multi-module project:

rootProject {
    plugins.apply(org.openrewrite.gradle.RewritePlugin)
    dependencies {
        rewrite("org.openrewrite.recipe:rewrite-migrate-java:3.38.0")
    }
    rewrite {
        activeRecipe("org.openrewrite.java.migrate.UpgradeToJava21")
        setExportDatatables(true)
    }
}

The affected module depends on Jolt (com.bazaarvoice.jolt:jolt-core:0.1.8), which ships its own
com.bazaarvoice.jolt.common.Optional<T>. That class exposes isPresent() and get() but has no
isEmpty() method
(its full public API is empty(), of(T), get(), isPresent(), equals, toString).

The project is private, but the third-party type is public and small; the relevant snippet is below.

What is the smallest, simplest way to reproduce the problem?

The receiver is a Jolt Optional, not a JDK Optional:

import com.bazaarvoice.jolt.common.Optional; // NOT java.util.Optional

class ConvertMarkdownToHtmlRTL {
    Optional<Object> applySingle(Object input) {
        Optional<Object> baseHtmlOpt = convert(input);
        if (!baseHtmlOpt.isPresent()) {   // original, compiles fine
            return Optional.empty();
        }
        // ...
    }
}

What did you expect to see?

The code left unchanged. baseHtmlOpt is a com.bazaarvoice.jolt.common.Optional, not a
java.util.Optional, so the recipe should not apply. The recipe's MethodMatcher is in fact scoped to the
JDK type — new MethodMatcher("java.util.Optional isPresent()") with template
#{any(java.util.Optional)}.isEmpty()
(OptionalNotPresentToIsEmpty.java, lines 46 and 56 on v3.38.0) — so the intent is clearly JDK-only.

What did you see instead?

The recipe rewrote it to call isEmpty() on the Jolt type, which does not have that method, so the module
no longer compiles:

import com.bazaarvoice.jolt.common.Optional;

class ConvertMarkdownToHtmlRTL {
    Optional<Object> applySingle(Object input) {
        Optional<Object> baseHtmlOpt = convert(input);
        if (baseHtmlOpt.isEmpty()) {   // does not compile: no isEmpty() on com.bazaarvoice.jolt.common.Optional
            return Optional.empty();
        }
        // ...
    }
}

Because the JDK-scoped MethodMatcher nevertheless matched a com.bazaarvoice.jolt.common.Optional
receiver, this looks like the matcher firing when the receiver's type is unresolved/incompletely attributed
(the third-party Optional not being on the recipe's typed classpath), falling through to a name-only match
on isPresent(). A type-scoped migration recipe should not rewrite when it cannot positively confirm the
receiver is java.util.Optional — otherwise it silently emits uncompilable code with no warning or error from
the run itself.

  • Note this is a distinct failure mode from #417/#418 (a ClassCastException thrown by the sibling
    OptionalNotEmptyToIsPresent); here there is no crash — the recipe completes "successfully" and the breakage
    only surfaces at the next compileJava.

What is the full stack trace of any errors you encountered?

No stack trace — the recipe run completes without error. The failure is a downstream Java compile error:

error: cannot find symbol
        if (baseHtmlOpt.isEmpty()) {
                       ^
  symbol:   method isEmpty()
  location: variable baseHtmlOpt of type com.bazaarvoice.jolt.common.Optional<Object>

Are you interested in contributing a fix to OpenRewrite?

Open to it if you can point me at the preferred guard (e.g. requiring resolved receiver type before applying). Happy to add a recipe test that reproduces the mis-fire on a non-JDK Optional.

Lenguaje dominante
Java
Estrellas
156
Forks
129
Merge medio
3 d 8 h
PR fusionados (30 d)
14

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de openrewrite/rewrite-migrate-java

Todos los issues de openrewrite/rewrite-migrate-java

Issues similares

Más issues de Java

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.