Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

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

Đang mở
#1,146 3 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

  • #1147 của @timtebeek — đã đóng, không merge

Đánh giá

Độ khó
3/5
Thời gian dự kiến
1-2 ngày
Mức phù hợp với người mới
70/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Ít trao đổi
Công nghệ
java
Lĩnh vực
tooling

Hướng nghiên cứu

Bắt đầu trong OptionalNotPresentToIsEmpty.java, đặc biệt là MethodMatcher và mẫu rewrite được tham chiếu ở dòng 46 và 56. Tái hiện phép biến đổi bằng com.bazaarvoice.jolt.common.Optional, sau đó thêm một regression test cho thấy các receiver chưa được resolve hoặc không thuộc JDK vẫn giữ nguyên và java.util.Optional vẫn được rewrite chính xác.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

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.

Ngôn ngữ chính
Java
Star
159
Fork
130
Merge trung bình
6 giờ 49 phút
Pull request đã merge (30 ngày)
47

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của openrewrite/rewrite-migrate-java

Tất cả issue của openrewrite/rewrite-migrate-java

Issue tương tự

Thêm issue về Java

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.