Skip to main content

Moderne Recipes

This doc includes every recipe that is exclusive to users of Moderne. For a full list of all recipes, check out our recipe catalog. For more information about how to use Moderne for automating code refactoring and analysis at scale, contact us.

io.moderne.recipe

recipes-kotlin

  • org.openrewrite.kotlin.android.Android$KtRecipe
    • Find Android smells
    • Search-only Android recipe family covering the deprecated-API surface (Activity / Fragment / Handler / kotlinx.android.synthetic / parcel / Vibrator / registerReceiver), storage-layer footguns (SharedPreferences.commit, Room @Query shape, ContentResolver.query), lifecycle smells (LiveData.observe(this, ...), public MutableLiveData), permissions/security (requestPermissions, MODE_WORLD_*), Android-specific performance (findViewById in onDraw, raw BitmapFactory, Handler.postDelayed, runOnUiThread), WebView smells (loadUrl, setJavaScriptEnabled), logging smells (Log.*, System.out), and modernization candidates (manual Parcelable/Serializable, RxJava, raw Dagger, manual ViewModelProvider, ObjectAnimator, Runtime.exec).
  • org.openrewrite.kotlin.android.FindAlertDialogBuilderConstructor$KtRecipe
    • Find AlertDialog.Builder(this) constructions
    • android.app.AlertDialog.Builder does not pick up app-bar themes or Material styles. For consistent theming on AppCompat surfaces, use androidx.appcompat.app.AlertDialog.Builder (or MaterialAlertDialogBuilder for Material apps). Each match is worth a quick accessibility/theming review.
  • org.openrewrite.kotlin.android.FindAndroidLifecycleSmells$KtRecipe
    • Find Android lifecycle / LiveData smells
    • Lifecycle and observable-state patterns that leak across configuration changes or expose internal mutability: LiveData.observe(this, ...) inside fragments, MutableLiveData.postValue from main-thread contexts, public MutableLiveData properties, and raw MutableLiveData allocations.
  • org.openrewrite.kotlin.android.FindAndroidLogUsage$KtRecipe
    • Find android.util.Log.v/d/i/w/e(...) calls
    • android.util.Log is a flat global logger with no routing, no structured fields, and no off-by-default for release builds. Migrate to Timber (per-tree fan-out, automatic tag detection) or AndroidX androidx.tracing for performance-trace integration. Each match is a candidate for the migration.
  • org.openrewrite.kotlin.android.FindAndroidLoggingSmells$KtRecipe
    • Find Android logging smells
    • Logging shapes that don't compose with structured logging: android.util.Log.v/d/i/w/e calls (flat global logger; prefer Timber or androidx.tracing) and System.out.println on Android (untagged logcat output).
  • org.openrewrite.kotlin.android.FindAndroidModernizationCandidates$KtRecipe
    • Find Android modernization candidates
    • Patterns that work but predate the modern Android toolchain: manual Parcelable (@Parcelize), java.io.Serializable on Android transports, RxJava imports (coroutines + Flow), Dagger*Component.builder() (Hilt), direct ViewModelProvider construction (by viewModels()), ObjectAnimator (Compose animation APIs), and Runtime.exec (audit for app-sandbox fit).
  • org.openrewrite.kotlin.android.FindAndroidPerformanceSmells$KtRecipe
    • Find Android-specific performance smells
    • Patterns that drop UI-thread frames or allocate at draw time: findViewById inside onDraw/onMeasure/onLayout, unbounded BitmapFactory.decode* (no inSampleSize), hand-rolled Thread.start() from UI components, Handler.postDelayed (no lifecycle), and pre-coroutine runOnUiThread / View.post dispatch.
  • org.openrewrite.kotlin.android.FindAndroidPermissionsSmells$KtRecipe
    • Find Android permissions / security smells
    • Permission-handling patterns the platform has deprecated: direct requestPermissions(...) calls and onRequestPermissionsResult overrides (use ActivityResultContracts.RequestPermission), and MODE_WORLD_READABLE / WORLD_WRITEABLE (use FileProvider).
  • org.openrewrite.kotlin.android.FindAndroidStorageSmells$KtRecipe
    • Find Android storage / data-layer smells
    • Storage-tier patterns that block the main thread or sit on deprecated APIs: SharedPreferences.Editor.commit(), bare prefs.edit() calls, Room @Query methods returning synchronous results, ContentResolver.query, and Realm usage.
  • org.openrewrite.kotlin.android.FindAndroidViewModelInjection$KtRecipe
    • Find ViewModelProvider(...) direct constructions
    • ViewModelProvider(this).get(MyViewModel::class.java) predates the by viewModels() / by activityViewModels() Kotlin property delegates. With Hilt, those delegates pick up the @HiltViewModel annotation — no factory plumbing needed at the call site.
  • org.openrewrite.kotlin.android.FindAndroidWebViewSmells$KtRecipe
    • Find Android WebView smells
    • WebView call sites that need a security review: WebView.loadUrl(...) (URL trust boundary) and WebSettings.setJavaScriptEnabled(true) (script-execution trust boundary). For untrusted content, Chrome Custom Tabs (CustomTabsIntent) is the safer alternative.
  • org.openrewrite.kotlin.android.FindAsyncTask$KtRecipe
    • Find AsyncTask instantiations
    • AsyncTask was deprecated in API 30. The Kotlin replacement is viewModelScope.launch \{ withContext(Dispatchers.IO) \{ … \} \} or another coroutine-aware framework. Each match is a candidate for migration.
  • org.openrewrite.kotlin.android.FindAsyncTaskExecute$KtRecipe
    • Find AsyncTask.execute / executeOnExecutor calls
    • Beyond AsyncTask instantiation, the execute/executeOnExecutor call sites are the place the background work actually starts. Migration to viewModelScope.launch \{ withContext(Dispatchers.IO) \{ … \} \} happens at the call site, not the declaration — flag both.
  • org.openrewrite.kotlin.android.FindBareHandlerConstructor$KtRecipe
    • Find Handler() constructor calls without an explicit Looper
    • The zero-arg Handler constructor was deprecated in API 30 because it implicitly captures the current thread's looper — a footgun when called from a background thread. Always pass an explicit Looper, e.g. Handler(Looper.getMainLooper()).
  • org.openrewrite.kotlin.android.FindBitmapFactoryDecode$KtRecipe
    • Find BitmapFactory.decode* calls
    • BitmapFactory.decode* without an explicit BitmapFactory.Options.inSampleSize allocates the bitmap at full source resolution. For UI use, downsample via inSampleSize (or move the entire concern to Coil/Glide, which handle pooling, lifecycle, and downsampling for you).
  • org.openrewrite.kotlin.android.FindContentResolverQuery$KtRecipe
    • Find ContentResolver.query(...) calls
    • ContentResolver.query(...) blocks the calling thread for the full lifetime of the underlying IPC and cursor walk. Wrap the call in withContext(Dispatchers.IO) \{ … \}, expose it as a Flow (contentResolver.observe(uri)-style), or move to a Room @Query if the data is backed by SQLite.
  • org.openrewrite.kotlin.android.FindContextRegisterReceiver$KtRecipe
    • Find Context.registerReceiver(...) calls
    • On API 33+ registerReceiver(...) requires an explicit RECEIVER_EXPORTED / RECEIVER_NOT_EXPORTED flag — calls without it throw SecurityException. Migrate to ContextCompat.registerReceiver(...), which forwards the flag and is backwards-compatible.
  • org.openrewrite.kotlin.android.FindDeprecatedAndroidApis$KtRecipe
    • Find deprecated Android APIs
    • Search-only bundle that flags Android APIs the platform has deprecated or removed: findViewById, AsyncTask, the single-arg getColor/getDrawable/getColorStateList resource getters, the zero-arg Handler() constructor, kotlinx.android.synthetic imports, the moved kotlinx.android.parcel.Parcelize import, startActivityForResult/onActivityResult/requestPermissions, LocalBroadcastManager, deprecated Fragment lifecycle methods, Resources.getDrawable / getColor, startService, PreferenceManager, Vibrator.vibrate(long), Context.registerReceiver without flags, and MutableLiveData allocations.
  • org.openrewrite.kotlin.android.FindDeprecatedParcelizeImport$KtRecipe
    • Find kotlinx.android.parcel imports
    • The kotlinx.android.parcel package was moved to kotlinx.parcelize in 2020 when the standalone Parcelize plugin shipped. Replace the import and the Gradle plugin coordinate.
  • org.openrewrite.kotlin.android.FindDeprecatedResourceGetters$KtRecipe
    • Find deprecated Context.getColor/getDrawable/getColorStateList calls
    • The single-argument forms on Context/Resources are deprecated — they don't take a theme and behave inconsistently across API levels. Use ContextCompat.getColor(context, id)/ContextCompat.getDrawable(context, id)/AppCompatResources.getColorStateList(context, id) instead.
  • org.openrewrite.kotlin.android.FindFindViewById$KtRecipe
    • Find findViewById call sites
    • findViewById is the legacy view-lookup API. Modern Android uses ViewBinding (auto-generated Binding classes per layout) which is type-safe, null-safe, and avoids the per-call HashMap walk. Each match is a candidate for migration.
  • org.openrewrite.kotlin.android.FindFindViewByIdInHotPath$KtRecipe
    • Find findViewById inside onDraw / onMeasure / onLayout
    • View tree-walking inside the draw/measure/layout pass costs frame budget on every pass. Cache the lookup once (in onFinishInflate or a lateinit initialized when the view attaches), or — better — migrate to ViewBinding so the cache is generated.
  • org.openrewrite.kotlin.android.FindFragmentManagerExecutePendingTransactions$KtRecipe
    • Find FragmentManager.executePendingTransactions() calls
    • executePendingTransactions() forces synchronous fragment-transaction execution on the calling thread — usually a workaround for code that races a not-yet-attached fragment. Prefer commitNow()/commitNowAllowingStateLoss() at the call site that scheduled the transaction, or restructure the call to read state from the resulting fragment's onViewCreated.
  • org.openrewrite.kotlin.android.FindFragmentOnActivityCreated$KtRecipe
    • Find Fragment.onActivityCreated overrides
    • Fragment.onActivityCreated(Bundle) was deprecated in Fragment 1.3 because the host-activity lifecycle is no longer a reliable signal for fragment readiness. Move the work to onViewCreated (view-state setup) or to a LifecycleObserver on viewLifecycleOwner (cross-component coordination).
  • org.openrewrite.kotlin.android.FindFragmentOnAttachActivity$KtRecipe
    • Find Fragment.onAttach(Activity) overrides
    • The onAttach(Activity) overload was deprecated in API 23 — use onAttach(Context) instead. The Context parameter is the activity for activity-hosted fragments and the application context for headless cases, and works on devices where the parent is not an activity.
  • org.openrewrite.kotlin.android.FindFragmentSetRetainInstance$KtRecipe
    • Find Fragment.setRetainInstance(true) calls
    • Fragment.setRetainInstance(true) was deprecated in Fragment 1.3 because retained fragments break process-death restoration and tangle the lifecycle owner with the host activity. Move retained state to a ViewModel scoped to the navigation host.
  • org.openrewrite.kotlin.android.FindHandlerPostDelayed$KtRecipe
    • Find Handler.postDelayed(...) calls
    • Handler.postDelayed(runnable, ms) schedules main-thread work without a story for cancellation — the canonical bug is the activity dying while the runnable is still scheduled, then running with a stale view reference. Use lifecycleScope.launch \{ delay(ms); … \} instead: cancellation is automatic on lifecycle teardown.
  • org.openrewrite.kotlin.android.FindIntentActionGetContent$KtRecipe
    • Find legacy Intent.ACTION_PICK / ACTION_GET_CONTENT references
    • ACTION_PICK / ACTION_GET_CONTENT predate the Photo Picker (ACTION_PICK_IMAGES, API 33+ with backport) and the Storage Access Framework (ACTION_OPEN_DOCUMENT). For media, prefer ActivityResultContracts.PickVisualMedia; for documents, OpenDocument.
  • org.openrewrite.kotlin.android.FindKotlinAndroidSyntheticImports$KtRecipe
    • Find kotlinx.android.synthetic.* imports
    • The Kotlin Android Extensions synthetic-view plugin was deprecated in 1.4.20 and removed entirely in later AGP versions. Migrate the call sites to ViewBinding. Each reference here corresponds to a usage that won't compile without the (removed) plugin.
  • org.openrewrite.kotlin.android.FindLifecycleObserveLiveData$KtRecipe
    • Find LiveData.observe(this, observer) calls inside Fragment
    • Inside a Fragment, liveData.observe(this, observer) ties the subscription to the fragment's lifecycle — which outlives the view across onDestroyView/onCreateView recreation and produces dangling references to a destroyed view. Use viewLifecycleOwner instead.
  • org.openrewrite.kotlin.android.FindLiveDataPostValueFromMain$KtRecipe
    • Find MutableLiveData.postValue(...) calls inside coroutine main-thread contexts
    • postValue exists for background-thread updaters — it marshals through the main looper and coalesces consecutive updates. When called from a place that already runs on the main thread (UI callbacks, view-model initialization, Dispatchers.Main blocks), the coalescing is a footgun: intermediate values silently drop. Use setValue/.value = on the main thread, postValue only from background threads.
  • org.openrewrite.kotlin.android.FindLocalBroadcastManager$KtRecipe
    • Find LocalBroadcastManager.getInstance(...) usage
    • LocalBroadcastManager was deprecated in AndroidX 1.1 — the project effectively documents it as a global event bus, with all the ordering and lifetime trouble that implies. Migrate to LiveData, StateFlow, or SharedFlow for in-process pub/sub.
  • org.openrewrite.kotlin.android.FindManualDaggerProvision$KtRecipe
    • Find Dagger*Component.builder().build() patterns
    • Calls of the form DaggerXComponent.builder().build() indicate the application is composing its dependency graph by hand. Hilt (@HiltAndroidApp / @AndroidEntryPoint) generates the same graph automatically and integrates with Compose, ViewModel, and WorkManager out of the box.
  • org.openrewrite.kotlin.android.FindManualThreadingInActivity$KtRecipe
    • Find Thread \{ \}.start() calls inside Activity / Fragment
    • Hand-rolled Thread \{ … \}.start() inside a UI component leaks across configuration changes (the thread outlives the activity) and has no story for cancellation. Migrate to lifecycleScope.launch(Dispatchers.IO) \{ … \} or viewModelScope.launch \{ … \} — both cancel when the lifecycle ends.
  • org.openrewrite.kotlin.android.FindModeWorldReadable$KtRecipe
    • Find MODE_WORLD_READABLE / MODE_WORLD_WRITEABLE references
    • Both constants were deprecated in API 17 — they grant any app on the device read or write access to the file. Use a FileProvider to share content URIs, or scope the file via Context.getFilesDir() and expose it through your own content provider with explicit permissions.
  • org.openrewrite.kotlin.android.FindMutableLiveDataAllocation$KtRecipe
    • Find MutableLiveData allocations
    • In Kotlin Android code, MutableStateFlow<T> is the modern equivalent. It integrates with structured concurrency, exposes the current value synchronously, and composes cleanly with Compose's collectAsState/collectAsStateWithLifecycle. Each MutableLiveData() here is a candidate for migration.
  • org.openrewrite.kotlin.android.FindObjectAnimator$KtRecipe
    • Find ObjectAnimator.ofInt/ofFloat(...) calls
    • Direct ObjectAnimator use is a candidate for review — for Compose UIs the idiomatic replacement is animate*AsState / Animatable, which integrate with recomposition and cancellation. For view-system code, SpringAnimation/PhysicsAnimation cover more interaction shapes than the time-based ObjectAnimator.
  • org.openrewrite.kotlin.android.FindOnActivityResultOverride$KtRecipe
    • Find onActivityResult overrides
    • Every override of Activity.onActivityResult / Fragment.onActivityResult is half of the deprecated startActivityForResult pair. The new Activity Result APIs (registerForActivityResult(ActivityResultContracts.X) \{ … \}) deliver results to a lambda colocated with the launcher — the override goes away.
  • org.openrewrite.kotlin.android.FindOnRequestPermissionsResultOverride$KtRecipe
    • Find onRequestPermissionsResult overrides
    • Override of Activity.onRequestPermissionsResult / Fragment.onRequestPermissionsResult — the result-callback half of the deprecated requestPermissions(...) pair. Replace with a registerForActivityResult(ActivityResultContracts.RequestPermission) lambda.
  • org.openrewrite.kotlin.android.FindParcelableJavaImpl$KtRecipe
    • Find Kotlin classes implementing Parcelable without @Parcelize
    • Manual writeToParcel / CREATOR implementations are pure boilerplate that the kotlin-parcelize plugin generates for any class annotated @Parcelize. Each manual implementation is a candidate for migration to the annotation.
  • org.openrewrite.kotlin.android.FindPreferenceManagerGetDefaultSharedPreferences$KtRecipe
    • Find PreferenceManager.getDefaultSharedPreferences(...) calls
    • android.preference.* (framework) was deprecated in API 29. AndroidX androidx.preference.* is the supported path, and even there the modern shape for KV state is Jetpack DataStore (Preferences DataStore for key/value, Proto DataStore for typed records) — which works in suspend contexts and survives process death without main-thread blocking.
  • org.openrewrite.kotlin.android.FindPreferenceManagerImport$KtRecipe
    • Find android.preference.PreferenceManager imports
    • The framework android.preference.* package was deprecated in API 29. Move to androidx.preference.* (drop-in replacement) or, for KV state, Jetpack DataStore — which composes with coroutines and Flow instead of running on the main thread.
  • org.openrewrite.kotlin.android.FindPublicMutableLiveDataProperty$KtRecipe
    • Find public MutableLiveData properties
    • Exposing MutableLiveData<T> (or MutableStateFlow<T>) to observers lets them mutate the model from the consumer side. The convention is to keep the mutable handle private and expose a read-only LiveData<T> / StateFlow<T> getter, so the owner is the only one that can publish updates.
  • org.openrewrite.kotlin.android.FindRealmUsage$KtRecipe
    • Find Realm.getDefaultInstance() calls
    • Realm Java/Kotlin is in maintenance and was effectively superseded by Realm Kotlin (io.realm.kotlin) and ultimately by MongoDB Atlas Device SDKs. Each Realm.getDefaultInstance() is a candidate for migration to Room + DataStore, or to the modern Realm Kotlin SDK.
  • org.openrewrite.kotlin.android.FindRequestPermissions$KtRecipe
    • Find direct requestPermissions(...) calls
    • Calling requestPermissions(...) directly couples the request to a screen and a request code that has to be matched in onRequestPermissionsResult. The modern shape is registerForActivityResult(ActivityResultContracts.RequestPermission()) \{ granted -> … \} — the result lands in a lambda next to the launcher.
  • org.openrewrite.kotlin.android.FindResourcesGetColor$KtRecipe
    • Find resources.getColor(...) (one-arg) calls
    • Resources.getColor(int) was deprecated in API 23 — it doesn't take a theme. Replace with ContextCompat.getColor(context, id), which threads the theme through and respects ?attr references.
  • org.openrewrite.kotlin.android.FindResourcesGetDrawable$KtRecipe
    • Find resources.getDrawable(...) (one-arg) calls
    • Resources.getDrawable(int) was deprecated in API 22 — it doesn't take a theme. Replace with ResourcesCompat.getDrawable(resources, id, theme) to render themed drawables consistently across API levels.
  • org.openrewrite.kotlin.android.FindRoomQueryWithoutLiveDataOrFlow$KtRecipe
    • Find @Query DAO methods returning a synchronous result
    • A non-suspend @Query returning List<X>, X?, or a scalar runs the DB query on the calling thread — by default Room throws IllegalStateException if that's the main thread. Mark the function suspend (single-shot) or return Flow<...> / LiveData<...> (observable) so Room can dispatch the query off the main thread.
  • org.openrewrite.kotlin.android.FindRunOnUiThread$KtRecipe
    • Find Activity.runOnUiThread \{ \} / View.post \{ \} calls
    • Hand-rolled main-thread dispatch (runOnUiThread, view.post) was the pre-coroutine pattern for crossing thread boundaries from a background worker. In Kotlin, prefer withContext(Dispatchers.Main) \{ … \} — it composes with structured cancellation and surfaces in the call site's coroutine context.
  • org.openrewrite.kotlin.android.FindRuntimeExecInAndroid$KtRecipe
    • Find Runtime.exec(...) / ProcessBuilder.start() calls
    • Forking a process from an Android app is almost never the right shape — the system imposes strict app-sandbox limits (no su, no arbitrary binaries) and process lifetime is unrelated to the activity that spawned it. Audit each call site against WorkManager, foreground services, or — if you genuinely need shell tools — java.lang.ProcessBuilder with explicit lifecycle management.
  • org.openrewrite.kotlin.android.FindRxJavaImports$KtRecipe
    • Find io.reactivex.* imports
    • RxJava is in maintenance and Kotlin's idiomatic story is coroutines + Flow. Each import is a candidate for migration to kotlinx.coroutines.flow.* (or, at the boundary, kotlinx-coroutines-rx3/kotlinx-coroutines-rx2 adapters during incremental migration).
  • org.openrewrite.kotlin.android.FindSerializableUsage$KtRecipe
    • Find Kotlin classes implementing java.io.Serializable
    • java.io.Serializable is reflection-based and slow on Android — it allocates substantially more than Parcelable and pulls in field-by-field reflection at deserialize time. For inter-component transport, prefer @Parcelize (kotlin-parcelize) or kotlinx.serialization.
  • org.openrewrite.kotlin.android.FindSharedPreferencesCommit$KtRecipe
    • Find SharedPreferences.Editor.commit() calls
    • commit() writes to disk on the calling thread (often the main thread, where it can drop a frame). apply() writes asynchronously and atomically, returning immediately — use it unless you specifically need the boolean result on the spot.
  • org.openrewrite.kotlin.android.FindSharedPreferencesEdit$KtRecipe
    • Find sharedPrefs.edit() calls
    • Raw edit() calls predate AndroidX SharedPreferences.edit \{ … \}, the Kotlin extension that handles apply/commit for the caller. Replace each prefs.edit().put*(...).apply() chain with prefs.edit \{ put*(...) \} — same disk write, less ceremony, and harder to drop the apply by accident.
  • org.openrewrite.kotlin.android.FindStartActivityForResult$KtRecipe
    • Find startActivityForResult calls
    • startActivityForResult was deprecated in AndroidX Activity 1.2 / Fragment 1.3 in favor of the Activity Result APIs (registerForActivityResult(ActivityResultContracts.X) \{ result -> … \}). The new API survives process death, decouples the launcher from the lifecycle owner, and removes the per-screen onActivityResult switch.
  • org.openrewrite.kotlin.android.FindStartService$KtRecipe
    • Find startService(...) calls
    • On API 26+ background apps cannot call startService(...) — the system throws IllegalStateException. For services that must run while the app is backgrounded, call ContextCompat.startForegroundService(context, intent) and post a notification within five seconds. For one-shot work, use WorkManager instead.
  • org.openrewrite.kotlin.android.FindSystemOutInAndroid$KtRecipe
    • Find System.out.println(...) calls
    • System.out on Android writes to logcat under a default tag that's easy to lose. Use Log.d/Log.i for tagged output (or, better, Timber) — both route through Android's logging pipeline with filterable tags.
  • org.openrewrite.kotlin.android.FindVibratorVibrateLong$KtRecipe
    • Find Vibrator.vibrate(long) (one-arg) calls
    • The single-long Vibrator.vibrate(ms) was deprecated in API 26. Use Vibrator.vibrate(VibrationEffect.createOneShot(...)) (or VibratorManager on API 31+) — both let the platform pick the appropriate amplitude.
  • org.openrewrite.kotlin.android.FindWebViewJavaScriptEnabled$KtRecipe
    • Find WebSettings.setJavaScriptEnabled(true) calls
    • Enabling JavaScript in a WebView lets the loaded page run arbitrary script in your app's context. For first-party content this is often fine; for any third-party content it's a critical security boundary. Each match is worth reviewing alongside the trust model of the loaded URLs.
  • org.openrewrite.kotlin.android.FindWebViewLoadUrl$KtRecipe
    • Find WebView.loadUrl(...) calls
    • Each WebView.loadUrl(...) is worth a security review: arbitrary http://-scheme URLs bypass the system browser and inherit the WebView's privileges (cookies, JS bridges). For untrusted content prefer CustomTabsIntent (Chrome Custom Tabs) — better security, better UX, no JS bridge.
  • org.openrewrite.kotlin.bestpractices.BestPractices$KtRecipe
    • Apply Kotlin best-practice idioms
    • Opinionated bundle of every best-practice recipe in this module: collection / string round-trip collapses, stdlib accessor swaps, and a broad set of search-only flags covering class structure, function declarations, string construction, Boolean conditionals, lambdas, and exception handling. For diff-only output, use ImproveKotlinBestPractices instead.
  • org.openrewrite.kotlin.bestpractices.CollapseRedundantConversions$KtRecipe
    • Collapse redundant collection / string conversions
    • Drops needless toList()/toMutableList()/toSet()/toTypedArray() round-trips and trimStart().trimEnd()-style chains that allocate one or more intermediate copies. The replacement performs the same conversion in one pass.
  • org.openrewrite.kotlin.bestpractices.FindAbstractClassWithoutMembers$KtRecipe
    • Find abstract class declarations without abstract members
    • An abstract class with no abstract members and no state offers nothing over interface — and interface composes better (multiple inheritance, no constructor coupling). If the class is being used as a marker, consider sealed interface for stronger exhaustiveness checks.
  • org.openrewrite.kotlin.bestpractices.FindAlsoPrintln$KtRecipe
    • Find .also \{ println(it) \} debug patterns
    • .also \{ println(it) \} is a side-channel print left over from debugging. It survives compilation, runs in production, and is invisible at the call site — remove it or route the value through a logger.
  • org.openrewrite.kotlin.bestpractices.FindAlsoWithEmptyBody$KtRecipe
    • Find x.also \{ \} calls with an empty body
    • x.also \{ \} with an empty block is a no-op that returns x. Drop the call — it adds an allocation for the captured lambda and obscures the value flow.
  • org.openrewrite.kotlin.bestpractices.FindAnonymousObjectSingleMethod$KtRecipe
    • Find anonymous object : Interface \{ override fun … \} with a single override
    • An anonymous object that implements one interface with a single function override is the canonical SAM-conversion target. If the interface is declared fun interface I \{ … \}, the call site collapses to a lambda.
  • org.openrewrite.kotlin.bestpractices.FindBareExceptionThrow$KtRecipe
    • Find throw Exception("…") calls
    • Throwing bare Exception (or RuntimeException) loses information that a more specific type would carry. Prefer IllegalArgumentException (bad input), IllegalStateException (object in wrong state), or a domain-specific subclass.
  • org.openrewrite.kotlin.bestpractices.FindBareRuntimeExceptionThrow$KtRecipe
    • Find throw RuntimeException("…") calls
    • Same family as throw Exception("...") — flag for replacement with IllegalArgumentException/IllegalStateException/domain-specific subclass.
  • org.openrewrite.kotlin.bestpractices.FindBestPracticeCandidates$KtRecipe
    • Find best-practice candidates
    • Search-only bundle: flags places where a more idiomatic Kotlin spelling is available. Each match shows up as a SearchResult for review; nothing is rewritten automatically because the migration is a judgment call.
  • org.openrewrite.kotlin.bestpractices.FindBooleanConditionalSmells$KtRecipe
    • Find Boolean-conditional smells
    • if (x) true else false, its inverse, !x.isEmpty() / !x.isBlank() family negations, if (x == null) null else x.foo(), if (x != null) x.foo() patterns, and if (return …) else … early-return ladders — each is the long form of a single Kotlin operator or method.
  • org.openrewrite.kotlin.bestpractices.FindBooleanLiteralReturnType$KtRecipe
    • Find fun f(): Boolean = true|false literal-returning functions
    • A function whose body is literally true or false is rarely the right shape — either the predicate was a stub left in by mistake, or the value is genuinely constant and should be a const val. Either way, surface the call site for human review.
  • org.openrewrite.kotlin.bestpractices.FindCatchReturningNull$KtRecipe
    • Find try \{ … \} catch (e: Exception) \{ null \} patterns
    • Swallowing every exception into null discards diagnostic information and conflates 'no value' with 'I lost the cause'. runCatching \{ \} returns a Result<T> whose getOrNull() matches this shape, and onFailure \{ \} keeps a hook for diagnostics.
  • org.openrewrite.kotlin.bestpractices.FindClassExtendsAny$KtRecipe
    • Find class Foo : Any() declarations
    • Every class implicitly extends Any — writing it explicitly only adds noise and a redundant supertype clause.
  • org.openrewrite.kotlin.bestpractices.FindClassStructureSmells$KtRecipe
    • Find class-declaration smells
    • Search-only bundle: flags class-level structural smells (empty companion, empty class body, redundant : Any(), manual toString/equals/hashCode trio, named companion of constants, multiple secondary constructors, sealed-class-without-state, marker-object-without-data, empty init, all-val classes that could be data class, mutable data class/object state, abstract classes without abstract members, and open classes with no overridable members).
  • org.openrewrite.kotlin.bestpractices.FindClassWithoutDataAnnotation$KtRecipe
    • Find classes that could be data class
    • A class whose only members are val constructor parameters — no methods, no init, no extra properties — is the canonical shape data class exists for. Adding data synthesizes toString/equals/hashCode/copy/componentN without changing any other semantics.
  • org.openrewrite.kotlin.bestpractices.FindCompareToInsteadOfOperator$KtRecipe
    • Find x.compareTo(y) <op> 0 patterns
    • x.compareTo(y) > 0 is the long form of x > y for any Comparable<T> — and >/</>=/<= are defined on Comparable to use exactly that call. The operator form reads as the comparison it is, and the bytecode emitted is identical.
  • org.openrewrite.kotlin.bestpractices.FindDataClassWithMutableProperty$KtRecipe
    • Find data class declarations with var properties
    • data class generates equals and hashCode over the primary-constructor properties. Mutating a var property after the instance is stored in a hash-based collection breaks the invariant — the entry can no longer be found by lookup. Prefer val; if mutation is needed, model it through copy(...).
  • org.openrewrite.kotlin.bestpractices.FindDataObjectCandidates$KtRecipe
    • Find marker object declarations that could be data object
    • A bare object Foo declaration with no body inherits Any.toString(), which prints as Foo$Companion@<hash>-style identity strings. data object Foo (Kotlin 1.9+) generates a readable toString ("Foo"), equals (identity), and hashCode — preferred for marker singletons used in when exhaustiveness checks and serialization.
  • org.openrewrite.kotlin.bestpractices.FindDoubleBangChain$KtRecipe
    • Find chained !! assertions in a single expression
    • x!!.y!!.z chains multiple !! assertions in one expression. Each one is a separate NPE risk with no diagnostic. Either the intermediate values are non-null (and the assertions can be dropped) or the chain should be modeled with ?.let \{ \} to surface the absent case explicitly.
  • org.openrewrite.kotlin.bestpractices.FindElseAfterReturn$KtRecipe
    • Find if (x) \{ return … \} else \{ … \} patterns
    • When the if branch returns, the else is unreachable as a fall-through guard — the body after the else can be moved out of the else block, which makes the early-return shape obvious to readers.
  • org.openrewrite.kotlin.bestpractices.FindEmptyClassBody$KtRecipe
    • Find class Foo \{\} declarations with an empty body
    • An empty class body \{\} adds no information. Kotlin allows the body to be omitted entirely: class Foo is the same declaration without the redundant braces.
  • org.openrewrite.kotlin.bestpractices.FindEmptyCompanionObject$KtRecipe
    • Find empty companion object declarations
    • A companion object \{\} with no members generates a synthetic Companion holder class that's never used. Remove it — Kotlin doesn't require an empty companion object for any feature.
  • org.openrewrite.kotlin.bestpractices.FindEmptyInitBlock$KtRecipe
    • Find empty init \{ \} blocks
    • An empty init \{\} block is a no-op. Remove it — every empty initializer is a place a future reader pauses before noticing it does nothing.
  • org.openrewrite.kotlin.bestpractices.FindEqualitySmells$KtRecipe
    • Find equality / comparison smells
    • === / !== referential checks (usually == is meant), x == true|false longhand for Boolean, and x.compareTo(y) > 0 calls that should use the comparison operator directly.
  • org.openrewrite.kotlin.bestpractices.FindEqualsToBooleanLiteral$KtRecipe
    • Find b == true / b == false comparisons
    • When b is Boolean, b == true is just b and b == false is !b. The longhand only obscures intent. For Boolean? the longhand is meaningful — it returns false for null rather than failing on !! — so leave those alone; flag only the comparison and let the reviewer decide.
  • org.openrewrite.kotlin.bestpractices.FindExceptionHandlingSmells$KtRecipe
    • Find exception-handling smells
    • Throwable.printStackTrace() (bypasses the logger), bare throw Exception("…") / throw RuntimeException("…") allocations (lose type information), and try \{ \} catch (e: Exception) \{ null \} patterns (collapsible to runCatching \{ \}.getOrNull()).
  • org.openrewrite.kotlin.bestpractices.FindExplicitUnitReturnType$KtRecipe
    • Find functions with explicit : Unit return type
    • Kotlin functions that don't declare a return type return Unit by convention. Writing : Unit explicitly adds noise — drop it unless the explicit form aids a generated API surface (e.g. @JvmOverloads).
  • org.openrewrite.kotlin.bestpractices.FindForEachAddCandidate$KtRecipe
    • Find xs.forEach \{ ys.add(it) \} patterns
    • xs.forEach \{ ys.add(it) \} is the loop-form of ys.addAll(xs). The bulk operation is a single method call and uses the most efficient copy strategy the receiver supports.
  • org.openrewrite.kotlin.bestpractices.FindForEachPrintln$KtRecipe
    • Find forEach \{ println(it) \} patterns
    • forEach \{ println(it) \} is the loop-form for (x in xs) println(x) — verify whether the print is debug-only. If it is intended output, consider joinToString("\n").let(::println) to write the whole thing in one syscall.
  • org.openrewrite.kotlin.bestpractices.FindForceUnwrapInLet$KtRecipe
    • Find !! inside a ?.let \{ \} body
    • x?.let \{ it!! \} (or it.foo()!!) is internally inconsistent: the safe-call entry already guards null, so the !! on it can never trigger. Drop the !! — the lambda body has a non-null receiver by construction.
  • org.openrewrite.kotlin.bestpractices.FindFunctionDeclarationSmells$KtRecipe
    • Find function-declaration smells
    • Search-only bundle: explicit : Unit / : Nothing return type, block bodies that are a single return expr, literal-Boolean-returning functions, Pair/Triple returns that could be data classes, default-before-required parameters, suspend fun declarations returning Job/Deferred, and explicit return Unit statements.
  • org.openrewrite.kotlin.bestpractices.FindFunctionReturningPair$KtRecipe
    • Find functions returning Pair<A, B>
    • A function returning Pair<A, B> forces every caller to remember which side is which. A small data class Result(val a: A, val b: B) documents the role of each component and gains componentN() / copy() for free.
  • org.openrewrite.kotlin.bestpractices.FindFunctionReturningTriple$KtRecipe
    • Find functions returning Triple<A, B, C>
    • Same shape as the Pair case but the readability cost compounds — three positional components is the limit where most readers stop guessing which is which. Replace with a data class.
  • org.openrewrite.kotlin.bestpractices.FindIfNullElseExpression$KtRecipe
    • Find if (x == null) null else x.foo() patterns
    • An if (x == null) null else x.foo() collapses to the safe-call x?.foo(). The safe-call is structurally null-aware — the longhand re-checks for null without surfacing the absent case in the type system.
  • org.openrewrite.kotlin.bestpractices.FindIfReturnFalseElseTrue$KtRecipe
    • Find if (x) false else true patterns
    • if (x) false else true is just !x. Replace the inverted Boolean conditional with the negation it already expresses.
  • org.openrewrite.kotlin.bestpractices.FindIfReturnTrueElseFalse$KtRecipe
    • Find if (x) true else false patterns
    • if (x) true else false is just x after Boolean simplification. The longhand only hides intent.
  • org.openrewrite.kotlin.bestpractices.FindIsNotEmptyOnString$KtRecipe
    • Find x.isNotEmpty() on String where isNotBlank() might be wanted
    • String.isNotEmpty() returns true for whitespace-only strings. When the check exists to guard against missing user input, isNotBlank() is usually closer to the intent.
  • org.openrewrite.kotlin.bestpractices.FindLambdaSmells$KtRecipe
    • Find lambda / functional smells
    • Redundant map \{ it.toString() \} / forEach \{ it.toString() \}, debug-leftover .also \{ println(it) \} / forEach \{ println(it) \}, xs.toList().forEach \{ \} over already-iterable receivers, .also \{ it.add(...) \}-built mutable collections, and forEach \{ ys.add(it) \} (use addAll).
  • org.openrewrite.kotlin.bestpractices.FindLazyWithoutMode$KtRecipe
    • Find lazy \{ \} calls without an explicit LazyThreadSafetyMode
    • lazy \{ \} defaults to LazyThreadSafetyMode.SYNCHRONIZED — every read passes a synchronized check. For thread-confined state (UI, single-threaded actors), lazy(LazyThreadSafetyMode.NONE) \{ \} avoids the synchronization entirely.
  • org.openrewrite.kotlin.bestpractices.FindLetForNotNullCheck$KtRecipe
    • Find if (x != null) x.foo() patterns
    • if (x != null) \{ x.foo() \} is the long form of x?.let \{ it.foo() \} — or, when only one call is needed and x is a local, x?.foo(). The safe-call form makes the null-aware path part of the type system rather than a separate Boolean check.
  • org.openrewrite.kotlin.bestpractices.FindManualToStringEqualsHashCode$KtRecipe
    • Find classes with manual toString/equals/hashCode overrides
    • A class that overrides all three of toString, equals, and hashCode over its own fields is the canonical shape data class exists for. Migrate to data class C(val a: A, …) to delete the boilerplate and gain copy() plus componentN() for free.
  • org.openrewrite.kotlin.bestpractices.FindMultipleSecondaryConstructors$KtRecipe
    • Find classes with multiple overloaded secondary constructors
    • Two or more secondary constructors with different arities almost always collapse into a single primary constructor with default arguments. The default-arg form composes with named arguments and @JvmOverloads for cross-language interop.
  • org.openrewrite.kotlin.bestpractices.FindMutableListAlsoAdd$KtRecipe
    • Find mutableListOf<T>().also \{ it.add(x) \} patterns
    • Building a list through mutableListOf<T>().also \{ it.add(...) \} is the side-channel form of buildList \{ add(...) \} (or just mutableListOf(x) if every element is known up front). The builder form makes intent explicit.
  • org.openrewrite.kotlin.bestpractices.FindMutableMapAlsoPut$KtRecipe
    • Find mutableMapOf<K,V>().also \{ it.put(...) \} patterns
    • Same shape as mutableListOf().also \{ it.add(...) \} for maps. The builder form (buildMap \{ put(...) \}) makes the entries visible without the side-channel it.put.
  • org.openrewrite.kotlin.bestpractices.FindMutableSetAlsoAdd$KtRecipe
    • Find mutableSetOf<T>().also \{ it.add(x) \} patterns
    • Same shape as the mutableListOf variant — use buildSet \{ add(...) \} for the same readability gain.
  • org.openrewrite.kotlin.bestpractices.FindNamedCompanionObjectOfConstants$KtRecipe
    • Find named companion object Constants patterns
    • A companion object Constants \{ const val X = ... \} adds a named singleton to hold what are essentially file-level constants. Promote the constants to top-level const vals — they read the same and avoid the synthetic holder class.
  • org.openrewrite.kotlin.bestpractices.FindNegatedIsBlank$KtRecipe
    • Find !x.isBlank() calls
    • Kotlin's String.isNotBlank() is the direct positive form. The negated-blank check reads the same value without the leading !.
  • org.openrewrite.kotlin.bestpractices.FindNegatedIsEmpty$KtRecipe
    • Find !x.isEmpty() calls
    • Kotlin ships isNotEmpty() directly on String, Collection, and Map. The negation reads top-down without re-parsing the !.
  • org.openrewrite.kotlin.bestpractices.FindNegatedIsNotBlank$KtRecipe
    • Find !x.isNotBlank() calls
    • !x.isNotBlank() is the long form of x.isBlank().
  • org.openrewrite.kotlin.bestpractices.FindNegatedIsNotEmpty$KtRecipe
    • Find !x.isNotEmpty() calls
    • !x.isNotEmpty() is the long form of x.isEmpty(). Use the direct positive call.
  • org.openrewrite.kotlin.bestpractices.FindObjectWithMutableState$KtRecipe
    • Find object declarations with var properties
    • An object is a singleton — its var properties are shared global mutable state. Concurrent reads/writes race without synchronization, and the value can change in surprising ways across modules.
  • org.openrewrite.kotlin.bestpractices.FindOpenClassWithoutOverrides$KtRecipe
    • Find open class declarations without overridable members
    • The open modifier on a class only matters if subclasses override something. A bare open class Foo (or one whose members are all final) signals an intent — "this class is meant to be extended" — that the type system can't actually enforce. Either declare specific members open (and drop the class-level open) or remove the modifier entirely.
  • org.openrewrite.kotlin.bestpractices.FindRedundantReturnUnit$KtRecipe
    • Find return Unit / return kotlin.Unit statements
    • Functions that return Unit don't need an explicit return at all — return (no expression) or simply falling off the end is the conventional shape. Writing return Unit repeats the implicit value.
  • org.openrewrite.kotlin.bestpractices.FindRedundantToStringInForEach$KtRecipe
    • Find forEach \{ it.toString() \} patterns
    • it.toString() inside a forEach evaluates the call but discards the result — equivalent to forEach \{\}. Either the side effect on toString() is the goal (very unusual) or the call is dead code.
  • org.openrewrite.kotlin.bestpractices.FindRedundantToStringInMap$KtRecipe
    • Find map \{ it.toString() \} / map \{ x -> x.toString() \} patterns
    • If the producer already returns a type whose toString() is the desired representation, the map is a no-op. If the goal is to materialize the Strings up-front, joinToString() / toString() on the collection is usually a better fit.
  • org.openrewrite.kotlin.bestpractices.FindReferentialEquality$KtRecipe
    • Find === / !== referential-equality comparisons
    • Kotlin's === checks reference identity, ignoring equals. For data class and other value-like types this almost always wants == instead. Flag every referential check for review — true reference comparisons (e.g. sentinel Any objects) are legitimate but rare.
  • org.openrewrite.kotlin.bestpractices.FindReturnTypeNothing$KtRecipe
    • Find functions declared with : Nothing return type
    • Nothing means 'this function never returns normally' — the body must throw, loop forever, or call another Nothing-returning function. Flag for review: if the body actually does return, the type is wrong; if it always throws, the call sites can rely on Kotlin's exhaustiveness checks.
  • org.openrewrite.kotlin.bestpractices.FindRunWithEmptyBody$KtRecipe
    • Find run \{ \} calls with an empty body
    • run \{ \} is a scope function for evaluating a block as an expression with an implicit this receiver. If the block is empty, the call evaluates to Unit and does nothing — drop it.
  • org.openrewrite.kotlin.bestpractices.FindScopeFunctionSmells$KtRecipe
    • Find scope-function smells
    • run \{ \} with empty or no-this bodies, also \{ \} with empty bodies, and ?.let \{ … !! \} patterns where the null-guard and force-unwrap contradict each other.
  • org.openrewrite.kotlin.bestpractices.FindSealedClassWithoutStateCandidates$KtRecipe
    • Find sealed class declarations that could be sealed interface
    • A sealed class with no constructor parameters and no fields adds no expressive power over sealed interface. The interface form composes better (allows multiple inheritance, supports data object direct implementation, makes the no-state contract explicit).
  • org.openrewrite.kotlin.bestpractices.FindSingleExpressionBodyCandidate$KtRecipe
    • Find fun foo(): T \{ return x \} block bodies
    • A function whose entire body is a single return expr is the canonical shape for Kotlin's single-expression-body syntax (fun foo(): T = expr). The expression form makes type inference more useful and removes one level of brace nesting.
  • org.openrewrite.kotlin.bestpractices.FindStandaloneRunWithoutThis$KtRecipe
    • Find top-level run \{ … \} whose body never uses this
    • Top-level run \{ … \} (no receiver) is meaningful only when the block uses the implicit this or executes multiple statements as an expression. If the body neither references this nor depends on the scoping it provides, the wrapper just adds an unnecessary lambda allocation.
  • org.openrewrite.kotlin.bestpractices.FindStringConcatWithEmptyLeft$KtRecipe
    • Find "" + x patterns
    • Prepending an empty "" is a Java idiom for forcing a toString() conversion. In Kotlin write x.toString() or the template "$x" for the same effect with explicit intent.
  • org.openrewrite.kotlin.bestpractices.FindStringConcatWithEmptyRight$KtRecipe
    • Find x + "" patterns
    • Appending an empty "" is the inverse of the Java "" + x idiom. Use x.toString() or "$x" instead.
  • org.openrewrite.kotlin.bestpractices.FindStringConstructionSmells$KtRecipe
    • Find string-construction smells
    • Trivial String.format("%s", x) calls and "" + x / x + "" concatenations that read more clearly as Kotlin string templates.
  • org.openrewrite.kotlin.bestpractices.FindStringFormatTrivial$KtRecipe
    • Find trivial String.format("%s", x) calls
    • String.format("%s", x) is the long-hand for the Kotlin string template "$x". The template avoids the per-call format-string parse and reads as the thing it produces.
  • org.openrewrite.kotlin.bestpractices.FindSuspendFunctionReturningJob$KtRecipe
    • Find suspend fun declarations returning Job / Deferred
    • A suspend fun foo(): Job is almost always a confusion of two patterns — either the function should suspend and return a value (drop the Job/Deferred), or it should launch and return the handle (drop suspend, and call coroutineScope \{ launch \{ … \} \} internally).
  • org.openrewrite.kotlin.bestpractices.FindThrowablePrintStackTrace$KtRecipe
    • Find Throwable.printStackTrace() calls
    • printStackTrace() writes the throwable straight to System.err, bypassing whatever structured logger the application uses. Route the throwable through a logger so log levels, MDCs, and sinks apply.
  • org.openrewrite.kotlin.bestpractices.FindToListBeforeForEach$KtRecipe
    • Find xs.toList().forEach \{ … \} patterns
    • Iterable.forEach already iterates without materializing a list. The intermediate toList() allocates a copy that's read once and discarded.
  • org.openrewrite.kotlin.bestpractices.FindWhenAsStatement$KtRecipe
    • Find when (x) \{ … \} used as a statement
    • A when used as a statement (its result is discarded) often obscures intent — either the writer expected an expression value or each branch is a side-effecting block that would read more clearly as if/else if. Flag for review.
  • org.openrewrite.kotlin.bestpractices.FindWhenSmells$KtRecipe
    • Find when-statement smells
    • when expressions that read awkwardly: missing else, single-branch, used as statement, with duplicate branch bodies that should collapse to comma-separated labels, or with a Boolean selector that should be if.
  • org.openrewrite.kotlin.bestpractices.FindWhenWithBooleanSubject$KtRecipe
    • Find when (b: Boolean) selectors
    • when (b) \{ true -> … false -> … \} is the long form of if (b) … — and the when reads as if it might gain a third branch, which Boolean cannot. Replace with if.
  • org.openrewrite.kotlin.bestpractices.FindWhenWithIdenticalBranches$KtRecipe
    • Find when with two or more branches having identical bodies
    • when (x) \{ A -> f(); B -> f() \} repeats the same body for distinct labels — collapse to a single branch with comma-separated labels: A, B -> f().
  • org.openrewrite.kotlin.bestpractices.FindWhenWithSingleBranch$KtRecipe
    • Find when (x) \{ A -> … \} with a single branch
    • A single-branch when (x) \{ A -> … \} is the long form of if (x == A) …. The if reads more directly and doesn't suggest the branch list will grow.
  • org.openrewrite.kotlin.bestpractices.FindWhenWithoutElse$KtRecipe
    • Find when (x) expressions without an else branch
    • A when (x) used as an expression requires exhaustiveness — without an else, the compiler can only prove it for sealed/enum selectors. Used as a statement, the missing else is a tripwire: any new variant silently falls through. Flag for review.
  • org.openrewrite.kotlin.bestpractices.FindWildcardImport$KtRecipe
    • Find wildcard import a.b.* statements
    • Wildcard imports pull every public symbol from a package — they hide the dependency surface and make incremental compilation more conservative. Prefer explicit per-symbol imports.
  • org.openrewrite.kotlin.bestpractices.ImproveKotlinBestPractices$KtRecipe
    • Apply Kotlin best-practice rewrites
    • Autofix-only best-practice bundle: collection / string round-trip collapses and stdlib accessor swaps. Excludes the search-only Find* recipes so the run output is just diffs, not a flood of search results.
  • org.openrewrite.kotlin.bestpractices.UseFirstForGetZero$KtRecipe
    • Use first() instead of get(0)
    • first() reads more naturally than get(0) and gives the same compile-time bounds guarantees — both throw NoSuchElementException/IndexOutOfBoundsException on an empty list.
  • org.openrewrite.kotlin.bestpractices.UseLengthForCountNoPredicate$KtRecipe
    • Use length instead of String.count()
    • String.count() walks every character and increments a counter. length reads the precomputed size off the String header.
  • org.openrewrite.kotlin.bestpractices.UseSizeForCountNoPredicate$KtRecipe
    • Use size instead of Collection.count()
    • Collection.count() without a predicate walks the iterable. size reads the precomputed property on Collection.
  • org.openrewrite.kotlin.bestpractices.UseStdlibAccessors$KtRecipe
    • Use stdlib accessors for size / first
    • Replaces walk-based accessors with their O(1) property/method equivalents — count()/length/size and get(0)/first().
  • org.openrewrite.kotlin.bestpractices.UseToListForToMutableListThenToList$KtRecipe
    • Use toList() instead of toMutableList().toList()
    • toMutableList() allocates a mutable copy, then toList() copies it again to an immutable list. toList() directly does what's needed in one pass.
  • org.openrewrite.kotlin.bestpractices.UseToListForToSetThenToList$KtRecipe
    • Use distinct() instead of toSet().toList()
    • distinct() returns a List with duplicates removed in one pass. toSet().toList() allocates a set and then copies its contents into a list — two allocations to do the same job, and the order semantics differ subtly because hash-based sets don't preserve insertion order across all platforms.
  • org.openrewrite.kotlin.bestpractices.UseToSetForDistinctThenToSet$KtRecipe
    • Use toSet() instead of distinct().toSet()
    • toSet() deduplicates while building the set. distinct().toSet() allocates a List of distinct elements first, then copies into the set.
  • org.openrewrite.kotlin.bestpractices.UseToSetForToListThenToSet$KtRecipe
    • Use toSet() instead of toList().toSet()
    • toSet() works on any Iterable. The intermediate toList() just allocates a list that's immediately discarded.
  • org.openrewrite.kotlin.bestpractices.UseToStringForStringToString$KtRecipe
    • Drop redundant String.toString()
    • Calling toString() on a value that is already a String is a no-op that compiles to a method call returning the same reference.
  • org.openrewrite.kotlin.bestpractices.UseToTypedArrayForToListThenToTypedArray$KtRecipe
    • Use toTypedArray() instead of toList().toTypedArray()
    • toTypedArray() accepts any Collection; the intermediate toList() just allocates a list that's immediately discarded.
  • org.openrewrite.kotlin.bestpractices.UseTrimForTrimEndThenTrimStart$KtRecipe
    • Use trim() instead of trimEnd().trimStart()
    • Same as the inverse — trim() strips both ends in one pass without the intermediate String allocation.
  • org.openrewrite.kotlin.bestpractices.UseTrimForTrimStartThenTrimEnd$KtRecipe
    • Use trim() instead of trimStart().trimEnd()
    • trim() strips whitespace from both ends in a single pass with no intermediate allocation. trimStart().trimEnd() builds a temporary String for the left-trimmed value before the second pass.
  • org.openrewrite.kotlin.compose.Compose$KtRecipe
    • Find Compose stability and recomposition issues
    • Search-only recipes that surface Jetpack Compose anti-patterns the Android docs and Compose stability guide call out: unstable parameter types, mutable classes annotated @Stable/@Immutable, inline Modifier allocations, missing remember keys, effect-handler misuse, navigation inside composable bodies, single-child layout wrappers, lazy-list items without keys, and API-shape violations. Each match is a SearchResult for human review — Compose remedies are judgement calls (hoist? wrap? annotate? split?) that depend on context outside any one expression. For diff-only output on the small autofix set, use ImproveKotlinCompose.
  • org.openrewrite.kotlin.compose.FindArrayParameterOnComposable$KtRecipe
    • Find Array<T> parameters on @Composable functions
    • JVM arrays are mutable references — Compose's stability inferrer marks an Array<T> parameter unstable, forcing the composable to recompose every time the parent recomposes. Prefer ImmutableList<T> or a @Stable wrapper.
  • org.openrewrite.kotlin.compose.FindBoxWithSingleChild$KtRecipe
    • Find Box \{ … \} with a single child
    • A Box \{ OneChild() \} adds a layout node and a measurement pass for no compositional benefit — the child could be invoked directly with the same Modifier. Either pull the modifier onto the child or use the explicit Box placement APIs if alignment is doing real work.
  • org.openrewrite.kotlin.compose.FindByRememberWithoutMutableState$KtRecipe
    • Find by remember \{ … \} delegations whose body isn't a mutableStateOf
    • by remember \{ … \} pairs with a MutableState<T> so the property delegates its read/write through the snapshot system. If the remember \{ \} body returns a plain T, the by does nothing useful — and is a strong hint the author forgot to wrap the value in mutableStateOf(...) or derivedStateOf \{ … \}.
  • org.openrewrite.kotlin.compose.FindCanvasInComposable$KtRecipe
    • Find Canvas \{ … \} blocks inside a @Composable
    • Canvas \{ drawXxx(...) \} re-runs the draw lambda on every recomposition; allocating Paint, Path, or Brush instances inside the lambda creates GC pressure that shows up as jank. Review for hoistable allocations (remember \{ Paint().apply \{ … \} \}) and for drawWithCache \{ … \} opportunities.
  • org.openrewrite.kotlin.compose.FindCardWithSingleChild$KtRecipe
    • Find Card \{ OneChild() \} patterns
    • A Card \{ OneChild() \} allocates a layout node and an elevation surface for exactly one composable. If the child already styles itself (Modifier.background/Modifier.shadow), the Card is decorative duplication — pull the styling into the child's Modifier chain.
  • org.openrewrite.kotlin.compose.FindColumnWithSingleChild$KtRecipe
    • Find Column \{ … \} with a single child
    • A Column \{ OneChild() \} allocates a layout node and runs the column measurement to position exactly one child. Either remove the column or replace with Box(modifier = m) if the column's verticalArrangement actually does work the parent isn't.
  • org.openrewrite.kotlin.compose.FindComposableCallInNonComposableLambda$KtRecipe
    • Find @Composable calls inside non-@Composable lambda parameters
    • A @Composable function called from inside a non-Composable lambda (e.g., a forEach \{ \}) won't enter the composition tree correctly — the function executes but its emitted nodes don't get tracked for invalidation. Either move the call out of the lambda, or use a Compose-aware iterator (items(list) \{ … \}).
  • org.openrewrite.kotlin.compose.FindComposableConventionSmells$KtRecipe
    • Find Compose function-naming conventions
    • Function-naming patterns the Compose API guide calls out: non-@Composable functions that use composable APIs (remember/LaunchedEffect/rememberCoroutineScope) without the annotation.
  • org.openrewrite.kotlin.compose.FindComposableLambdaParamMissingDefault$KtRecipe
    • Find @Composable functions with a content lambda parameter not defaulted to \{\}
    • By Material/Compose convention, content slot lambdas (content: @Composable () -> Unit) default to \{\} so callers can compose the function without supplying a body when they only want the surrounding chrome. Flag content slots without defaults so the API gets the convention-conforming overload.
  • org.openrewrite.kotlin.compose.FindComposableMissingModifierParam$KtRecipe
    • Find @Composable functions without a Modifier parameter
    • The Compose API guideline says every composable that emits UI should accept a Modifier parameter (named modifier, defaulted to Modifier) so callers can size, layout, and decorate without subclassing. Flag composables that emit content but expose no Modifier slot.
  • org.openrewrite.kotlin.compose.FindComposableWithReturnValue$KtRecipe
    • Find @Composable fun … (): X functions returning a non-Unit value
    • A @Composable function that returns a value either emits UI as a side-effect (anti-pattern: invocation order is now load-bearing) or computes a derived value that should be a @ReadOnlyComposable. Mark explicit value-returning composables @ReadOnlyComposable so callers know they don't emit, or split into emitting-vs-returning pairs.
  • org.openrewrite.kotlin.compose.FindComposeApiDesignIssues$KtRecipe
    • Find Compose API design issues
    • Composable functions that don't follow the Compose API guidelines: lowercase name (UI emitters should be PascalCase), non-Unit return without @ReadOnlyComposable, content slot without a default \{\}, missing Modifier parameter, and @Composable invocations from non-Composable lambdas.
  • org.openrewrite.kotlin.compose.FindComposeEffectIssues$KtRecipe
    • Find Compose effect handler issues
    • Effect handlers misused: LaunchedEffect(Unit)/LaunchedEffect(true) placeholder keys, DisposableEffect lambdas missing onDispose \{ \}, rememberCoroutineScope() mis-placed inside a lambda, LaunchedEffect inside loops, and side-effecting calls (logging, File) inside the composable body rather than an effect block.
  • org.openrewrite.kotlin.compose.FindComposeLayoutIssues$KtRecipe
    • Find Compose layout hierarchy smells
    • Layout containers that exist for no compositional benefit: Box/Column/Row wrapping a single child, and LazyColumn/LazyRow items missing a stable key (which churns composition state on reorder).
  • org.openrewrite.kotlin.compose.FindComposeModifierIssues$KtRecipe
    • Find Compose Modifier smells
    • Modifier-chain shapes that allocate per recomposition (Modifier.padding(...) inline), branch with structurally distinct chains (if (x) Modifier.foo() else Modifier), or stack fillMax/padding in a layout-changing order. Each match needs the author's intent to fix correctly.
  • org.openrewrite.kotlin.compose.FindComposeNavigationIssues$KtRecipe
    • Find Compose navigation / coroutine misuse
    • Calls that need to be wrapped in an effect handler or event handler: navController.navigate(...) from a composable body, scope.launch \{ … \} outside LaunchedEffect, lifecycle-naive collectAsState instead of collectAsStateWithLifecycle.
  • org.openrewrite.kotlin.compose.FindComposeRememberIssues$KtRecipe
    • Find Compose remember key issues
    • remember \{ … \} calls where the keys do not align with the values the block reads — keyless remember that captures changing variables, and remember \{ mutableStateOf(call()) \} candidates for derivedStateOf.
  • org.openrewrite.kotlin.compose.FindComposeStabilityIssues$KtRecipe
    • Find Compose stability issues
    • Surface declarations where Compose's stability inferrer will refuse to mark a parameter, property, or class as stable: MutableList/MutableMap/MutableSet parameters, read-only List parameters, @Stable/@Immutable annotations applied to classes with var fields, and data class declarations holding List<T> properties.
  • org.openrewrite.kotlin.compose.FindComposeStateReadIssues$KtRecipe
    • Find Compose state read/write issues
    • Patterns where a MutableState/State is read or constructed in a way that loses the snapshot value: explicit .value reads, bare mutableStateOf without remember, class-field state ownership, missing derivedStateOf, transient collection allocations.
  • org.openrewrite.kotlin.compose.FindComposeViewModelIssues$KtRecipe
    • Find Compose ViewModel wiring issues
    • ViewModel acquisition inside composables — hiltViewModel<X>() and viewModel<X>() — and StateFlow exposure: MutableStateFlow without an asStateFlow() read-only view.
  • org.openrewrite.kotlin.compose.FindConditionalModifier$KtRecipe
    • Find if (x) Modifier.foo() else Modifier patterns
    • if (cond) Modifier.foo() else Modifier returns two structurally different Modifier chains, breaking memoization on the consumer. Use Modifier.then(if (cond) Modifier.foo() else Modifier) or Modifier.composed \{ if (cond) padding(8.dp) else this \} so the consumer sees a single stable reference.
  • org.openrewrite.kotlin.compose.FindContextParameterOnComposable$KtRecipe
    • Find android.content.Context parameters on @Composable functions
    • Passing a Context into a composable couples it to the activity instance and makes the function harder to preview/test. Use LocalContext.current inside the composable instead — it works through the composition tree and is preview-safe.
  • org.openrewrite.kotlin.compose.FindCoroutineLaunchInComposableBody$KtRecipe
    • Find scope.launch \{ … \} calls inside a @Composable body
    • scope.launch \{ … \} in a @Composable body starts a new coroutine on every recomposition — none of them get cancelled until the scope dies. Use LaunchedEffect(key) \{ … \}, which is automatically cancelled and restarted by the composition's lifecycle.
  • org.openrewrite.kotlin.compose.FindCoroutineLaunchInsideLaunchedEffectInLoop$KtRecipe
    • Find for (...) \{ LaunchedEffect(...) \{ … \} \} patterns
    • A LaunchedEffect inside a loop creates a separate coroutine per iteration. That is rarely the intended structure — it is usually a mis-placement of effect logic. Prefer a single LaunchedEffect(keys = arrayOf(...)) \{ for (...) \{ … \} \} or restructure the loop to live inside the effect.
  • org.openrewrite.kotlin.compose.FindDataClassWithListProperty$KtRecipe
    • Find data class declarations with List<T> properties
    • When a data class is passed to a @Composable and one of its properties is a kotlin.collections.List<T>, Compose marks the entire class unstable. Wrap the list in ImmutableList<T> from kotlinx.collections.immutable (or split the list out and remember it separately) so stability inference can prove the holder is @Stable.
  • org.openrewrite.kotlin.compose.FindDerivedStateOfCandidate$KtRecipe
    • Find remember \{ mutableStateOf(expensiveCall()) \} patterns
    • remember \{ mutableStateOf(expensiveCall()) \} evaluates the expression once and stores it — but if the expression depends on snapshot state, you want it to recompute when that state changes. derivedStateOf \{ expensiveCall() \} (inside a remember \{ \}) recomputes lazily only when its tracked reads invalidate, instead of either staling out or recomputing on every recomposition.
  • org.openrewrite.kotlin.compose.FindDisposableEffectMissingOnDispose$KtRecipe
    • Find DisposableEffect \{ … \} blocks missing an onDispose \{ \}
    • DisposableEffect's contract is to return a DisposableEffectResult from onDispose \{ … \} — without it, the compiler should reject the block, but easy mistakes (early return, wrong receiver) silently bypass cleanup. Confirm the final statement of every DisposableEffect lambda is an onDispose \{ \} call so resources are released on leave-the-composition.
  • org.openrewrite.kotlin.compose.FindDpAllocationInComposableBody$KtRecipe
    • Find n.dp allocations inside a @Composable body
    • Dp is an inline value class — most .dp accesses compile to a primitive. But certain platforms (older Kotlin, KMP non-JVM targets) box the value. In hot composables, prefer hoisting private val padding = 8.dp to file scope so the conversion runs once.
  • org.openrewrite.kotlin.compose.FindFlowCollectAsState$KtRecipe
    • Find Flow.collectAsState() calls — prefer collectAsStateWithLifecycle()
    • collectAsState() keeps collecting whenever the composition is alive, including while the host activity is stopped. collectAsStateWithLifecycle() (from androidx.lifecycle:lifecycle-runtime-compose) ties collection to the lifecycle owner, dropping subscription while in the background and freeing the upstream Flow from doing work nothing will display.
  • org.openrewrite.kotlin.compose.FindFlowParameterOnComposable$KtRecipe
    • Find Flow<T> / StateFlow<T> parameters on @Composable functions
    • Passing a Flow<T> into a @Composable shifts collection from a LaunchedEffect to the consumer — but if the caller re-creates the Flow per recomposition, collection restarts every time. Prefer collecting at the call site and passing the resulting State<T> (or T directly).
  • org.openrewrite.kotlin.compose.FindHardcodedColor$KtRecipe
    • Find Color(0xFF…) / Color.X literals inside @Composable
    • Hardcoded Color literals inside a composable bypass MaterialTheme.colorScheme.X, breaking light/dark theme adaptation and theming overrides. Move the literal into the theme (a ColorScheme extension or a top-level theme val) and read it via MaterialTheme.colorScheme at the call site.
  • org.openrewrite.kotlin.compose.FindHardcodedDesignTokens$KtRecipe
    • Find hardcoded color literals inside @Composable
    • Color(0xFF…) literals inside composables break theming and accessibility (light/dark). Hoist into MaterialTheme.colorScheme.*.
  • org.openrewrite.kotlin.compose.FindHiltViewModelInComposable$KtRecipe
    • Find hiltViewModel<X>() calls inside @Composable
    • hiltViewModel<MyViewModel>() inside a @Composable couples the screen-level dependency injection to that composable. That is the recommended pattern at navigation entry points, but flagged for review when the same ViewModel is injected from multiple composables (you'll get distinct instances per nav graph entry).
  • org.openrewrite.kotlin.compose.FindImmutableAnnotationOnMutableClass$KtRecipe
    • Find @Immutable on classes with var properties
    • @Immutable is the stronger sibling of @Stable: it promises that all public properties are observably unchangeable after construction. A var field is by definition observably changeable — Compose will assume it can skip recompositions safely and miss updates. Drop the annotation or convert the property to val (and a private backing var if needed).
  • org.openrewrite.kotlin.compose.FindInlineModifierConstruction$KtRecipe
    • Find Modifier.xxx() allocations inside a @Composable body
    • Each Modifier.padding(...)-style chain allocates a fresh Modifier instance, and a fresh Modifier defeats Compose's structural-equality skip — every recomposition allocates again and forces re-layout. Hoist the modifier into a remember \{ Modifier… \}, accept a Modifier parameter from the caller, or build static modifiers as top-level vals.
  • org.openrewrite.kotlin.compose.FindLambdaAsComposableParamWithoutNoinline$KtRecipe
    • Find lambda parameters on @Composable functions
    • Function-typed parameters are unstable from Compose's stability inferrer perspective unless the lambda reference is stable (e.g., function reference or rememberd). For frequently-recomposed composables, accept a (T) -> Unit and document caller responsibility, or fold the callback into a stable holder. Flag for review when the API is performance-sensitive.
  • org.openrewrite.kotlin.compose.FindLambdaCapturingMutableStateInItems$KtRecipe
    • Find lazy-list items(...) \{ … \} content lambdas that read a MutableState from the enclosing scope
    • When a LazyColumn/LazyRow content lambda reads a MutableState/State from the enclosing scope, every change to that state invalidates the entire item composition. Hoist the state into a per-item remember, or read it inside a child composable so only the affected item recomposes.
  • org.openrewrite.kotlin.compose.FindLaunchedEffectMultipleSuspendCalls$KtRecipe
    • Find LaunchedEffect bodies with several distinct suspend calls
    • A LaunchedEffect lambda that issues several distinct top-level suspend calls is usually doing two things: a long-running collector plus an unrelated kickoff. Split them into separate LaunchedEffects keyed independently so canceling one doesn't cancel the other on key changes.
  • org.openrewrite.kotlin.compose.FindLaunchedEffectWithTrueKey$KtRecipe
    • Find LaunchedEffect(true) \{ … \} blocks
    • LaunchedEffect(true) (or any literal true/false key) is a one-shot effect dressed up to look like it has a key. It is structurally identical to LaunchedEffect(Unit) but reads as if the author meant to pass a variable. Switch to Unit for clarity or pass the real dependency.
  • org.openrewrite.kotlin.compose.FindLaunchedEffectWithUnitKey$KtRecipe
    • Find LaunchedEffect(Unit) \{ … \} blocks
    • LaunchedEffect(Unit) \{ … \} runs exactly once per composition lifetime — that's intentional for one-shot startup work, but it is also the easiest spelling when the author wanted lifecycle-aware re-launch on a real key. Confirm Unit was intentional and not a placeholder for the actual dependencies the effect reads.
  • org.openrewrite.kotlin.compose.FindLaunchedEffectWithoutKey$KtRecipe
    • Find LaunchedEffect \{ … \} calls with no key argument
    • LaunchedEffect always takes at least one key — without one the call is a compile error (or silently rebound to a (suspend () -> Unit) overload in stubbed builds). Confirm a key is supplied; LaunchedEffect(Unit) \{ … \} is the canonical one-shot spelling.
  • org.openrewrite.kotlin.compose.FindLazyColumnDirectCall$KtRecipe
    • Find LazyColumn \{ … \} calls — verify items use stable keys
    • A LazyColumn \{ items(...) \{ … \} \} whose inner items call has no key = \{ … \} recomposes every visible row on every reorder/insertion. Audit the call to add a stable key.
  • org.openrewrite.kotlin.compose.FindLazyListItemMissingKey$KtRecipe
    • Find LazyColumn/LazyRow items(...) calls missing a key = \{ … \} argument
    • Without a stable key, LazyColumn/LazyRow indexes items by position. Inserting an item shifts every following index and Compose has to recompose every visible child, recreating their state. A stable key (typically an id) lets Compose preserve composition state across reorderings and animations.
  • org.openrewrite.kotlin.compose.FindLazyRowDirectCall$KtRecipe
    • Find LazyRow \{ … \} calls — verify items use stable keys
    • A LazyRow \{ items(...) \{ … \} \} whose inner items call has no key = \{ … \} recomposes every visible cell on every reorder. Audit the call to add a stable key.
  • org.openrewrite.kotlin.compose.FindLazyVerticalGridDirectCall$KtRecipe
    • Find LazyVerticalGrid \{ … \} calls — verify items use stable keys
    • A LazyVerticalGrid \{ items(...) \{ … \} \} whose inner items call has no key = \{ … \} recomposes every visible cell on every reorder. Audit the call to add a stable key.
  • org.openrewrite.kotlin.compose.FindLifecycleAwareFlowSmells$KtRecipe
    • Find lifecycle-naive flow / LiveData collection in Composables
    • Collectors and observers that keep running while the host activity is stopped: LiveData.observeAsState(), viewModel.uiState.collectAsState() (vs collectAsStateWithLifecycle()), and LiveData.observe(...) called directly from a @Composable.
  • org.openrewrite.kotlin.compose.FindListAsComposableParam$KtRecipe
    • Find @Composable functions with List/Map/Set parameters
    • kotlin.collections.List (and friends) are read-only views, not immutable types — a List<T> can be a MutableList<T> upcast, so Compose's stability inferrer marks the parameter unstable and re-invokes the composable on every parent recomposition. Use ImmutableList<T> from kotlinx.collections.immutable or wrap in a @Immutable data holder.
  • org.openrewrite.kotlin.compose.FindListOfInComposableBody$KtRecipe
    • Find listOf(...) / mapOf(...) / setOf(...) calls inside a @Composable
    • listOf(a, b) allocates a fresh List on every recomposition. If the composable downstream is @Stable and compares its inputs by reference, the new list defeats memoization. Hoist into a remember \{ listOf(a, b) \} or convert to an ImmutableList declared at file scope.
  • org.openrewrite.kotlin.compose.FindLiveDataObserveInComposable$KtRecipe
    • Find LiveData.observe(...) calls inside @Composable
    • LiveData.observe(lifecycleOwner, observer) is for Activity/Fragment code; inside a @Composable it registers a brand-new observer on every recomposition and never removes it. Use observeAsState() (or migrate to StateFlow and collectAsStateWithLifecycle()).
  • org.openrewrite.kotlin.compose.FindLongModifierChain$KtRecipe
    • Find Modifier.xxx().yyy()... chains longer than five operations
    • A Modifier chain with more than five operations is hard to read, hard to memoize, and often hides a hoist-into-a-named-Modifier opportunity. Extract the chain into a val styled = Modifier… declaration (ideally remembered at the call site) so the composable body reads as intent rather than plumbing.
  • org.openrewrite.kotlin.compose.FindLowercaseComposableFunction$KtRecipe
    • Find @Composable functions whose name starts with a lowercase letter
    • Compose convention: composables that emit UI use PascalCase to set them apart from regular Kotlin functions in IDE auto-complete and stack traces. Lowercase-named composables either should be renamed or, if they return a value rather than emit UI, marked @ReadOnlyComposable to signal they don't compose.
  • org.openrewrite.kotlin.compose.FindModifierClickableBeforeBackground$KtRecipe
    • Find Modifier.clickable \{ \}.background(...) chains
    • When clickable precedes background in a Modifier chain, the background paints on top of the touch target — the visible color is the background, but the ripple/feedback originates from the layer underneath, which usually isn't the look the author wanted. Place background(...) first and clickable \{ … \} last so the touch surface sits above the visual fill.
  • org.openrewrite.kotlin.compose.FindModifierFillMaxAndPaddingOrderSmell$KtRecipe
    • Find Modifier.fillMaxXxx().padding(...) chains
    • Modifier order matters: Modifier.fillMaxSize().padding(8.dp) fills the parent first and then insets — the visible content is smaller than the parent. Modifier.padding(8.dp).fillMaxSize() insets the available space and then fills it, producing a layout that hugs the padded box. The right order is intent-specific; flag chains for review.
  • org.openrewrite.kotlin.compose.FindModifierFillMaxWidthAfterFillMaxSize$KtRecipe
    • Find Modifier.fillMaxSize().fillMaxWidth() chains
    • fillMaxSize() already constrains both width and height — appending fillMaxWidth() is redundant and signals the author wasn't sure which size operator they wanted. Drop the second call or swap to the single operator that captures the intent.
  • org.openrewrite.kotlin.compose.FindModifierOrderingSmells$KtRecipe
    • Find Compose Modifier ordering smells
    • Modifier chains whose order produces a subtly wrong visual or interactive shape: clickable painted over by a later background, fillMaxWidth followed by padding (inset after the fill), and fillMaxSize immediately followed by a redundant fillMaxWidth/fillMaxHeight. Also surfaces Modifier.weight(...) calls outside a Row/Column scope, and overly long chains that could be hoisted into a named modifier.
  • org.openrewrite.kotlin.compose.FindModifierPaddingAfterFillMaxWidth$KtRecipe
    • Find Modifier.fillMaxWidth().padding(...) chains
    • fillMaxWidth() followed by padding(...) reserves the full width and then insets — the visible content is narrower than the parent. Most authors who write that chain meant padding(...).fillMaxWidth() so the inset comes first and the fill happens inside the inset region. The right order is intent-specific; flag for review.
  • org.openrewrite.kotlin.compose.FindModifierPaddingAllEqual$KtRecipe
    • Find padding(start = x, end = x, top = x, bottom = x) shorthand opportunities
    • When every named padding(...) argument carries the same value, padding(all = x) (or just padding(x)) communicates the uniform inset in a single token. Mixed-value padding(...) is fine; equal-on-all-sides is a shorthand candidate.
  • org.openrewrite.kotlin.compose.FindModifierPaddingHorizontalEqualToVertical$KtRecipe
    • Find padding(start = x, end = x, top = y, bottom = y) shorthand opportunities
    • Modifier.padding(start = 8.dp, end = 8.dp, top = 16.dp, bottom = 16.dp) reads as four independent insets but really means "8 horizontal, 16 vertical". The shorter padding(horizontal = 8.dp, vertical = 16.dp) says that intent up front and survives a future change to one axis without re-pairing the values.
  • org.openrewrite.kotlin.compose.FindModifierPaddingZero$KtRecipe
    • Find zero-valued Modifier.padding(...) calls
    • Modifier.padding(0.dp) allocates a PaddingValues and a layout pass to inset by zero — the call is a no-op in terms of layout but not at runtime. Drop the call (or split out the surrounding chain so the zero edge isn't expressed at all).
  • org.openrewrite.kotlin.compose.FindModifierShorthands$KtRecipe
    • Find Modifier.padding(...) shorthand opportunities
    • Named-argument padding(...) calls whose values reduce to a shorter spelling: equal start/end + equal top/bottom collapses to padding(horizontal = x, vertical = y); all-equal collapses to padding(all = x); all-zero is a removable no-op.
  • org.openrewrite.kotlin.compose.FindModifierWeightOutsideRowColumn$KtRecipe
    • Find Modifier.weight(...) calls outside a Row/Column scope
    • Modifier.weight(weight) is an extension on RowScope/ColumnScope — calling it elsewhere is a compile error in well-typed code, but stub builds and intrinsic-measurement hacks let mis-scoped calls slip through. Flag any weight(...) on a Modifier chain whose nearest enclosing scoped builder isn't a Row or Column.
  • org.openrewrite.kotlin.compose.FindMutableCollectionAsComposableParam$KtRecipe
    • Find @Composable functions with MutableList/MutableMap/MutableSet parameters
    • Compose's stability inferrer treats MutableList/MutableMap/MutableSet parameters as unstable — every recomposition compares by identity and re-invokes the composable even if no element changed. Use kotlinx.collections.immutable.ImmutableList (or wrap in a @Stable class) so equality checks short-circuit and recomposition is skipped.
  • org.openrewrite.kotlin.compose.FindMutableStateInClassField$KtRecipe
    • Find mutableStateOf(...) stored in a class field
    • private val x = mutableStateOf(...) at class scope ties the state to the lifetime of the enclosing class — ViewModel scope is fine, but UI-layer classes shouldn't be holding state for the composable. Hoist into a ViewModel or accept the state from the caller via parameters so recomposition and lifecycle agree on ownership.
  • org.openrewrite.kotlin.compose.FindMutableStateInComposableWithoutRemember$KtRecipe
    • Find bare mutableStateOf(...) inside @Composable without remember \{ \}
    • A bare mutableStateOf(...) call inside a @Composable allocates a fresh MutableState on every recomposition, throwing away the previous value. Wrap in remember \{ mutableStateOf(...) \} so the snapshot survives recomposition (or hoist into a ViewModel if it needs to survive process death).
  • org.openrewrite.kotlin.compose.FindNavigateInComposableBody$KtRecipe
    • Find NavController.navigate(...) calls in a @Composable body
    • navController.navigate(...) called directly in the body fires on every recomposition, leading to navigation loops or back-stack corruption. Wrap in a LaunchedEffect(key) \{ … \} keyed by the condition that should trigger the navigation, or move the call into an event handler (onClick = \{ … \}).
  • org.openrewrite.kotlin.compose.FindNonComposableUsingComposableApis$KtRecipe
    • Find non-@Composable functions calling @Composable-only APIs
    • A function that calls LaunchedEffect/remember/rememberCoroutineScope but isn't annotated @Composable itself is a compile error in well-typed code, but suppressors and hand-rolled annotations let it slip through. Add @Composable to the function declaration so the contract is explicit.
  • org.openrewrite.kotlin.compose.FindObserveAsState$KtRecipe
    • Find LiveData.observeAsState() calls — prefer collectAsStateWithLifecycle()
    • observeAsState() ties subscription to the composition, not to the lifecycle owner — collection keeps running while the host activity is STOPPED. Migrate to StateFlow and collectAsStateWithLifecycle() (or stay on LiveData and use androidx.lifecycle.compose.observeAsState, which is lifecycle-aware in newer versions).
  • org.openrewrite.kotlin.compose.FindPublicMutableStateFlowProperty$KtRecipe
    • Find non-private MutableStateFlow properties
    • Convention pattern: private val _state = MutableStateFlow(...); val state: StateFlow<T> = _state.asStateFlow(). A non-private MutableStateFlow property exposes the writable handle to consumers — anyone who can read it can also call .value = … or tryEmit(...), breaking the unidirectional-data-flow contract the ViewModel is supposed to enforce. Make the field private and expose a read-only StateFlow view.
  • org.openrewrite.kotlin.compose.FindRecompositionSmells$KtRecipe
    • Find Compose recomposition smells
    • Recomposition-related patterns whose default behavior surprises authors: LazyColumn/LazyRow/LazyVerticalGrid whose inner items(...) calls have no key (composition state churns on reorder), and lazy-list content lambdas that read snapshot state from the enclosing scope (every state change invalidates the entire list).
  • org.openrewrite.kotlin.compose.FindRememberCoroutineScopeInLambda$KtRecipe
    • Find rememberCoroutineScope() calls inside a lambda
    • rememberCoroutineScope() must be called from a composition-aware position — inside a lambda (like an onClick) it's a compile error. The recipe surfaces such mis-positioned calls so they migrate to the composable body proper.
  • org.openrewrite.kotlin.compose.FindRememberMutableListOfWithoutMutableState$KtRecipe
    • Find remember \{ mutableListOf<T>() \} patterns
    • remember \{ mutableListOf<T>() \} survives recomposition but mutations to the list are invisible to Compose — adding an item won't trigger a re-render of any consumer that reads the list. Use remember \{ mutableStateListOf<T>() \} (or hoist to mutableStateListOf<T>() at file scope) so writes register as snapshot writes.
  • org.openrewrite.kotlin.compose.FindRememberNoKeys$KtRecipe
    • Find remember \{ … \} calls with no keys
    • remember \{ … \} with no key arguments caches once per call site forever. If the block references variables that vary between recompositions, the cache holds a stale value. Either pass the referenced variables as keys (remember(input) \{ … \}) or — if the value really is invariant — leave a comment justifying it.
  • org.openrewrite.kotlin.compose.FindRememberWithUnstableKey$KtRecipe
    • Find remember(mutableListOf(...), …) and similar unstable-key calls
    • A remember(key, calc) whose key is a fresh allocation — mutableListOf(...), arrayOf(...), listOf(...) — is structurally a new key on every recomposition. The cache resets every time, defeating the entire purpose of remember. Pass the underlying values that do survive recomposition (or stable references) as the keys.
  • org.openrewrite.kotlin.compose.FindRowWithSingleChild$KtRecipe
    • Find Row \{ … \} with a single child
    • A Row \{ OneChild() \} allocates a layout node and runs the row measurement to position exactly one child. Either remove the row or replace with Box(modifier = m) if the row's horizontalArrangement actually does work the parent isn't.
  • org.openrewrite.kotlin.compose.FindSideEffectAllocationsInBody$KtRecipe
    • Find java.io.File(...) allocations inside a @Composable body
    • Filesystem objects allocated inside a @Composable body get rebuilt on every recomposition. Even if the constructor is cheap, the I/O performed by callers (File.exists(), File.length()) often is not. Move the allocation into a remember \{ File(...) \} block or out of the composable entirely.
  • org.openrewrite.kotlin.compose.FindSideEffectInComposableBody$KtRecipe
    • Find logging calls in @Composable bodies
    • android.util.Log/println inside a @Composable body runs on every recomposition — often dozens of times during a single user interaction — producing log spam and disguising real telemetry. Move the call into a SideEffect \{ \} (or a LaunchedEffect(key) \{ \}) so it fires once per successful composition, or out of the composable entirely.
  • org.openrewrite.kotlin.compose.FindSideEffectSmells$KtRecipe
    • Find Compose effect-handler misuse
    • Effect calls whose shape mismatches the effect's contract: SideEffect(key) \{ \} (SideEffect takes no keys); LaunchedEffect \{ \} with no key (use LaunchedEffect(Unit)); LaunchedEffect lambdas with several distinct suspend calls that probably want splitting.
  • org.openrewrite.kotlin.compose.FindSideEffectWithKey$KtRecipe
    • Find SideEffect(key) \{ … \} calls
    • SideEffect \{ \} takes no keys — it runs after every successful composition. Passing an argument suggests the author meant LaunchedEffect(key) \{ \} (lifecycle-tied) or DisposableEffect(key) \{ … \} (cleanup-tied). Either drop the argument or switch to the keyed effect type.
  • org.openrewrite.kotlin.compose.FindStableAnnotationOnClassWithMutableCollection$KtRecipe
    • Find @Stable classes holding mutable-collection properties
    • An @Stable class with a MutableList/MutableMap/MutableSet property cannot uphold the contract: the collection can mutate without equals/hashCode reflecting the change, so Compose's skip-when-equal heuristic produces stale UI. Replace with ImmutableList/PersistentList or drop the @Stable annotation.
  • org.openrewrite.kotlin.compose.FindStableAnnotationOnMutableClass$KtRecipe
    • Find @Stable on classes with var properties
    • @Stable is a contract: callers may skip recomposition when input references compare equal, and the class promises that equals/hashCode reflect all observable state. A var property breaks both halves — the value can mutate without anyone updating the snapshot system, so the annotation lies and downstream @Composables silently skip required recompositions.
  • org.openrewrite.kotlin.compose.FindStateAndRememberSmells$KtRecipe
    • Find Compose state + remember misuse
    • State that does not survive recomposition the way the author intended: remember(unstableKey, …) whose key is itself a fresh allocation; by remember \{ … \} whose body isn't a MutableState (the delegate is a no-op); remember \{ mutableListOf(...) \} where the mutations bypass the snapshot system.
  • org.openrewrite.kotlin.compose.FindStateFlowDirectCollect$KtRecipe
    • Find viewModel.uiState.collectAsState() calls — confirm lifecycle-aware collection
    • viewModel.uiState.collectAsState() keeps the collector active while the host is in the background — collectAsStateWithLifecycle() is the lifecycle-aware analogue. Both work; the recipe surfaces the call so each ViewModel-collection site is verified rather than defaulted.
  • org.openrewrite.kotlin.compose.FindStateValueRead$KtRecipe
    • Find state.value reads inside a @Composable
    • Reading state.value works but loses the by delegate ergonomics — and worse, with remember \{ mutableStateOf(...) \} plus .value, it is easy to forget the remember and create a fresh MutableState per recomposition. Prefer val state by remember \{ mutableStateOf(...) \} so the type checker keeps the snapshot read implicit.
  • org.openrewrite.kotlin.compose.FindSurfaceWithSingleChild$KtRecipe
    • Find Surface \{ OneChild() \} patterns
    • A Surface \{ OneChild() \} wrapper that only sets a tonal elevation or color is rarely the right place to live — the same effect is achievable by passing Modifier.background(...) or Modifier.shadow(...) directly to the child. Audit single-child surfaces for redundancy.
  • org.openrewrite.kotlin.compose.FindUnnecessaryComposeWrappers$KtRecipe
    • Find single-child wrapper composables (Material 3)
    • Material 3 wrapper composables that add a layout node and a styling pass for exactly one child: Surface \{ OneChild() \} and Card \{ OneChild() \}. Audit for redundancy — the same styling can usually be expressed by passing Modifier.background/Modifier.shadow to the child.
  • org.openrewrite.kotlin.compose.FindViewModelInComposable$KtRecipe
    • Find viewModel<X>() calls inside @Composable
    • viewModel<X>() retrieves a ViewModel scoped to the nearest ViewModelStoreOwner. Inside a generic composable this couples the composable to the host's ViewModelStoreOwner provision — fine at screen entry points, surprising deep in a component tree. Flag to confirm intent.
  • org.openrewrite.kotlin.compose.ImproveKotlinCompose$KtRecipe
    • Apply Compose autofix rewrites
    • Autofix-only Compose bundle: promotes remember \{ mutableStateOf(emptyList/Map()) \} to the snapshot-aware mutableStateListOf / mutableStateMapOf containers. The bulk of Compose remediation is judgement-call work flagged by Compose — for diff-only output, use this recipe instead.
  • org.openrewrite.kotlin.compose.UseMutableStateListOf$KtRecipe
    • Find remember \{ mutableStateOf(emptyList<T>()) \} candidates for mutableStateListOf
    • remember \{ mutableStateOf(emptyList()) \} boxes the list in a MutableState, so writes require state.value = state.value + item. mutableStateListOf<T>() is a snapshot-aware list: add/remove register as snapshot writes and trigger recomposition for readers.
  • org.openrewrite.kotlin.compose.UseMutableStateMapOf$KtRecipe
    • Find remember \{ mutableStateOf(emptyMap<K, V>()) \} candidates for mutableStateMapOf
    • remember \{ mutableStateOf(emptyMap()) \} boxes the map in a MutableState, so writes require state.value = state.value + … (or a clone). mutableStateMapOf<K, V>() is a snapshot-aware map: direct put/remove calls register as snapshot writes and trigger recomposition for readers.
  • org.openrewrite.kotlin.compose.UseSpecializedComposeStateContainers$KtRecipe
    • Find remember \{ mutableStateOf(emptyList/Map()) \} candidates for snapshot-aware containers
    • Patterns like remember \{ mutableStateOf(emptyList()) \} box the collection in a MutableState — direct add/put calls bypass the snapshot system. mutableStateListOf<T>() / mutableStateMapOf<K, V>() are snapshot-aware containers whose mutations register as snapshot writes and notify readers.
  • org.openrewrite.kotlin.coroutines.Coroutines$KtRecipe
    • Modernize Kotlin coroutines code
    • Search-only recipes that surface coroutine-related issues IntelliJ IDEA 2026.1's coroutine inspections flag: structured-concurrency leaks, blocking on suspend contexts, Flow operator misorder, and hand-rolled sequencing where a canonical operator exists. Each match is a SearchResult for review — nothing is rewritten automatically.
  • org.openrewrite.kotlin.coroutines.FindAsyncImmediatelyAwait$KtRecipe
    • Find async \{ ... \}.await() patterns
    • async \{ … \}.await() on its own is structurally identical to withContext \{ … \} plus an extra Deferred allocation. Use withContext(ctx) \{ … \} (or just inline the body) — async is for concurrency, not sequencing.
  • org.openrewrite.kotlin.coroutines.FindBareCoroutineScopeCtor$KtRecipe
    • Find raw CoroutineScope(...) constructions
    • A CoroutineScope(...) constructed inline must be cancelled explicitly when its owner is torn down; nothing automatic ties it to a lifecycle. Prefer one of the framework scopes (viewModelScope, lifecycleScope) or own the cancellation explicitly in a Closeable.
  • org.openrewrite.kotlin.coroutines.FindBlockingOnSuspend$KtRecipe
    • Find blocking calls inside coroutine contexts
    • Java-monitor and runBlocking primitives that pin the dispatcher thread when invoked from a suspend function or coroutine builder. Each match needs to migrate to a coroutine-aware signaling primitive (delay, Channel, Mutex, CompletableDeferred).
  • org.openrewrite.kotlin.coroutines.FindCallbackFlowWithoutAwaitClose$KtRecipe
    • Find callbackFlow \{ \} blocks without an awaitClose \{ \} terminator
    • callbackFlow \{ \} must end with awaitClose \{ \} to suspend until the consumer cancels. Without it, the producer either completes immediately (silent drop) or throws — the same flow needs to register its cleanup hook in awaitClose \{ \}.
  • org.openrewrite.kotlin.coroutines.FindCoroutineScopeBuilderWithSingleLaunch$KtRecipe
    • Find coroutineScope \{ launch \{ ... \} \} with a single child
    • A coroutineScope \{ launch \{ … \} \} containing a single launch is equivalent to just running the launch body inline — the surrounding scope adds an allocation and a synchronization point with nothing to coordinate.
  • org.openrewrite.kotlin.coroutines.FindCoroutineSequencingSmells$KtRecipe
    • Find coroutine sequencing smells
    • Hand-rolled sequencing that would be cleaner with the canonical operators: map \{ it.await() \} (use awaitAll), forEach \{ it.join() \} (use joinAll), async \{ \}.await() (use withContext or inline), nested withContext, coroutineScope \{ launch \{ \} \} with a single child.
  • org.openrewrite.kotlin.coroutines.FindDebounceBeforeDistinctUntilChanged$KtRecipe
    • Find debounce(...).distinctUntilChanged() patterns
    • debounce already drops intermediate values within the window; adding distinctUntilChanged after it is redundant if the upstream is already deduped. Verify whether distinctUntilChanged belongs before debounce, where it can prevent re-firing the debounce window for repeated values.
  • org.openrewrite.kotlin.coroutines.FindFlowAntiPatterns$KtRecipe
    • Find Flow operator antipatterns
    • Flow chains where operator order, sharing configuration, or terminal placement undermines the intended behavior — collapsible map.map / filter.filter, flowOn past a terminal, stateIn/shareIn without an explicit timeout, Flow.collect inside @Composable, etc.
  • org.openrewrite.kotlin.coroutines.FindFlowCollectInsideCompose$KtRecipe
    • Find Flow.collect calls inside a @Composable
    • Flow.collect inside a @Composable ties collection to recomposition rather than the composable's lifecycle, leaking work on re-entry. Use collectAsStateWithLifecycle (Compose) or wrap with LaunchedEffect \{ flow.collect \{ … \} \}.
  • org.openrewrite.kotlin.coroutines.FindFlowFilterFilterChain$KtRecipe
    • Find Flow.filter \{ \} .filter \{ \} chains
    • Adjacent Flow.filter \{ \} calls do twice the work a combined predicate would do. Fold them into one filter \{ p1(it) && p2(it) \}.
  • org.openrewrite.kotlin.coroutines.FindFlowMapMapChain$KtRecipe
    • Find Flow.map \{ \} .map \{ \} chains
    • Two adjacent Flow.map \{ \} operators emit through two transform stages where one would do. Fold them into a single map, or use map \{ (a, b) -> … \} destructuring.
  • org.openrewrite.kotlin.coroutines.FindFlowOfWithVararg$KtRecipe
    • Find flowOf(...) calls — verify size
    • flowOf(items) materializes each item upfront — for hot data or large fanout, prefer flow \{ items.forEach \{ emit(it) \} \} or a Channel-backed flow to avoid the upfront vararg array.
  • org.openrewrite.kotlin.coroutines.FindFlowOnAfterTerminal$KtRecipe
    • Find Flow.flowOn placed after a terminal operator
    • flowOn(...) applies to upstream operators only. Placing it after a terminal like collect, first, or toList is a no-op — the producer dispatcher is whatever the collector inherits.
  • org.openrewrite.kotlin.coroutines.FindForEachJoin$KtRecipe
    • Find forEach \{ it.join() \} over List<Job>
    • Sequential .forEach \{ it.join() \} waits for each Job to complete before starting the next wait. joinAll() waits for all jobs concurrently with a single suspension point.
  • org.openrewrite.kotlin.coroutines.FindGlobalScopeActor$KtRecipe
    • Find GlobalScope.actor calls
    • GlobalScope.actor \{ \} is structurally identical to GlobalScope.launch: the actor coroutine has no parent and cannot be cancelled cooperatively. Use a lifecycle-scoped actor instead.
  • org.openrewrite.kotlin.coroutines.FindGlobalScopeAsync$KtRecipe
    • Find GlobalScope.async calls
    • GlobalScope.async \{ \} produces an orphan Deferred that has no parent in the structured-concurrency tree. Exceptions thrown from this coroutine are dropped until something await()s the result — and if nothing does, they vanish silently.
  • org.openrewrite.kotlin.coroutines.FindGlobalScopeLaunch$KtRecipe
    • Find GlobalScope.launch calls
    • GlobalScope.launch \{ \} is a fire-and-forget coroutine builder with no parent — it cannot be cancelled with the lifecycle that started it and leaks if the work outlives the screen/process. Prefer a scoped CoroutineScope tied to the lifecycle (viewModelScope, lifecycleScope, or an explicit scope cancelled in onCleared).
  • org.openrewrite.kotlin.coroutines.FindGlobalScopeProduce$KtRecipe
    • Find GlobalScope.produce calls
    • GlobalScope.produce \{ \} returns an unscoped ReceiveChannel that keeps running until its producer block returns. Anchor the channel to a scope owned by the surrounding lifecycle.
  • org.openrewrite.kotlin.coroutines.FindJobAsContext$KtRecipe
    • Find raw Job() allocations
    • Raw Job() calls usually feed a CoroutineScope(...) context, where they signal an intent to manage coroutine lifecycle manually. That manual lifecycle is easy to forget to cancel; prefer SupervisorJob() paired with a scope tied to the surrounding lifecycle (e.g. viewModelScope).
  • org.openrewrite.kotlin.coroutines.FindMapAwait$KtRecipe
    • Find map \{ it.await() \} over List<Deferred<T>>
    • Sequential .map \{ it.await() \} waits for each Deferred in turn and rethrows the first exception only after every earlier element completes. awaitAll() waits concurrently and rethrows immediately on the first failure.
  • org.openrewrite.kotlin.coroutines.FindMutableStateFlowNullable$KtRecipe
    • Find MutableStateFlow<T?>(null) declarations
    • Nullable MutableStateFlow<T?> is a common pattern for 'no value yet', but collapses the empty state and the value-is-null state into one. A SharedFlow<T> with replay = 0 and explicit tryEmit (or a sealed wrapper UiState.Empty | Loaded(T)) usually expresses intent more precisely.
  • org.openrewrite.kotlin.coroutines.FindObjectNotifyInSuspend$KtRecipe
    • Find Object.notify / notifyAll calls inside suspend functions
    • Monitor-based signaling (notify/notifyAll) doesn't compose with coroutine cancellation or structured concurrency. Replace with a Channel, MutableSharedFlow, or CompletableDeferred to wake suspended coroutines.
  • org.openrewrite.kotlin.coroutines.FindObjectWaitInSuspend$KtRecipe
    • Find Object.wait calls inside suspend functions
    • Object.wait() blocks the dispatcher thread on a monitor and cannot be interrupted by coroutine cancellation. Migrate to Channel/Flow/Mutex or a CompletableDeferred for cross-coroutine signaling.
  • org.openrewrite.kotlin.coroutines.FindRunBlockingInLaunch$KtRecipe
    • Find runBlocking calls inside a launch/async lambda
    • runBlocking inside an outer coroutine builder pins the dispatcher thread until the inner block returns, defeating the cooperative scheduling the outer builder set up. Inline the suspending body — you're already in a suspend context.
  • org.openrewrite.kotlin.coroutines.FindRunBlockingInSuspend$KtRecipe
    • Find runBlocking calls inside suspend functions
    • runBlocking inside a suspend function blocks the calling thread until the inner block finishes, defeating cooperative cancellation and pinning a thread that the dispatcher could otherwise reuse. From a suspend context, the block can be inlined or wrapped in withContext(...) instead.
  • org.openrewrite.kotlin.coroutines.FindShareInWithoutTimeout$KtRecipe
    • Find shareIn calls without a timeout-parameterized start
    • Same trap as stateIn — without an explicit WhileSubscribed(timeoutMillis), an unused upstream producer keeps running, and config changes (which momentarily drop subscriber counts) can either drop state or hold work alive.
  • org.openrewrite.kotlin.coroutines.FindStateInWithoutTimeout$KtRecipe
    • Find stateIn with SharingStarted.Eagerly or unparameterized start
    • stateIn(scope) with the default Eagerly start keeps the upstream Flow producing forever (no last-subscriber timeout). For UI state, WhileSubscribed(5_000) is the canonical setting — it survives configuration changes without leaking the producer.
  • org.openrewrite.kotlin.coroutines.FindStructuredConcurrencyLeaks$KtRecipe
    • Find structured-concurrency leaks
    • Coroutine builders that escape the structured-concurrency tree: GlobalScope builders, raw Job() / CoroutineScope(...) allocations, and suspendCoroutine calls that ignore cancellation. Each match is a SearchResult for review.
  • org.openrewrite.kotlin.coroutines.FindSuspendCoroutineWithoutCancellation$KtRecipe
    • Find suspendCoroutine calls
    • suspendCoroutine lacks cancellation hooks — if the surrounding coroutine is cancelled before the continuation resumes, the underlying callback work runs to completion uselessly. Switch to suspendCancellableCoroutine so the continuation block can register an invokeOnCancellation callback.
  • org.openrewrite.kotlin.coroutines.FindThreadSleepInSuspend$KtRecipe
    • Find Thread.sleep calls inside suspend functions
    • Thread.sleep parks the underlying dispatcher thread and ignores coroutine cancellation. From a suspend function, use delay(ms) — it suspends without blocking and integrates with structured cancellation.
  • org.openrewrite.kotlin.coroutines.FindWithContextInsideSameDispatcher$KtRecipe
    • Find nested withContext calls
    • A withContext(...) nested inside another withContext(...) rarely makes sense — the inner switch only matters if the dispatchers differ, and in either case the redundancy is worth a second look.
  • org.openrewrite.kotlin.functional.FindCatchAllException$KtRecipe
    • Find broad catch (e: Exception) / catch (e: Throwable) clauses
    • catch (e: Exception) catches almost everything — IllegalArgumentException, ConcurrentModificationException, even programmer-error NullPointerException. catch (e: Throwable) is worse: it catches OutOfMemoryError and kotlinx.coroutines.CancellationException. Each broad catch is a candidate for narrowing to the specific exceptions the surrounding code is prepared to handle.
  • org.openrewrite.kotlin.functional.FindCatchAndRethrowNewExceptionWithoutCause$KtRecipe
    • Find catch (e: Exception) \{ throw OtherException(...) \} without e as cause
    • Catching one exception and throwing a different one without passing the original as the cause argument loses the original stack trace at the throw site — debugging then starts from the wrapping exception with no breadcrumbs to the actual failure. Include e in the new exception's constructor (or use .initCause(e)).
  • org.openrewrite.kotlin.functional.FindCatchAndRethrowSameException$KtRecipe
    • Find catch (e: Exception) \{ throw e \} patterns
    • A catch whose only statement is throw e is a no-op: the same exception flows through the same way it would have without the try. Drop the entire try/catch (or, if there's a finally, switch to a try/finally).
  • org.openrewrite.kotlin.functional.FindCatchBindingUnusedException$KtRecipe
    • Find catch (e: Exception) clauses whose body never references e
    • If the catch body never reads the bound exception parameter — and there's still some statement that handles the recovery — the binding name is dead weight. Use catch (_: Exception) to make 'I have no use for the exception' explicit, and so future readers don't waste time looking for where e gets used.
  • org.openrewrite.kotlin.functional.FindCatchWithoutLogging$KtRecipe
    • Find non-empty catch blocks that neither log nor rethrow
    • A catch that handles the exception by silently absorbing it (without logging, without rethrowing, without storing it) loses every detail of the failure. Either log with the exception as the cause (log.error("context", e)), rethrow as a wrapping exception, or capture the exception into a Result/sealed result type.
  • org.openrewrite.kotlin.functional.FindNestedTryCatch$KtRecipe
    • Find try \{ \} catch \{ \} nested inside another try \{ \} catch \{ \}
    • A try nested inside another try usually means two failure modes are being handled at two different recovery points in the same control-flow tree. Pull each failure mode into its own helper function (or into a runCatching \{ \}.fold(...) chain) so the recovery strategy is visible at each level.
  • org.openrewrite.kotlin.functional.FindNullabilityErgonomics$KtRecipe
    • Find nullability idiom opportunities
    • Search-only bundle for nullable-handling if/else shapes that map to Kotlin idioms: if (x != null) x else default (use ?:), if (x != null) f(x) else null (use x?.let \{ f(it) \}), if (x == null) throw IllegalArgumentException (use requireNotNull), if (x == null) throw IllegalStateException (use checkNotNull), and if (p(x)) x else null (use x.takeIf \{ p(it) \}).
  • org.openrewrite.kotlin.functional.FindPrintStackTraceInCatch$KtRecipe
    • Find e.printStackTrace() calls inside a catch block
    • e.printStackTrace() writes to stderr — which in most server environments is either unread, unrotated, or both. Replace with a real logger call (log.error("context", e)) so the stack trace lands in the same place every other error in the application does.
  • org.openrewrite.kotlin.functional.FindResultErgonomics$KtRecipe
    • Find Result<T> API ergonomics opportunities
    • Search-only bundle for Result<T> call sites where a different operator would be clearer: if (result.isSuccess) … else … (use .fold(...)), Result.map \{ … \}.getOrThrow() (drop the Result wrapper or use .fold(...)), and getOrElse \{ default \} whose lambda ignores the failure (use getOrDefault(default)).
  • org.openrewrite.kotlin.functional.FindResultFoldImperative$KtRecipe
    • Find if (result.isSuccess) … else … patterns
    • Branching on Result.isSuccess / Result.isFailure and then unwrapping with getOrNull() / exceptionOrNull() is the imperative form of result.fold(onSuccess, onFailure). The .fold(...) form is total (the compiler verifies both branches are present) and reads as the value-producing expression it actually is.
  • org.openrewrite.kotlin.functional.FindResultGetOrElseIgnoringFailure$KtRecipe
    • Find Result.getOrElse \{ \} whose lambda ignores the failure parameter
    • result.getOrElse \{ default \} (lambda ignores its parameter) is exactly result.getOrDefault(default). The getOrDefault form makes the intent — 'a constant fallback, the exception type is irrelevant' — explicit in the call name.
  • org.openrewrite.kotlin.functional.FindResultGetOrThrow$KtRecipe
    • Find .getOrThrow() calls on a Result<T>
    • Result.getOrThrow() unwraps success or rethrows the captured failure. If the call site does that immediately after runCatching \{ … \}, the Result round-trip is pure ceremony — the same value with the same failure mode comes out of the bare expression. Prefer the bare expression, or use .fold(...) / .getOrElse \{ … \} to actually do something with the failure.
  • org.openrewrite.kotlin.functional.FindResultMapWithoutErrorHandling$KtRecipe
    • Find Result.map \{ \}.getOrThrow() chains
    • result.map \{ transform(it) \}.getOrThrow() is result.fold(::transform, \{ throw it \}) written long-hand — and .fold(...) keeps the transformation and the failure handling next to each other. If the failure branch really is 'rethrow', drop the Result wrapper entirely and put the transformation inside runCatching \{ \}.
  • org.openrewrite.kotlin.functional.FindRunCatchingForLogOnly$KtRecipe
    • Find runCatching \{ \}.onFailure \{ log… \} chains with no further handling
    • runCatching \{ … \}.onFailure \{ log.error("…", it) \} — when nothing follows the onFailure — succeeds-on-error rather than just observing. Often fine, but worth a glance: usually the caller still needs to know success/failure happened (return the Result, or chain .getOrElse \{ fallback \}).
  • org.openrewrite.kotlin.functional.FindRunCatchingGetOrNullDiscardingError$KtRecipe
    • Find runCatching \{ \}.getOrNull() chains
    • runCatching \{ … \}.getOrNull() silently swallows every failure and replaces it with null. The shape is fine for fire-and-forget side effects, but for value-producing calls you usually want at least an onFailure \{ \} hook for diagnostics, or .getOrElse \{ default \} so the failure is observable.
  • org.openrewrite.kotlin.functional.FindRunCatchingOnSuccessOnly$KtRecipe
    • Find runCatching \{ \}.onSuccess \{ … \} chains with no failure handler
    • runCatching \{ \}.onSuccess \{ … \} with nothing after it discards the failure side of the Result. The success block runs only on success; the failure case vanishes silently. Add a paired .onFailure \{ \} for diagnostics, or .fold(::onSuccess, ::onFailure) to make both cases explicit.
  • org.openrewrite.kotlin.functional.FindRunCatchingSmells$KtRecipe
    • Find runCatching \{ \} smells
    • Search-only bundle covering the most common runCatching \{ \} pitfalls: swallowing CancellationException, collapsing failures into null via .getOrNull(), discarding the Result in statement context, log-only handlers that drop the failure on the floor, .onSuccess \{ \} chains with no failure handler, and .getOrThrow() patterns that turn the Result round-trip into pure ceremony.
  • org.openrewrite.kotlin.functional.FindRunCatchingSwallowingCancellation$KtRecipe
    • Find runCatching \{ \} blocks that may swallow CancellationException
    • runCatching \{ \} catches every Throwable, including kotlinx.coroutines.CancellationException. Inside a coroutine that's a bug — CancellationException is the cooperative-cancellation signal, and swallowing it stops the coroutine from cancelling. Either avoid runCatching in suspending code, or rethrow with .onFailure \{ if (it is CancellationException) throw it \} before any other handling.
  • org.openrewrite.kotlin.functional.FindRunCatchingWithoutHandling$KtRecipe
    • Find runCatching \{ \} calls whose result is discarded
    • A runCatching \{ \} in statement context throws nothing and returns nothing — the Result<T> is allocated and dropped on the floor. If the intent was 'do this, but don't fail the caller', wrap with .onFailure \{ log(it) \}; if the intent was 'do this, ignoring exceptions', say so with try \{ … \} catch (_: Exception) \{ \} (or rethink whether to swallow at all).
  • org.openrewrite.kotlin.functional.FindThrowCatchSmells$KtRecipe
    • Find throw/catch shape smells
    • Search-only bundle for throw shapes inside catch blocks: bare-RuntimeException/Exception wrappers that discard contextual messages, useless catch \{ throw e \} blocks, and rethrows of new exception types that don't pass the caught exception as cause.
  • org.openrewrite.kotlin.functional.FindTryCatchReturningDefault$KtRecipe
    • Find try \{ x \} catch (e: Exception) \{ default \} patterns
    • A try whose catch returns a non-null default value maps directly to runCatching \{ x \}.getOrDefault(default) or .getOrElse \{ default \}. The latter is preferred when the default depends on the exception type.
  • org.openrewrite.kotlin.functional.FindTryCatchReturningNull$KtRecipe
    • Find try \{ x \} catch (e: Exception) \{ null \} patterns
    • Swallowing every exception into null discards diagnostic information and conflates 'no value' with 'I lost the cause'. runCatching \{ x \}.getOrNull() matches the shape, and .onFailure \{ … \} keeps a hook for diagnostics if you decide you want one later.
  • org.openrewrite.kotlin.functional.FindTryCatchSmells$KtRecipe
    • Find raw try/catch smells
    • Search-only bundle for try/catch shapes worth reviewing: collapse-to-null and collapse-to-default branches (candidates for runCatching \{ \}.getOrNull() / .getOrDefault(...)), empty catches that lose every detail of the failure, broad catch (Exception) / catch (Throwable) clauses, catches that absorb the exception without logging or rethrowing, e.printStackTrace() calls that should be logger calls, nested try/catch trees, and catch parameters that are bound but never read.
  • org.openrewrite.kotlin.functional.FindTryCatchSwallowingException$KtRecipe
    • Find empty catch blocks
    • An empty catch (catch (e: Exception) \{ \}) eats every exception and produces no record of it ever happening. Even a logger call records that something went wrong; an empty block makes the failure undebuggable.
  • org.openrewrite.kotlin.functional.FindUseCheckForState$KtRecipe
    • Find if (x == null) throw IllegalStateException(...) patterns
    • checkNotNull(x) \{ "…" \} is the state-precondition twin of requireNotNull: throws IllegalStateException when x is null and smart-casts to non-nullable on return. Use it for invariants about the object's state, leaving requireNotNull for arguments.
  • org.openrewrite.kotlin.functional.FindUseElvisForNullableDefault$KtRecipe
    • Find if (x != null) x else default patterns
    • if (x != null) x else default is the elvis operator written long-hand: x ?: default. The elvis form composes naturally with chains (a ?: b ?: c) and keeps the value derivation in a single expression.
  • org.openrewrite.kotlin.functional.FindUseLetForNullableMap$KtRecipe
    • Find if (x != null) f(x) else null patterns
    • if (x != null) f(x) else null is x?.let \{ f(it) \} written long-hand. The ?.let \{ \} form is more concise and (when f is a member call) collapses further to x?.f(...).
  • org.openrewrite.kotlin.functional.FindUseRequireForPrecondition$KtRecipe
    • Find if (x == null) throw IllegalArgumentException(...) patterns
    • Kotlin's requireNotNull(x) \{ "…" \} is the idiomatic precondition check: it throws IllegalArgumentException when x is null, smart-casts x to its non-nullable type after the call, and reads as the assertion it is. The if/throw form does the same thing without the smart-cast.
  • org.openrewrite.kotlin.functional.FindUseTakeIfForFilter$KtRecipe
    • Find if (predicate(x)) x else null patterns
    • if (predicate(x)) x else null is x.takeIf \{ predicate(it) \} written long-hand. The takeIf form keeps the value as the focal point and composes with ?.let \{ \} / elvis (x.takeIf \{ … \} ?: default).
  • org.openrewrite.kotlin.functional.FindWrappingExceptionInCatch$KtRecipe
    • Find throw RuntimeException(e) inside a catch block
    • Wrapping the caught exception in a bare RuntimeException/Exception discards the contextual message the catch site should be adding. Wrap with a domain-specific subclass and a real message (throw FetchFailedException("fetch profile for $userId", e)), or rethrow e directly if there's nothing to add.
  • org.openrewrite.kotlin.functional.Functional$KtRecipe
    • Modernize Kotlin functional / Result ergonomics
    • Search-only recipes that surface kotlin.Result / runCatching \{ \} smells and try/catch shapes that map cleanly to Kotlin idioms (.fold(...), .getOrNull(), .getOrDefault(...), ?:, ?.let \{ \}, requireNotNull, checkNotNull). Most of the actual rewrites involve moving statements between try-body / catch-body / Result-chain shapes, which the declarative rewrite \{ \} to \{ \} DSL doesn't model yet — so each match is a SearchResult for human review.
  • org.openrewrite.kotlin.idiom.FindAlsoWithMutation$KtRecipe
    • Find also \{ \} blocks that mutate the receiver
    • also \{ \} is for side effects that don't change the receiver — logging, validation, registering a callback. If the lambda mutates it, prefer apply \{ … \}, which is built for that and reads as configuration.
  • org.openrewrite.kotlin.idiom.FindApplyResultUnused$KtRecipe
    • Find ?.apply \{ \} whose result is discarded
    • x?.apply \{ … \} returns the receiver, but if the result is discarded the safe-call's return value adds nothing. Use x?.also \{ … \} or move the side effect out of apply, where the receiver isn't needed.
  • org.openrewrite.kotlin.idiom.FindApplyThisQualifier$KtRecipe
    • Find redundant this. inside apply \{ \} blocks
    • Inside apply \{ \}, every member access resolves against the implicit receiver — this.prop = v is just prop = v. Drop the qualifier; the whole point of apply is the implicit receiver.
  • org.openrewrite.kotlin.idiom.FindApplyWithoutMutation$KtRecipe
    • Find apply \{ \} blocks that perform no mutation
    • apply \{ \} is for configuring the receiver and returning it. If the block has no assignments or property writes, also \{ \} (which exposes the receiver as it and runs for side effects) or just inlining the call expresses the intent more clearly.
  • org.openrewrite.kotlin.idiom.FindCastAndNullableShapes$KtRecipe
    • Find cast and nullable-shape idioms
    • Unsafe as casts vs as?, takeIf \{ \}?.let \{ \} chains, takeUnless \{ !p \} double-negatives, deep ?. safe-call chains, explicit return null statements.
  • org.openrewrite.kotlin.idiom.FindCheckNotNullWithoutMessage$KtRecipe
    • Find checkNotNull(x) without an explanatory message
    • checkNotNull(x) throws an IllegalStateException with a generic message. Pass a lazy message — checkNotNull(x) \{ "state invariant: x ready after init" \} — to make the failure self-documenting.
  • org.openrewrite.kotlin.idiom.FindCollectionNullSafety$KtRecipe
    • Find collection null-safety idioms
    • listOf(...).filterNotNull() vs listOfNotNull(...), map \{ \}.filterNotNull() vs mapNotNull \{ \}, filter \{ it != null \}.map \{ it!! \} chains, firstOrNull patterns where single is intended, ?.x.orEmpty() mixed-call shapes.
  • org.openrewrite.kotlin.idiom.FindElvisThrowWithoutMessage$KtRecipe
    • Find x ?: throw SomeException() without a message
    • x ?: throw IllegalStateException() (no message arg) throws with a stack trace and no context. Pass an argument that explains why x was expected non-null at this point — error reports are the cheapest tool we have.
  • org.openrewrite.kotlin.idiom.FindFilterMapToMapNotNull$KtRecipe
    • Find filter \{ it != null \}.map \{ it!! \} chains
    • filter \{ it != null \}.map \{ it!! \} is the long form of mapNotNull \{ it \}. Both passes can be folded into a single filterNotNull (when no transform is needed) or mapNotNull (with a transform).
  • org.openrewrite.kotlin.idiom.FindFirstOrNullElvisError$KtRecipe
    • Find firstOrNull \{ \} ?: error(...) patterns
    • firstOrNull \{ p \}.let \{ it ?: error("missing") \} (or the ?: error form) is a manual single \{ p \}single throws when there's no match or more than one, which is usually the intended precondition.
  • org.openrewrite.kotlin.idiom.FindFirstOrNullOnNullableReceiver$KtRecipe
    • Find x?.firstOrNull() calls
    • x?.firstOrNull() produces null either when x is null OR when x is empty — the two cases collapse. Use x?.firstOrNull() ?: default only when both null-cases should yield the same fallback.
  • org.openrewrite.kotlin.idiom.FindIfElseNullDefault$KtRecipe
    • Find if (cond) value else null patterns
    • if (cond) value else null is value.takeIf \{ cond \} (when value doesn't depend on cond) — the extension makes the predicate's role visible at the call site.
  • org.openrewrite.kotlin.idiom.FindIfNotNullAssign$KtRecipe
    • Find if (x != null) y = x.foo() patterns
    • if (x != null) y = x.foo() followed by a default elsewhere reads as a hand-rolled y = x?.foo() ?: default. The elvis form keeps the value derivation in one expression.
  • org.openrewrite.kotlin.idiom.FindIfNotNullThenCall$KtRecipe
    • Find if (x != null) x.foo() that could use ?.
    • An if (x != null) x.foo() ladder reads as a manual nullable dispatch where Kotlin already has x?.foo(). The safe-call form is shorter and folds into expression position, where the if cannot.
  • org.openrewrite.kotlin.idiom.FindIfNullReturn$KtRecipe
    • Find if (x == null) return ... early-exit patterns
    • An if (x == null) return … reads as a manual null guard where Kotlin's x ?: return … says the same thing inline. The elvis form keeps the expression in line with its consumer and avoids a separate control-flow statement.
  • org.openrewrite.kotlin.idiom.FindIfNullThrow$KtRecipe
    • Find if (x == null) throw ... patterns
    • An if (x == null) throw … is the elvis-throw idiom written long-hand. x ?: throw … keeps the throw expression in line and reads as the assertion it is, rather than as a control-flow branch.
  • org.openrewrite.kotlin.idiom.FindLetAtStatementPosition$KtRecipe
    • Find ?.let \{ \} calls at statement position
    • x?.let \{ … \} at statement position discards its return value, behaving identically to x?.also \{ … \} but reading as a transform. also makes the side-effect-only intent explicit.
  • org.openrewrite.kotlin.idiom.FindLetElvis$KtRecipe
    • Find x?.let \{ \} ?: y patterns
    • x?.let \{ … \} ?: y mixes two intents — transform-when-present and fall-back — into a single expression. Inverts the natural reading order; consider an explicit if (x != null) … else y or pull the elvis branch out for clarity.
  • org.openrewrite.kotlin.idiom.FindLetIdioms$KtRecipe
    • Find let \{ \} ergonomics
    • ?.let \{ it \}, ?.let \{ it.foo() \} (including property reads), nested let ladders, let blocks at statement position, and the ?.let \{ \} ?: y pattern — all cases where let \{ \} adds shape without clarity.
  • org.openrewrite.kotlin.idiom.FindLetItCall$KtRecipe
    • Find ?.let \{ it.foo() \} that could use ?.foo()
    • x?.let \{ it.foo() \} is the long form of x?.foo() — the safe call already provides the non-null receiver, and the let introduces an unused binding. Drop .let \{ \} and call foo() directly.
  • org.openrewrite.kotlin.idiom.FindLetItIdentity$KtRecipe
    • Find ?.let \{ it \} no-ops
    • x?.let \{ it \} is structurally equivalent to x — the lambda introduces a binding and immediately returns it without transforming. Drop the .let \{ it \} call.
  • org.openrewrite.kotlin.idiom.FindLetWithFnOfIt$KtRecipe
    • Find obj.let \{ fn(it) \} where obj is non-null
    • When obj is non-nullable, obj.let \{ fn(it) \} only adds a binding around fn(obj). Save the lambda allocation and pass obj directly.
  • org.openrewrite.kotlin.idiom.FindListOfFilterNotNull$KtRecipe
    • Find listOf(a, b, c).filterNotNull() patterns
    • listOf(a, b, c).filterNotNull() materializes a list with null entries only to discard them. listOfNotNull(a, b, c) skips the nulls up front and returns the same result with one fewer allocation and one fewer pass.
  • org.openrewrite.kotlin.idiom.FindMapThenFilterNotNull$KtRecipe
    • Find map \{ ... \}.filterNotNull() chains
    • Two-pass map \{ … \}.filterNotNull() builds an intermediate list of nullable values. mapNotNull \{ … \} does both in one pass with a single allocation and propagates null returns naturally.
  • org.openrewrite.kotlin.idiom.FindNestedLet$KtRecipe
    • Find nested let \{ \} chains
    • a?.let \{ b?.let \{ … \} \} ladders for combining nullable values are clearer as a single if (a != null && b != null) … or a sealed pair. Two-level nesting is a code smell; three or more is almost always a refactor opportunity.
  • org.openrewrite.kotlin.idiom.FindNotNullAssertion$KtRecipe
    • Find !! non-null assertions
    • The !! operator throws a generic NullPointerException with no context. requireNotNull(x) \{ "explain why" \} or x ?: error("explain why") produces a message that points at the assumption.
  • org.openrewrite.kotlin.idiom.FindNotNullAssertionAsArgument$KtRecipe
    • Find !! passed as a function argument
    • foo(x!!) pushes the null-check onto the call site, where the function signature could just accept T? and document the contract. If foo must have a non-null x, prefer requireNotNull(x) \{ ... \} at the call site to produce a contextual error.
  • org.openrewrite.kotlin.idiom.FindNullAssertionPolish$KtRecipe
    • Find null-assertion polish opportunities
    • !! operators (including as arguments), requireNotNull / checkNotNull calls without a lazy message, and throw SomeException() without a contextual message inside an elvis.
  • org.openrewrite.kotlin.idiom.FindNullCheckIdioms$KtRecipe
    • Find manual null-check idioms
    • if (x != null) x.foo() / if (x == null) return … / if (x == null) throw … patterns where Kotlin's ?., ?: return, and ?: throw operators express the same intent in expression position.
  • org.openrewrite.kotlin.idiom.FindOrEmptyAfterSafeCall$KtRecipe
    • Find x?.something.orEmpty() patterns
    • x?.something.orEmpty() mixes safe-call and a null-coalescing extension. Either drop the ?. (if x is non-null) or chain through ?: emptyList() — the mix obscures which call is providing the fallback.
  • org.openrewrite.kotlin.idiom.FindRequireNotNullWithoutMessage$KtRecipe
    • Find requireNotNull(x) without an explanatory message
    • requireNotNull(x) throws an IllegalArgumentException with a generic message. Pass a lazy message — requireNotNull(x) \{ "x must be set before init" \} — so the stack trace explains the precondition.
  • org.openrewrite.kotlin.idiom.FindReturnNullExplicit$KtRecipe
    • Find return null in functions with nullable returns
    • An explicit return null is rarely the clearest expression of intent — usually the calling chain that produces the nullable can use ?: or mapNotNull to handle the no-value case at the boundary, not the inside.
  • org.openrewrite.kotlin.idiom.FindRunWithoutReceiverUse$KtRecipe
    • Find x.run \{ ... \} that doesn't use the receiver
    • run \{ \} is meaningful when the lambda references this; otherwise x.let \{ … \} (binding via it) or even no scope function at all is clearer. The runtime cost is identical — the value is purely readability.
  • org.openrewrite.kotlin.idiom.FindSafeCallChain$KtRecipe
    • Find long ?. safe-call chains
    • a?.b?.c?.d?.e chains beyond 3 hops indicate a domain object hierarchy with too many nullable boundaries — the chain hides which boundary is the real concern. Flatten with let blocks at the boundary that matters, or refactor to non-nullable intermediates.
  • org.openrewrite.kotlin.idiom.FindScopeFunctionSwaps$KtRecipe
    • Find scope-function correctness swaps
    • The 12 well-known scope-function correctness rules: with(x) used as a receiver expression vs x.run \{ \}, ?.apply \{ \} whose result is discarded vs ?.also \{ \}, apply \{ \} without mutation vs also \{ \}, also \{ \} with mutation vs apply \{ \}, redundant this. inside apply \{ \}, run without this references.
  • org.openrewrite.kotlin.idiom.FindSetOfFilterNotNull$KtRecipe
    • Find setOf(a, b, c).filterNotNull() patterns
    • Same shape as listOf(...).filterNotNull() — building a set with nulls and filtering them out. setOfNotNull(a, b, c) exists for exactly this case.
  • org.openrewrite.kotlin.idiom.FindTakeIfChainedLet$KtRecipe
    • Find x.takeIf \{ p \}?.let \{ ... \} patterns
    • x.takeIf \{ p \}?.let \{ … \} is a guard-then-transform expressed as two calls plus a safe-call. if (p) x.let \{ … \} else null (or x.takeIf(p)?.run \{ … \}) is the same in one operator without the implicit null bridge.
  • org.openrewrite.kotlin.idiom.FindTakeUnlessNegated$KtRecipe
    • Find takeUnless \{ !p \} (double-negative) patterns
    • takeUnless \{ !p \} is takeIf \{ p \} written with a double negative. Inverting takeUnless's predicate to positive form makes the intent immediate.
  • org.openrewrite.kotlin.idiom.FindUnsafeCast$KtRecipe
    • Find unsafe as casts
    • x as T throws ClassCastException on mismatch — there's no diagnostic, just the JVM exception. x as? T returns null on mismatch and folds into elvis/requireNotNull(...) with a better message.
  • org.openrewrite.kotlin.idiom.FindWithAsReceiver$KtRecipe
    • Find with(x) \{ ... \} used as an expression
    • with(x) \{ … \} returns the lambda result, which makes it interchangeable with x.run \{ … \}. The extension form chains better in safe-call sequences (x?.run \{ … \}) and reads as receiver-style throughout.
  • org.openrewrite.kotlin.idiom.NullSafetyAndScopeFunctions$KtRecipe
    • Apply Kotlin null-safety and scope-function idioms
    • Search-only recipes covering the two most-cited stylistic categories in IntelliJ's Kotlin inspections: null-safety (if (x != null) ladders, !!, requireNotNull polish, mapNotNull / listOfNotNull adoption, unsafe casts) and scope-function ergonomics (the 12 well-defined let/run/with/apply/also correctness rules). Each match is a SearchResult for review — nothing is rewritten automatically.
  • org.openrewrite.kotlin.interop.FindBufferedReaderLines$KtRecipe
    • Find bufferedReader().lines() calls
    • BufferedReader.lines() returns a Stream<String> that must be closed explicitly and consumed exactly once. Kotlin offers lineSequence() (lazy Sequence<String>) and useLines \{ sequence -> … \} (auto-closing) for the same use cases.
  • org.openrewrite.kotlin.interop.FindBuilderClass$KtRecipe
    • Find inner class Builder classes — default-args candidate
    • A Java-style nested class Builder mirrors the outer class fields with setters that return this, then a terminal build(). In Kotlin, a data class with default arguments composes with named-argument call syntax to express the same intent — usually with less code and no double maintenance.
  • org.openrewrite.kotlin.interop.FindClockAndTestabilityFriction$KtRecipe
    • Find non-injected clock / I/O calls (testability)
    • System.currentTimeMillis() / System.nanoTime() / LocalDateTime.now() and friends read the system clock implicitly. Each flagged call site is a candidate to receive a Clock (or the JDK java.time.Clock) so tests can advance time deterministically. Also flags BufferedReader.lines() — usually a lineSequence/useLines migration.
  • org.openrewrite.kotlin.interop.FindCompletableFutureReturn$KtRecipe
    • Find functions returning CompletableFuture<T>
    • Returning CompletableFuture<T> from Kotlin code obliges every caller to either .thenCompose chain or .await() through the kotlinx-coroutines-jdk8 bridge. A suspend fun foo(): T integrates with structured concurrency at the language level — keep the future shape only at the Java boundary.
  • org.openrewrite.kotlin.interop.FindCompletableFutureUsage$KtRecipe
    • Find CompletableFuture usage in Kotlin
    • CompletableFuture<T> is the JVM equivalent of a Deferred<T> or single-emission Flow<T>. In Kotlin, suspend fun/Flow integrate with structured concurrency, cancellation, and exception handling at the language level — prefer them inside Kotlin modules and bridge with kotlinx-coroutines-jdk8 at the boundary.
  • org.openrewrite.kotlin.interop.FindInteropFriction$KtRecipe
    • Find Java↔Kotlin interop friction points
    • Search-only bundle: every interop-flavored Find* recipe in this module. Covers Optional/Stream/Collections Java factories with Kotlin replacements, CompletableFuture/Rx/Reactor types with coroutine replacements, missing @Jvm* annotations on Kotlin-defined declarations Java callers reach for, and Java-style call shapes inside Kotlin source.
  • org.openrewrite.kotlin.interop.FindIterableForEach$KtRecipe
    • Find Java-style iterable.forEach(Consumer) calls
    • Iterable.forEach(Consumer<T>) is the Java-8 functional terminal; Kotlin source can use the same shape but the inline kotlin.collections.forEach is preferred — it doesn't allocate a Consumer and integrates with non-local return/break inside the lambda.
  • org.openrewrite.kotlin.interop.FindJavaGetterCallStyleInKotlin$KtRecipe
    • Find Java-style getX() calls in Kotlin source
    • Kotlin synthesizes property syntax for any Java getter that follows the getX()/isX() no-arg convention: obj.x reads the same value as obj.getX(). Writing the JVM-style call in Kotlin source obscures that — flag the call sites for migration to property access.
  • org.openrewrite.kotlin.interop.FindJavaIdiomsInKotlin$KtRecipe
    • Find Java-style call shapes inside Kotlin source
    • Search-only bundle of Java idioms that have idiomatic Kotlin equivalents at the call site: getX()/isX() getters where property syntax reads the same value, iterable.forEach(Consumer), requireNotNull(javaCall()) over platform types, manual getX/setX pairs, static-utility/constants object holders, Builder classes, and manual equals/hashCode.
  • org.openrewrite.kotlin.interop.FindJavaUtilArraysAsList$KtRecipe
    • Find Arrays.asList(...) calls
    • Arrays.asList(a, b, c) is the Java idiom for a small read-only List. In Kotlin, listOf(a, b, c) is more concise, properly read-only (the returned list is structurally immutable), and avoids leaking the array-backed quirk where set is allowed but add is not.
  • org.openrewrite.kotlin.interop.FindJavaUtilCollectionsEmptyList$KtRecipe
    • Find Collections.emptyList/Set/Map() calls
    • Collections.emptyList() (and its emptySet/emptyMap siblings) predate Kotlin's stdlib factories. emptyList<T>()/emptySet<T>()/emptyMap<K, V>() carry the same singletons, infer the type parameter at the call site, and don't drag the java.util.Collections import into Kotlin code.
  • org.openrewrite.kotlin.interop.FindJavaUtilCollectionsFriction$KtRecipe
    • Find java.util.Collections / Arrays factory usage inside Kotlin
    • Arrays.asList, Collections.emptyList, Collections.singletonList, and Collections.unmodifiableList (plus Set/Map siblings) all have idiomatic Kotlin stdlib replacements — listOf, emptyList<T>(), setOf, mapOf, and .toList()/.toSet()/.toMap() for immutable copies.
  • org.openrewrite.kotlin.interop.FindJavaUtilCollectionsSingleton$KtRecipe
    • Find Collections.singletonList/Set/Map(...) calls
    • Collections.singletonList(x) is the Java idiom for a one-element read-only list. listOf(x) returns the same shape with cleaner syntax and consistent overload selection for setOf/mapOf.
  • org.openrewrite.kotlin.interop.FindJavaUtilCollectionsUnmodifiable$KtRecipe
    • Find Collections.unmodifiableList/Set/Map(...) wrappers
    • Collections.unmodifiableList(x) wraps a collection in a view that throws on mutation. Kotlin's x.toList()/x.toSet()/x.toMap() produce a fresh immutable copy — safer in concurrent contexts and removes the runtime wrapper.
  • org.openrewrite.kotlin.interop.FindKotlinDefaultMethodInterface$KtRecipe
    • Find interface declarations with default-method bodies
    • An interface I \{ fun foo() \{ … \} \} exposes the body to Java callers only on JDK-8+ targets and only when the Kotlin compiler emits real default methods (@JvmDefault / -Xjvm-default=all). Without the right compiler flags, the body lives in a synthetic DefaultImpls and Java sees an abstract method.
  • org.openrewrite.kotlin.interop.FindLocalDateTimeNow$KtRecipe
    • Find LocalDateTime.now() / Instant.now() calls
    • LocalDateTime.now() (and its Instant/LocalDate/ZonedDateTime siblings) read the system clock implicitly. Inject a Clock and use the overload LocalDateTime.now(clock) so tests can advance time deterministically.
  • org.openrewrite.kotlin.interop.FindManualEqualsHashCode$KtRecipe
    • Find classes with manual equals/hashCode overrides — data class candidate
    • A non-data class that overrides both equals and hashCode over its own fields is the canonical shape data class exists for. Migrating gives equals/hashCode/toString/copy()/componentN() for free and removes the maintenance hazard of editing one of the two implementations.
  • org.openrewrite.kotlin.interop.FindManualGetterSetter$KtRecipe
    • Find manual getX() / setX(v) pairs in Kotlin classes
    • A class that exposes state through hand-rolled getX()/setX(v) is reimplementing what var x: T already provides — Kotlin generates the same accessors on the JVM. Migrate to a property and let the compiler emit the getter/setter pair.
  • org.openrewrite.kotlin.interop.FindMissingJvmAnnotations$KtRecipe
    • Find Kotlin declarations missing @Jvm* interop annotations
    • Search-only bundle of declarations where the JVM-visible API surface would benefit from one of the @JvmStatic / @JvmField / @JvmOverloads / @JvmName / @Throws annotations. Each match is a candidate for review — none should be applied blindly, but the absence is the primary friction Java callers feel.
  • org.openrewrite.kotlin.interop.FindMissingJvmFieldOnConst$KtRecipe
    • Find const val / companion val declarations missing @JvmField
    • A companion-object val without @JvmField is exposed to Java as Outer.Companion.getX() — a getter on a synthetic singleton. @JvmField lifts the property to a true static public final field on Outer, matching the Java idiom of named constants.
  • org.openrewrite.kotlin.interop.FindMissingJvmNameOnExtensionFunction$KtRecipe
    • Find top-level functions missing @JvmName
    • Top-level Kotlin functions (including extension functions) compile to static methods on a <FileName>Kt facade — Java callers see MyKotlinUtilsKt.bar(...) with a name the source file doesn't suggest. @JvmName("bar") on the function (or @file:JvmName("...") on the file) gives Java callers a name to bind against.
  • org.openrewrite.kotlin.interop.FindMissingJvmNameOnIsGetter$KtRecipe
    • Find val isX Boolean properties missing @get:JvmName
    • A Kotlin property named isEnabled compiles to a Java getter getIsEnabled() — not the idiomatic isEnabled(). @get:JvmName("isEnabled") (or naming the underlying property differently) restores the boolean-getter convention Java callers expect.
  • org.openrewrite.kotlin.interop.FindMissingJvmOverloadsOnDefaults$KtRecipe
    • Find functions with default parameters missing @JvmOverloads
    • A Kotlin function with default arguments compiles to a single JVM method — Java callers see only the all-parameters form. @JvmOverloads synthesizes overloads at every default-parameter boundary so Java callers can drop trailing arguments naturally.
  • org.openrewrite.kotlin.interop.FindMissingJvmStaticInCompanion$KtRecipe
    • Find companion object functions missing @JvmStatic
    • Without @JvmStatic, Java callers must reach companion-object functions through the synthetic Companion holder: Outer.Companion.foo(...). Adding @JvmStatic lifts the function to Outer.foo(...), matching what a Java reader expects from a class with static methods. Flag-only — sometimes the wrapper is intentional.
  • org.openrewrite.kotlin.interop.FindMissingThrowsAnnotation$KtRecipe
    • Find functions with throw of a checked exception missing @Throws
    • Kotlin doesn't track checked exceptions, so a function that throws IOException looks unchecked to a Java caller — try \{ … \} catch (IOException e) \{ … \} won't compile without @Throws(IOException::class) on the Kotlin declaration. Flag declarations that throw a Throwable whose Java analog is checked.
  • org.openrewrite.kotlin.interop.FindOptionalFriction$KtRecipe
    • Find java.util.Optional friction inside Kotlin
    • Bundle every Optional-related search: declarations that return or accept Optional<T>, Optional.ofNullable(...) constructions, and .isPresent/.get()/.orElse(...) consumption sites. Once an upstream switches Optional<T> to T?, each flagged call site collapses to a ?: / ?.let \{ … \} / !! expression.
  • org.openrewrite.kotlin.interop.FindOptionalGet$KtRecipe
    • Find Optional.get() / orElseThrow() calls
    • opt.get() is the unsafe unwrap that throws NoSuchElementException when the Optional is empty — the equivalent of Kotlin's !! on a nullable. Once the underlying value type is T?, the call site becomes value!! (or, better, a requireNotNull(value)).
  • org.openrewrite.kotlin.interop.FindOptionalIsPresent$KtRecipe
    • Find Optional.isPresent / isEmpty checks
    • opt.isPresent and opt.isEmpty are the Optional-flavored analogs of x != null and x == null. Once the upstream returns T? instead of Optional<T>, the check collapses to a Kotlin null comparison plus a smart-cast.
  • org.openrewrite.kotlin.interop.FindOptionalOfNullable$KtRecipe
    • Find Optional.ofNullable(...) calls
    • Optional.ofNullable(x) is the conversion T? -> Optional<T> — the very wrapping Kotlin's null type system was designed to make unnecessary. Inside Kotlin code, return x and let ?:/?.let \{ … \} express the absent-value branch directly.
  • org.openrewrite.kotlin.interop.FindOptionalOrElse$KtRecipe
    • Find Optional.orElse(...) calls
    • opt.orElse(default) is the Optional version of value ?: default. Once the producer returns T? directly, the elvis operator reads more naturally and produces tighter bytecode.
  • org.openrewrite.kotlin.interop.FindOptionalParam$KtRecipe
    • Find function parameters typed Optional<T>
    • Taking Optional<T> as a parameter is strictly weaker than T? — every caller wraps the same value in an Optional, the function unwraps it, and the type system stops helping with null checking. The nullable parameter form composes with default arguments and ?./?: operators.
  • org.openrewrite.kotlin.interop.FindOptionalReturn$KtRecipe
    • Find functions returning Optional<T>
    • A Kotlin function that returns Optional<T> forces every caller into a .isPresent/.get() dance the language already expresses with T?. Returning the nullable type instead lets the call site use ?:, let, and smart-casts directly.
  • org.openrewrite.kotlin.interop.FindOptionalUsage$KtRecipe
    • Find java.util.Optional usage in Kotlin
    • Kotlin already models the absent-value case with the nullable type system (T?). Optional<T> is a JVM-only crutch that's worth keeping at the Java boundary only — converting Kotlin-internal Optional usage to T? improves null-safety and removes one wrapper allocation per call.
  • org.openrewrite.kotlin.interop.FindReactiveInteropFriction$KtRecipe
    • Find reactive-framework return types in Kotlin
    • RxJava's Observable/Flowable/Single/Maybe/Completable and Reactor's Mono/Flux predate Kotlin coroutines. Each match is a candidate for migration to suspend fun (single-shot) or Flow<T> (stream); the corresponding kotlinx-coroutines-rx*/-reactor adapters cover the boundary to downstream Java callers.
  • org.openrewrite.kotlin.interop.FindReactorPublisherInKotlin$KtRecipe
    • Find Reactor Mono/Flux returns in Kotlin
    • Project Reactor's Mono<T>/Flux<T> are the Spring-WebFlux reactive types. Inside Kotlin code the canonical shape is suspend fun (for Mono) and Flow<T> (for Flux); kotlinx-coroutines-reactor provides the boundary adapters for downstream Reactor APIs.
  • org.openrewrite.kotlin.interop.FindRequireNotNullOnJavaCall$KtRecipe
    • Find requireNotNull(javaCall()) patterns
    • requireNotNull(javaApi.something()) is the safe-conversion idiom when a Java API returns an unannotated reference (platform type T!). Once the underlying API is annotated @Nullable/@NotNull (or migrated to Kotlin), the wrapper either becomes javaApi.something()!! or disappears entirely.
  • org.openrewrite.kotlin.interop.FindRequiresOptInOnExperimentalApi$KtRecipe
    • Find @RequiresOptIn annotation declarations
    • @RequiresOptIn marks an annotation as a feature opt-in marker — every caller of an annotated declaration must acknowledge the experimental status via @OptIn(...). The marker itself is a stability contract worth surfacing for review whenever a new Kotlin-defined API claims experimental status.
  • org.openrewrite.kotlin.interop.FindRxObservableInKotlin$KtRecipe
    • Find io.reactivex.Observable/Flowable/Single/Maybe usage in Kotlin
    • RxJava's reactive types predate Kotlin coroutines. Flow<T> covers cold-stream Observable/Flowable, suspend fun covers Single/Maybe, and kotlinx-coroutines-rx2/-rx3 bridges the interop boundary. Inside Kotlin code, migrate to the coroutine equivalent.
  • org.openrewrite.kotlin.interop.FindStaticHolderObject$KtRecipe
    • Find object Constants \{ const val A = ... \} static-constants holders
    • An object Constants whose body is exclusively const val declarations is a holder for compile-time constants. Promote each const val to a top-level declaration — both forms inline identically at the JVM bytecode level, but the top-level form is one import shorter at every call site.
  • org.openrewrite.kotlin.interop.FindStaticUtilObject$KtRecipe
    • Find object Utils \{ fun foo() = ... \} static-utility holders
    • An object Utils whose members are all functions (no state) is the Kotlin spelling of a Java static-utility class. Promote the functions to top-level — they're indexable, importable directly, and don't carry the synthetic singleton-load overhead Java callers see.
  • org.openrewrite.kotlin.interop.FindStreamCollectorsToList$KtRecipe
    • Find stream.collect(Collectors.toList()) calls
    • The collect(Collectors.toList()) terminal materializes a Stream into a List. In Kotlin source, the natural shape is iterable.toList() (eager) or sequence.toList() (lazy) — both avoid the Collector machinery and read at a glance.
  • org.openrewrite.kotlin.interop.FindStreamFilterMap$KtRecipe
    • Find stream.filter(...).map(...) chains
    • A filter().map() chain on Stream is structurally identical to the same chain on Iterable/Sequence — the Stream machinery just adds Collector requirements at the terminal. Migrate to Kotlin collections; if laziness matters, use asSequence() once at the head.
  • org.openrewrite.kotlin.interop.FindStreamFriction$KtRecipe
    • Find java.util.stream.Stream friction inside Kotlin
    • Bundle every Stream-related search: declarations returning Stream<T>, Collectors.toList() terminals, filter.map chains, and Stream.of constructions. Each match has a Kotlin equivalent in Sequence/Iterable/Flow that's idiomatic at the same call site.
  • org.openrewrite.kotlin.interop.FindStreamOfCall$KtRecipe
    • Find Stream.of(...) calls
    • Stream.of(...) is a varargs-to-Stream constructor used to bootstrap a Stream pipeline. In Kotlin, sequenceOf(...) (lazy) or listOf(...) (eager) cover the same uses without committing to the Stream type at the boundary.
  • org.openrewrite.kotlin.interop.FindStreamReturn$KtRecipe
    • Find functions returning java.util.stream.Stream<T>
    • Stream<T> is the Java 8 lazy-pipeline type — single-use, no built-in cancellation, only consumable through collect. In Kotlin, Sequence<T> is the equivalent for lazy iterable pipelines; List<T> and Flow<T> cover the eager and async-lazy cases respectively.
  • org.openrewrite.kotlin.interop.FindSystemCurrentTimeMillis$KtRecipe
    • Find System.currentTimeMillis() calls
    • Direct System.currentTimeMillis() calls are convenient but couple the call site to wall-clock time, making tests deterministic only by mocking the whole class. Inject a Clock (or, on JDK 8+, java.time.Clock) and read time through it.
  • org.openrewrite.kotlin.interop.ImproveKotlinInterop$KtRecipe
    • Apply Java↔Kotlin interop rewrites
    • Autofix-only interop bundle: collapses Optional.of(x).get() round-trips that have a direct value equivalent. Excludes the search-only Find* recipes (Optional / CompletableFuture / Stream / Collections factories, Jvm-annotation gaps, Java-style call shapes, reactive return types) — for diff-only output, use this recipe instead.
  • org.openrewrite.kotlin.interop.Interop$KtRecipe
    • Improve Java↔Kotlin interop ergonomics
    • Opinionated bundle of every interop recipe in this module: the Optional.of(x).get() collapse plus search-only flags for Java idioms that have first-class Kotlin replacements (Optional, CompletableFuture, Stream, Collections factories, @Jvm*-annotation gaps, Java-style call shapes, reactive return types, non-injected clocks). For diff-only output, use ImproveKotlinInterop.
  • org.openrewrite.kotlin.interop.UseValueForOptionalOfGet$KtRecipe
    • Use x instead of Optional.of(x).get()
    • Optional.of(x).get() is a JVM-style round-trip that's equivalent to x. In Kotlin you'd model the same thing with a non-nullable x directly, and Java callers already see the same value via the cross-language binding.
  • org.openrewrite.kotlin.logging.FindCompanionLoggerWithoutPrivate$KtRecipe
    • Find companion-object loggers missing private
    • A companion-object val log = LoggerFactory.getLogger(...) without private is exposed to Java callers as Foo.Companion.getLog() — they can mutate the logger reference (well, not the val, but the visibility is wider than needed). Mark it private.
  • org.openrewrite.kotlin.logging.FindEagerLogMessages$KtRecipe
    • Find eager log-message construction
    • Bundles the trace/debug/info/warn/error eager-interpolation and string-concatenation finders. Every hit is a candidate for migration to kotlin-logging's lambda form (log.debug \{ "..." \}) or SLF4J's parameterized form (log.debug("x=\{\}", x)).
  • org.openrewrite.kotlin.logging.FindEagerStringInterpolationInLogDebug$KtRecipe
    • Find eager string interpolation in log.debug(...)
    • log.debug("x=$x") evaluates the template (including any toString() work on x) before the call even reaches the logger — if debug is disabled, the work is wasted. With kotlin-logging use log.debug \{ "x=$x" \}; with SLF4J use the parameterized form log.debug("x=\{\}", x).
  • org.openrewrite.kotlin.logging.FindEagerStringInterpolationInLogError$KtRecipe
    • Find eager string interpolation in log.error(...)
    • Error logs almost always fire, so cost is rarely the issue — but a parameterized message keeps the template stable for log aggregators that group errors by template hash, and lets the throwable argument flow through SLF4J's last-arg-is-Throwable convention cleanly.
  • org.openrewrite.kotlin.logging.FindEagerStringInterpolationInLogInfo$KtRecipe
    • Find eager string interpolation in log.info(...)
    • log.info("x=$x") evaluates the template eagerly. If your application turns info off in production, the toString() calls inside the template still run. Use the lambda form (kotlin-logging) or the SLF4J \{\} placeholder form.
  • org.openrewrite.kotlin.logging.FindEagerStringInterpolationInLogTrace$KtRecipe
    • Find eager string interpolation in log.trace(...)
    • log.trace("x=$x") evaluates the template (including any toString() work on x) before the call even reaches the logger — if trace is disabled, the work is wasted. With kotlin-logging use log.trace \{ "x=$x" \}; with SLF4J use the parameterized form log.trace("x=\{\}", x).
  • org.openrewrite.kotlin.logging.FindEagerStringInterpolationInLogWarn$KtRecipe
    • Find eager string interpolation in log.warn(...)
    • Warning logs are usually enabled in production, so eager interpolation is less of a hot-path issue — but the parameterized form (log.warn("x=\{\}", x)) still keeps the message template stable for log aggregators that group by template hash.
  • org.openrewrite.kotlin.logging.FindIsDebugEnabledGuard$KtRecipe
    • Find if (log.isDebugEnabled) ... guards
    • if (log.isDebugEnabled) log.debug(...) is the Java-1.4-era idiom — replaced by kotlin-logging's log.debug \{ "..." \} lambda or SLF4J's log.debug("x=\{\}", x) placeholder form. Either defers the work without the explicit guard.
  • org.openrewrite.kotlin.logging.FindIsErrorEnabledGuard$KtRecipe
    • Find if (log.isErrorEnabled) ... guards
    • Errors are practically always enabled. The guard suggests the code was once shared with debug/trace machinery — drop it.
  • org.openrewrite.kotlin.logging.FindIsInfoEnabledGuard$KtRecipe
    • Find if (log.isInfoEnabled) ... guards
    • Info is usually on in production, so the guard rarely saves anything. If you keep it, prefer the lambda or \{\}-placeholder form for consistency with debug/trace.
  • org.openrewrite.kotlin.logging.FindIsTraceEnabledGuard$KtRecipe
    • Find if (log.isTraceEnabled) ... guards
    • With kotlin-logging's lambda form (log.trace \{ "..." \}) the level-check is built into the call — wrapping it in if (log.isTraceEnabled) repeats the check. With SLF4J's parameterized form, the placeholder substitution is also deferred, so the explicit guard is only worthwhile if the argument construction itself is expensive.
  • org.openrewrite.kotlin.logging.FindIsWarnEnabledGuard$KtRecipe
    • Find if (log.isWarnEnabled) ... guards
    • Warning logs are nearly always enabled. The guard is almost certainly dead code — drop it and use the parameterized form.
  • org.openrewrite.kotlin.logging.FindJulLoggerGetLogger$KtRecipe
    • Find java.util.logging.Logger.getLogger(...) calls
    • java.util.logging ships with the JDK but lacks the structured-logging, MDC, and parameterized-message ergonomics of SLF4J or kotlin-logging. Migrate j.u.l.Logger.getLogger(...) to LoggerFactory.getLogger(...) (SLF4J) or KotlinLogging.logger \{ \} (kotlin-logging).
  • org.openrewrite.kotlin.logging.FindJulLoggerLog$KtRecipe
    • Find julLogger.log(level, msg) and level-specific julLogger.fine/info/severe/... calls
    • Each j.u.l.Logger.log(Level, ...) call (and the level-specific shortcuts fine/info/warning/severe/config) needs to be re-expressed against SLF4J/kotlin-logging when migrating. Flag for review.
  • org.openrewrite.kotlin.logging.FindLegacyLoggerLibraries$KtRecipe
    • Find legacy logger-library usage
    • Bundles java.util.logging and log4j 1.x finders. Both predate structured logging and should migrate to SLF4J or kotlin-logging.
  • org.openrewrite.kotlin.logging.FindLog4j1Logger$KtRecipe
    • Find org.apache.log4j.Logger references
    • Log4j 1.x reached end-of-life in 2015 and has known unfixed vulnerabilities. Migrate to log4j 2.x (org.apache.logging.log4j.Logger) or SLF4J. The migration is mechanical for the basic getLogger / log methods, but custom appenders and layouts need a manual rewrite.
  • org.openrewrite.kotlin.logging.FindLoggerDeclarationSmells$KtRecipe
    • Find logger declaration smells
    • Bundles log vs logger naming, missing private on companion-object loggers, and instance-field loggers (one per allocation). The shape consensus is private val log = LoggerFactory.getLogger(...) in a companion object.
  • org.openrewrite.kotlin.logging.FindLoggerFactoryGetLogger$KtRecipe
    • Find LoggerFactory.getLogger(SomeClass::class.java) calls
    • LoggerFactory.getLogger(Foo::class.java) is the Java idiom Kotlin code inherited. kotlin-logging's KotlinLogging.logger \{ \} infers the enclosing class automatically (via the stack frame at site of declaration) and avoids the ::class.java reflection round-trip.
  • org.openrewrite.kotlin.logging.FindLoggerFactoryGetLoggerWithStringName$KtRecipe
    • Find LoggerFactory.getLogger("some-name") calls
    • A string logger name is fine for named/structured loggers but is a smell when the string happens to spell out a class FQN — that should be getLogger(Foo::class.java) (or KotlinLogging.logger \{ \}) so renames track. Flag for human review.
  • org.openrewrite.kotlin.logging.FindLoggerFactoryGetLoggerWithThisClass$KtRecipe
    • Find LoggerFactory.getLogger(this::class.java) calls
    • this::class.java resolves the runtime class — fine for non-final classes, but kotlin-logging's KotlinLogging.logger \{ \} already infers the declaring class lexically and avoids the runtime reflection. Either form binds the same logger name for a final class.
  • org.openrewrite.kotlin.logging.FindLoggerFactoryMigrationCandidates$KtRecipe
    • Find LoggerFactory.getLogger migration candidates
    • Bundles the SLF4J LoggerFactory.getLogger shapes — class-literal, this::class.java, and string-name. Each is a candidate for kotlin-logging's KotlinLogging.logger \{ \}.
  • org.openrewrite.kotlin.logging.FindLoggerFieldNamedLog$KtRecipe
    • Find top-level/companion logger fields named log
    • Both log and logger are common — pick one and stick with it across the codebase. The naming convention is the only thing that lets a reader skim a file and spot the logger declaration in two seconds. Flag log-named declarations so the team can confirm the project convention.
  • org.openrewrite.kotlin.logging.FindLoggerGuards$KtRecipe
    • Find redundant logger level-check guards
    • Bundles if (log.isXxxEnabled) ... finders. With kotlin-logging's lambda form or SLF4J's \{\} placeholder form, the level check is built into the call.
  • org.openrewrite.kotlin.logging.FindLoggerNotInCompanion$KtRecipe
    • Find loggers declared as instance fields (one per object)
    • An instance-field val log = LoggerFactory.getLogger(...) allocates one logger per object. Logger factories cache by name, so the runtime cost is one extra map lookup per allocation — but the conventional shape is a private val log in the companion object (or a top-level private val log for top-level functions), so a per-instance logger usually reflects accidental code placement.
  • org.openrewrite.kotlin.logging.FindPrintAndPrintStackTrace$KtRecipe
    • Find println / System.err.println / printStackTrace
    • Bundles the unstructured-output finders. Each call writes outside the logger pipeline, so it bypasses level filters, MDCs, and structured sinks.
  • org.openrewrite.kotlin.logging.FindPrintErr$KtRecipe
    • Find System.err.println(...) calls
    • System.err.println writes to stderr — better than stdout for errors, but still bypasses whatever structured logger the application uses. Route through log.error("...", throwable) so log aggregators see the context.
  • org.openrewrite.kotlin.logging.FindPrintStackTrace$KtRecipe
    • Find Throwable.printStackTrace() calls
    • e.printStackTrace() writes the throwable's stack frames straight to System.err, bypassing whatever logger the application configures. Use log.error("context", e) so the throwable flows through SLF4J's last-arg-is-Throwable convention and ends up in the same sink as the rest of your errors.
  • org.openrewrite.kotlin.logging.FindPrintln$KtRecipe
    • Find println(...) calls
    • println writes to stdout, which in containerized deployments lands in log files without structure, level filtering, or correlation IDs. Replace with a proper logger; if this is a CLI tool, consider the kotlin-logging level filter so tests can silence noisy output.
  • org.openrewrite.kotlin.logging.FindStringConcatInLogDebug$KtRecipe
    • Find string concatenation in log.debug(...)
    • log.debug("x=" + x) performs the concatenation eagerly. Use kotlin-logging's log.debug \{ "x=$x" \} lambda form or SLF4J's log.debug("x=\{\}", x) placeholder form.
  • org.openrewrite.kotlin.logging.FindStringConcatInLogError$KtRecipe
    • Find string concatenation in log.error(...)
    • log.error("failed: " + e) mixes the throwable into the message string, losing the stack trace. Use the parameterized form with the throwable as the last argument: log.error("failed", e).
  • org.openrewrite.kotlin.logging.FindStringConcatInLogInfo$KtRecipe
    • Find string concatenation in log.info(...)
    • log.info("x=" + x) is the Java-1.4-era logging idiom — replaced in SLF4J by log.info("x=\{\}", x) so the template is stable and the work is deferred.
  • org.openrewrite.kotlin.logging.FindStringConcatInLogTrace$KtRecipe
    • Find string concatenation in log.trace(...)
    • log.trace("x=" + x) performs the concatenation (and the toString on x) before the call — wasted work if trace is disabled. Use the lambda form or SLF4J \{\} placeholders.
  • org.openrewrite.kotlin.logging.FindStringConcatInLogWarn$KtRecipe
    • Find string concatenation in log.warn(...)
    • log.warn("x=" + x) does the concatenation up front. Use the parameterized form so log aggregators can group by message template.
  • org.openrewrite.kotlin.logging.FindThrowablePrintStackTraceWithStream$KtRecipe
    • Find e.printStackTrace(out) calls
    • Writing the stack trace to a PrintStream / PrintWriter is the Java idiom for re-routing it manually. With a structured logger you don't need to — log.error("context", e) already carries the throwable to the configured sink. Review whether the explicit redirection still serves a purpose.
  • org.openrewrite.kotlin.logging.Logging$KtRecipe
    • Find Kotlin logging smells
    • Search-only recipes for SLF4J, kotlin-logging, java.util.logging, log4j 1.x, and println/printStackTrace usage. Covers eager-message construction (string templates and concatenation), redundant level-check guards, LoggerFactory.getLogger shapes that could be KotlinLogging.logger \{ \}, unstructured-output calls that bypass the logger, and logger-declaration smells (naming, visibility, instance-field placement).
  • org.openrewrite.kotlin.migrate.Kotlin1To2$KtRecipe
    • Migrate to Kotlin 2.x
    • Modernizes a Kotlin 1.x codebase for Kotlin 2.x: replaces stdlib APIs deprecated between 1.4 and 2.0 with their modern equivalents, swaps JVM-only java.lang/java.util helpers for multiplatform Kotlin extensions, migrates inline class to @JvmInline value class, and removes @OptIn annotations for experimental markers that have since graduated to stable.
  • org.openrewrite.kotlin.migrate.RemoveRedundantOptIns$KtRecipe
    • Remove redundant @OptIn annotations
    • Removes @OptIn annotations for stdlib experimental markers that have since graduated to stable (ExperimentalStdlibApi, ExperimentalTime, ExperimentalUnsignedTypes, ExperimentalPathApi). The annotations no longer suppress anything and just add noise.
  • org.openrewrite.kotlin.migrate.UseAppendLine$KtRecipe
    • Use appendLine() instead of appendln()
    • Appendable.appendln() was deprecated in Kotlin 1.4 in favor of appendLine() (consistent naming with Reader.readLine()).
  • org.openrewrite.kotlin.migrate.UseAppendLineAny$KtRecipe
    • Use appendLine(value) instead of appendln(value) (Any?)
    • StringBuilder.appendln(value: Any?) was deprecated in Kotlin 1.4 in favor of appendLine(value: Any?).
  • org.openrewrite.kotlin.migrate.UseAppendLineChar$KtRecipe
    • Use appendLine(char) instead of appendln(char)
    • Appendable.appendln(value: Char) was deprecated in Kotlin 1.4 in favor of appendLine(value: Char).
  • org.openrewrite.kotlin.migrate.UseAppendLineCharSequence$KtRecipe
    • Use appendLine(cs) instead of appendln(cs) (CharSequence)
    • Appendable.appendln(value: CharSequence?) was deprecated in Kotlin 1.4 in favor of appendLine(value: CharSequence?).
  • org.openrewrite.kotlin.migrate.UseAppendLineWithValue$KtRecipe
    • Use appendLine(value) instead of appendln(value)
    • Appendable.appendln(value) was deprecated in Kotlin 1.4 in favor of appendLine(value).
  • org.openrewrite.kotlin.migrate.UseArrayContentDeepEquals$KtRecipe
    • Use Array.contentDeepEquals() instead of Arrays.deepEquals(a, b)
    • java.util.Arrays.deepEquals(a, b) recursively compares nested arrays; a.contentDeepEquals(b) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseArrayContentDeepHashCode$KtRecipe
    • Use Array.contentDeepHashCode() instead of Arrays.deepHashCode(arr)
    • java.util.Arrays.deepHashCode(arr) recursively hashes nested arrays; arr.contentDeepHashCode() is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseArrayContentDeepToString$KtRecipe
    • Use Array.contentDeepToString() instead of Arrays.deepToString(arr)
    • java.util.Arrays.deepToString(arr) recursively unrolls nested arrays; arr.contentDeepToString() is the multiplatform Kotlin extension producing the same representation.
  • org.openrewrite.kotlin.migrate.UseArrayContentEquals$KtRecipe
    • Use Array.contentEquals() instead of Arrays.equals(a, b)
    • java.util.Arrays.equals(a: Object[], b: Object[]) is JVM-only; a.contentEquals(b) is the multiplatform Kotlin extension. For nested arrays use the deep variant.
  • org.openrewrite.kotlin.migrate.UseArrayContentHashCode$KtRecipe
    • Use Array.contentHashCode() instead of Arrays.hashCode(arr)
    • java.util.Arrays.hashCode(arr: Object[]) is JVM-only; arr.contentHashCode() is the multiplatform Kotlin extension. For nested arrays use the deep variant.
  • org.openrewrite.kotlin.migrate.UseArrayContentToString$KtRecipe
    • Use Array.contentToString() instead of Arrays.toString(arr)
    • java.util.Arrays.toString(arr: Object[]) is JVM-only; arr.contentToString() is the multiplatform Kotlin extension. For nested arrays use the deep variant.
  • org.openrewrite.kotlin.migrate.UseBooleanArrayContentEquals$KtRecipe
    • Use BooleanArray.contentEquals() instead of Arrays.equals(a, b)
    • java.util.Arrays.equals(a: boolean[], b: boolean[]) is JVM-only; a.contentEquals(b) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseBooleanArrayContentHashCode$KtRecipe
    • Use BooleanArray.contentHashCode() instead of Arrays.hashCode(arr)
    • java.util.Arrays.hashCode(arr: boolean[]) is JVM-only; arr.contentHashCode() is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseBooleanArrayContentToString$KtRecipe
    • Use BooleanArray.contentToString() instead of Arrays.toString(arr)
    • java.util.Arrays.toString(arr: boolean[]) is JVM-only; arr.contentToString() is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseBooleanArrayCopyOf$KtRecipe
    • Use BooleanArray.copyOf() instead of Arrays.copyOf(arr, newLength)
    • java.util.Arrays.copyOf(arr: boolean[], newLength) is JVM-only; arr.copyOf(newLength) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseBooleanArrayFill$KtRecipe
    • Use BooleanArray.fill() instead of Arrays.fill(arr, value)
    • java.util.Arrays.fill(arr: boolean[], value) is JVM-only; arr.fill(value) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseByteArrayBinarySearch$KtRecipe
    • Use ByteArray.binarySearch() instead of Arrays.binarySearch(arr, key)
    • java.util.Arrays.binarySearch(arr: byte[], key: Byte) is JVM-only; arr.binarySearch(key) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseByteArrayContentEquals$KtRecipe
    • Use ByteArray.contentEquals() instead of Arrays.equals(a, b)
    • java.util.Arrays.equals(a: byte[], b: byte[]) is JVM-only; a.contentEquals(b) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseByteArrayContentHashCode$KtRecipe
    • Use ByteArray.contentHashCode() instead of Arrays.hashCode(arr)
    • java.util.Arrays.hashCode(arr: byte[]) is JVM-only; arr.contentHashCode() is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseByteArrayContentToString$KtRecipe
    • Use ByteArray.contentToString() instead of Arrays.toString(arr)
    • java.util.Arrays.toString(arr: byte[]) is JVM-only; arr.contentToString() is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseByteArrayCopyOf$KtRecipe
    • Use ByteArray.copyOf() instead of Arrays.copyOf(arr, newLength)
    • java.util.Arrays.copyOf(arr: byte[], newLength) is JVM-only; arr.copyOf(newLength) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseByteArrayFill$KtRecipe
    • Use ByteArray.fill() instead of Arrays.fill(arr, value)
    • java.util.Arrays.fill(arr: byte[], value) is JVM-only; arr.fill(value) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseByteArraySort$KtRecipe
    • Use ByteArray.sort() instead of Arrays.sort(arr)
    • java.util.Arrays.sort(arr: byte[]) is JVM-only; arr.sort() is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseCapitalize$KtRecipe
    • Use replaceFirstChar \{ … \} instead of capitalize()
    • String.capitalize() was deprecated in Kotlin 1.5 in favor of the locale-explicit replaceFirstChar \{ if (it.isLowerCase()) it.titlecase() else it.toString() \}.
  • org.openrewrite.kotlin.migrate.UseCharArrayBinarySearch$KtRecipe
    • Use CharArray.binarySearch() instead of Arrays.binarySearch(arr, key)
    • java.util.Arrays.binarySearch(arr: char[], key: Char) is JVM-only; arr.binarySearch(key) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseCharArrayContentEquals$KtRecipe
    • Use CharArray.contentEquals() instead of Arrays.equals(a, b)
    • java.util.Arrays.equals(a: char[], b: char[]) is JVM-only; a.contentEquals(b) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseCharArrayContentHashCode$KtRecipe
    • Use CharArray.contentHashCode() instead of Arrays.hashCode(arr)
    • java.util.Arrays.hashCode(arr: char[]) is JVM-only; arr.contentHashCode() is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseCharArrayContentToString$KtRecipe
    • Use CharArray.contentToString() instead of Arrays.toString(arr)
    • java.util.Arrays.toString(arr: char[]) is JVM-only; arr.contentToString() is the multiplatform Kotlin extension. Note this produces a bracketed list — use String(arr) if you want a String view of the characters.
  • org.openrewrite.kotlin.migrate.UseCharArrayCopyOf$KtRecipe
    • Use CharArray.copyOf() instead of Arrays.copyOf(arr, newLength)
    • java.util.Arrays.copyOf(arr: char[], newLength) is JVM-only; arr.copyOf(newLength) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseCharArrayFill$KtRecipe
    • Use CharArray.fill() instead of Arrays.fill(arr, value)
    • java.util.Arrays.fill(arr: char[], value) is JVM-only; arr.fill(value) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseCharArraySort$KtRecipe
    • Use CharArray.sort() instead of Arrays.sort(arr)
    • java.util.Arrays.sort(arr: char[]) is JVM-only; arr.sort() is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseCharCode$KtRecipe
    • Use Char.code instead of Char.toInt()
    • Char.toInt() was deprecated in Kotlin 1.5; the replacement Char.code makes the conversion-to-codepoint intent explicit (the old name collided with Number.toInt()).
  • org.openrewrite.kotlin.migrate.UseCharCodeAsByte$KtRecipe
    • Use Char.code.toByte() instead of Char.toByte()
    • Char.toByte() was deprecated in Kotlin 1.5 in favor of going through Char.code.
  • org.openrewrite.kotlin.migrate.UseCharCodeAsDouble$KtRecipe
    • Use Char.code.toDouble() instead of Char.toDouble()
    • Char.toDouble() was deprecated in Kotlin 1.5 in favor of going through Char.code.
  • org.openrewrite.kotlin.migrate.UseCharCodeAsFloat$KtRecipe
    • Use Char.code.toFloat() instead of Char.toFloat()
    • Char.toFloat() was deprecated in Kotlin 1.5 in favor of going through Char.code.
  • org.openrewrite.kotlin.migrate.UseCharCodeAsLong$KtRecipe
    • Use Char.code.toLong() instead of Char.toLong()
    • Char.toLong() was deprecated in Kotlin 1.5 in favor of going through Char.code.
  • org.openrewrite.kotlin.migrate.UseCharCodeAsShort$KtRecipe
    • Use Char.code.toShort() instead of Char.toShort()
    • Char.toShort() was deprecated in Kotlin 1.5 in favor of going through Char.code.
  • org.openrewrite.kotlin.migrate.UseCharCompareTo$KtRecipe
    • Use Char.compareTo instead of java.lang.Character.compare
    • Character.compare(a, b) becomes a.compareTo(b) — the multiplatform receiver call on Char returns the same Int ordering.
  • org.openrewrite.kotlin.migrate.UseCharCtor$KtRecipe
    • Use Char(int) instead of Int.toChar()
    • Int.toChar() was deprecated in Kotlin 1.5; the replacement Char(int) constructor expresses the codepoint-to-char intent symmetrically with Char.code.
  • org.openrewrite.kotlin.migrate.UseCharDigitToInt$KtRecipe
    • Use Char.digitToInt(radix) instead of Character.digit(c, radix)
    • Character.digit(c, radix) returns -1 for non-digits; the Kotlin extension c.digitToInt(radix) throws IllegalArgumentException instead. Use c.digitToIntOrNull(radix) if the JVM null-on-failure semantic is required.
  • org.openrewrite.kotlin.migrate.UseCharIsDefined$KtRecipe
    • Use Char.isDefined() instead of Character.isDefined(c)
    • Prefer the multiplatform Kotlin extension c.isDefined() over the JVM-only Character.isDefined(c).
  • org.openrewrite.kotlin.migrate.UseCharIsDigit$KtRecipe
    • Use Char.isDigit() instead of Character.isDigit(c)
    • Java's Character.isDigit(c) is JVM-only; the Kotlin extension c.isDigit() is multiplatform and reads more naturally as a receiver call.
  • org.openrewrite.kotlin.migrate.UseCharIsHighSurrogate$KtRecipe
    • Use Char.isHighSurrogate() instead of Character.isHighSurrogate(c)
    • Prefer the multiplatform Kotlin extension c.isHighSurrogate() over the JVM-only Character.isHighSurrogate(c).
  • org.openrewrite.kotlin.migrate.UseCharIsISOControl$KtRecipe
    • Use Char.isISOControl() instead of Character.isISOControl(c)
    • Prefer the multiplatform Kotlin extension c.isISOControl() over the JVM-only Character.isISOControl(c).
  • org.openrewrite.kotlin.migrate.UseCharIsLetter$KtRecipe
    • Use Char.isLetter() instead of Character.isLetter(c)
    • Prefer the multiplatform Kotlin extension c.isLetter() over the JVM-only Character.isLetter(c).
  • org.openrewrite.kotlin.migrate.UseCharIsLetterOrDigit$KtRecipe
    • Use Char.isLetterOrDigit() instead of Character.isLetterOrDigit(c)
    • Prefer the multiplatform Kotlin extension c.isLetterOrDigit() over the JVM-only Character.isLetterOrDigit(c).
  • org.openrewrite.kotlin.migrate.UseCharIsLowSurrogate$KtRecipe
    • Use Char.isLowSurrogate() instead of Character.isLowSurrogate(c)
    • Prefer the multiplatform Kotlin extension c.isLowSurrogate() over the JVM-only Character.isLowSurrogate(c).
  • org.openrewrite.kotlin.migrate.UseCharIsLowerCase$KtRecipe
    • Use Char.isLowerCase() instead of Character.isLowerCase(c)
    • Prefer the multiplatform Kotlin extension c.isLowerCase() over the JVM-only Character.isLowerCase(c).
  • org.openrewrite.kotlin.migrate.UseCharIsTitleCase$KtRecipe
    • Use Char.isTitleCase() instead of Character.isTitleCase(c)
    • Prefer the multiplatform Kotlin extension c.isTitleCase() over the JVM-only Character.isTitleCase(c).
  • org.openrewrite.kotlin.migrate.UseCharIsUpperCase$KtRecipe
    • Use Char.isUpperCase() instead of Character.isUpperCase(c)
    • Prefer the multiplatform Kotlin extension c.isUpperCase() over the JVM-only Character.isUpperCase(c).
  • org.openrewrite.kotlin.migrate.UseCharIsWhitespace$KtRecipe
    • Use Char.isWhitespace() instead of Character.isWhitespace(c)
    • Prefer the multiplatform Kotlin extension c.isWhitespace() over the JVM-only Character.isWhitespace(c).
  • org.openrewrite.kotlin.migrate.UseCharLowercaseCharForCharacter$KtRecipe
    • Use Char.lowercaseChar() instead of Character.toLowerCase(c)
    • Prefer the multiplatform Kotlin extension c.lowercaseChar() over the JVM-only Character.toLowerCase(c).
  • org.openrewrite.kotlin.migrate.UseCharToString$KtRecipe
    • Use Char.toString() instead of Character.toString(c)
    • java.lang.Character.toString(c) is JVM-only; c.toString() is the multiplatform receiver call and produces the same one-character String.
  • org.openrewrite.kotlin.migrate.UseCharUppercaseCharForCharacter$KtRecipe
    • Use Char.uppercaseChar() instead of Character.toUpperCase(c)
    • Prefer the multiplatform Kotlin extension c.uppercaseChar() over the JVM-only Character.toUpperCase(c).
  • org.openrewrite.kotlin.migrate.UseCollectionMax$KtRecipe
    • Use Collection.max() instead of Collections.max(coll)
    • java.util.Collections.max(coll) is JVM-only; Kotlin's Collection.max() extension is multiplatform and reads as a receiver call. Both throw NoSuchElementException on an empty collection.
  • org.openrewrite.kotlin.migrate.UseCollectionMin$KtRecipe
    • Use Collection.min() instead of Collections.min(coll)
    • java.util.Collections.min(coll) is JVM-only; Kotlin's Collection.min() extension is multiplatform and reads as a receiver call. Both throw NoSuchElementException on an empty collection.
  • org.openrewrite.kotlin.migrate.UseConcatToString$KtRecipe
    • Use CharArray.concatToString() instead of String(charArray)
    • The String(CharArray) constructor is JVM-only; charArray.concatToString() is the multiplatform Kotlin extension producing the same String.
  • org.openrewrite.kotlin.migrate.UseDecapitalize$KtRecipe
    • Use replaceFirstChar \{ it.lowercase() \} instead of decapitalize()
    • String.decapitalize() was deprecated in Kotlin 1.5 in favor of replaceFirstChar \{ it.lowercase() \}.
  • org.openrewrite.kotlin.migrate.UseDecodeToString$KtRecipe
    • Use ByteArray.decodeToString() instead of String(byteArray)
    • The String(ByteArray) constructor is JVM-only and uses the platform default charset; byteArray.decodeToString() is the multiplatform Kotlin extension and always uses UTF-8.
  • org.openrewrite.kotlin.migrate.UseDoubleArrayBinarySearch$KtRecipe
    • Use DoubleArray.binarySearch() instead of Arrays.binarySearch(arr, key)
    • java.util.Arrays.binarySearch(arr: double[], key: Double) is JVM-only; arr.binarySearch(key) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseDoubleArrayContentEquals$KtRecipe
    • Use DoubleArray.contentEquals() instead of Arrays.equals(a, b)
    • java.util.Arrays.equals(a: double[], b: double[]) is JVM-only; a.contentEquals(b) is the multiplatform Kotlin extension. Like Arrays.equals, NaN compares equal to NaN.
  • org.openrewrite.kotlin.migrate.UseDoubleArrayContentHashCode$KtRecipe
    • Use DoubleArray.contentHashCode() instead of Arrays.hashCode(arr)
    • java.util.Arrays.hashCode(arr: double[]) is JVM-only; arr.contentHashCode() is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseDoubleArrayContentToString$KtRecipe
    • Use DoubleArray.contentToString() instead of Arrays.toString(arr)
    • java.util.Arrays.toString(arr: double[]) is JVM-only; arr.contentToString() is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseDoubleArrayCopyOf$KtRecipe
    • Use DoubleArray.copyOf() instead of Arrays.copyOf(arr, newLength)
    • java.util.Arrays.copyOf(arr: double[], newLength) is JVM-only; arr.copyOf(newLength) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseDoubleArrayFill$KtRecipe
    • Use DoubleArray.fill() instead of Arrays.fill(arr, value)
    • java.util.Arrays.fill(arr: double[], value) is JVM-only; arr.fill(value) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseDoubleArraySort$KtRecipe
    • Use DoubleArray.sort() instead of Arrays.sort(arr)
    • java.util.Arrays.sort(arr: double[]) is JVM-only; arr.sort() is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseDoubleIEEErem$KtRecipe
    • Use Double.IEEErem instead of java.lang.Math.IEEEremainder
    • Math.IEEEremainder(x, y) becomes x.IEEErem(y) — the multiplatform Kotlin extension, also shorter.
  • org.openrewrite.kotlin.migrate.UseDoubleNextDown$KtRecipe
    • Use Double.nextDown() instead of java.lang.Math.nextDown
    • Math.nextDown(x) becomes x.nextDown() — multiplatform Kotlin extension on Double.
  • org.openrewrite.kotlin.migrate.UseDoubleNextTowards$KtRecipe
    • Use Double.nextTowards() instead of java.lang.Math.nextAfter
    • Math.nextAfter(x, y) becomes x.nextTowards(y) — multiplatform Kotlin extension on Double. Only the (Double, Double) overload is rewritten; the (Float, Double) overload's mixed types don't line up with Kotlin's Float.nextTowards(Float).
  • org.openrewrite.kotlin.migrate.UseDoubleNextUp$KtRecipe
    • Use Double.nextUp() instead of java.lang.Math.nextUp
    • Math.nextUp(x) becomes x.nextUp() — multiplatform Kotlin extension on Double.
  • org.openrewrite.kotlin.migrate.UseDoublePow$KtRecipe
    • Use Double.pow instead of java.lang.Math.pow
    • Math.pow(x, y) is JVM-only; the Kotlin extension x.pow(y) reads as a receiver call and is multiplatform.
  • org.openrewrite.kotlin.migrate.UseDoubleRoundToLong$KtRecipe
    • Use Double.roundToLong() instead of java.lang.Math.round
    • Math.round(d: Double): Long becomes d.roundToLong() as a multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseDoubleWithSign$KtRecipe
    • Use Double.withSign instead of java.lang.Math.copySign
    • Math.copySign(magnitude, sign) becomes magnitude.withSign(sign) — the multiplatform Kotlin extension expresses the same magnitude/sign combination as a receiver call.
  • org.openrewrite.kotlin.migrate.UseEnumEntries$KtRecipe
    • Use enumEntries<T>() instead of enumValues<T>()
    • Kotlin 1.9 introduced enumEntries<T>() returning a stable EnumEntries<T> view. Prefer it over enumValues<T>(), which allocates a fresh array on each call.
  • org.openrewrite.kotlin.migrate.UseFloatArrayBinarySearch$KtRecipe
    • Use FloatArray.binarySearch() instead of Arrays.binarySearch(arr, key)
    • java.util.Arrays.binarySearch(arr: float[], key: Float) is JVM-only; arr.binarySearch(key) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseFloatArrayContentEquals$KtRecipe
    • Use FloatArray.contentEquals() instead of Arrays.equals(a, b)
    • java.util.Arrays.equals(a: float[], b: float[]) is JVM-only; a.contentEquals(b) is the multiplatform Kotlin extension. Like Arrays.equals, NaN compares equal to NaN.
  • org.openrewrite.kotlin.migrate.UseFloatArrayContentHashCode$KtRecipe
    • Use FloatArray.contentHashCode() instead of Arrays.hashCode(arr)
    • java.util.Arrays.hashCode(arr: float[]) is JVM-only; arr.contentHashCode() is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseFloatArrayContentToString$KtRecipe
    • Use FloatArray.contentToString() instead of Arrays.toString(arr)
    • java.util.Arrays.toString(arr: float[]) is JVM-only; arr.contentToString() is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseFloatArrayCopyOf$KtRecipe
    • Use FloatArray.copyOf() instead of Arrays.copyOf(arr, newLength)
    • java.util.Arrays.copyOf(arr: float[], newLength) is JVM-only; arr.copyOf(newLength) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseFloatArrayFill$KtRecipe
    • Use FloatArray.fill() instead of Arrays.fill(arr, value)
    • java.util.Arrays.fill(arr: float[], value) is JVM-only; arr.fill(value) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseFloatArraySort$KtRecipe
    • Use FloatArray.sort() instead of Arrays.sort(arr)
    • java.util.Arrays.sort(arr: float[]) is JVM-only; arr.sort() is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseFloatRoundToInt$KtRecipe
    • Use Float.roundToInt() instead of java.lang.Math.round
    • Math.round(f: Float): Int becomes f.roundToInt() as a multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseIntArrayBinarySearch$KtRecipe
    • Use IntArray.binarySearch() instead of Arrays.binarySearch(arr, key)
    • java.util.Arrays.binarySearch(arr: int[], key: Int) is JVM-only; arr.binarySearch(key) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseIntArrayContentEquals$KtRecipe
    • Use IntArray.contentEquals() instead of Arrays.equals(a, b)
    • java.util.Arrays.equals(a: int[], b: int[]) is JVM-only; a.contentEquals(b) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseIntArrayContentHashCode$KtRecipe
    • Use IntArray.contentHashCode() instead of Arrays.hashCode(arr)
    • java.util.Arrays.hashCode(arr: int[]) is JVM-only; arr.contentHashCode() is the multiplatform Kotlin extension and produces the same value.
  • org.openrewrite.kotlin.migrate.UseIntArrayContentToString$KtRecipe
    • Use IntArray.contentToString() instead of Arrays.toString(arr)
    • java.util.Arrays.toString(arr: int[]) is JVM-only; arr.contentToString() is the multiplatform Kotlin extension producing the same bracketed representation.
  • org.openrewrite.kotlin.migrate.UseIntArrayCopyOf$KtRecipe
    • Use IntArray.copyOf() instead of Arrays.copyOf(arr, newLength)
    • java.util.Arrays.copyOf(arr: int[], newLength) is JVM-only; arr.copyOf(newLength) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseIntArrayFill$KtRecipe
    • Use IntArray.fill() instead of Arrays.fill(arr, value)
    • java.util.Arrays.fill(arr: int[], value) is JVM-only; arr.fill(value) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseIntArraySort$KtRecipe
    • Use IntArray.sort() instead of Arrays.sort(arr)
    • java.util.Arrays.sort(arr: int[]) is JVM-only; arr.sort() is the multiplatform Kotlin extension that sorts in place ascending.
  • org.openrewrite.kotlin.migrate.UseIntCompareTo$KtRecipe
    • Use Int.compareTo instead of java.lang.Integer.compare
    • Integer.compare(a, b) is the JVM-only static comparator; the multiplatform a.compareTo(b) reads as a receiver call and returns the same Int ordering.
  • org.openrewrite.kotlin.migrate.UseIntCountLeadingZeroBits$KtRecipe
    • Use Int.countLeadingZeroBits() instead of Integer.numberOfLeadingZeros
    • Integer.numberOfLeadingZeros(i) becomes i.countLeadingZeroBits() — the multiplatform Kotlin extension reads as a receiver call.
  • org.openrewrite.kotlin.migrate.UseIntCountOneBits$KtRecipe
    • Use Int.countOneBits() instead of java.lang.Integer.bitCount
    • Integer.bitCount(i) is the JVM-only popcount; the multiplatform i.countOneBits() extension reads as a receiver call.
  • org.openrewrite.kotlin.migrate.UseIntCountTrailingZeroBits$KtRecipe
    • Use Int.countTrailingZeroBits() instead of Integer.numberOfTrailingZeros
    • Integer.numberOfTrailingZeros(i) becomes i.countTrailingZeroBits() — the multiplatform Kotlin extension reads as a receiver call.
  • org.openrewrite.kotlin.migrate.UseIntDigitToChar$KtRecipe
    • Use Int.digitToChar(radix) instead of Character.forDigit(digit, radix)
    • Character.forDigit(digit, radix) returns the null '\u0000' for invalid input; the Kotlin extension digit.digitToChar(radix) throws IllegalArgumentException instead.
  • org.openrewrite.kotlin.migrate.UseIntFloorDiv$KtRecipe
    • Use Int.floorDiv instead of java.lang.Math.floorDiv
    • Math.floorDiv(a, b) becomes a.floorDiv(b) — the multiplatform Kotlin extension on Int.
  • org.openrewrite.kotlin.migrate.UseIntMod$KtRecipe
    • Use Int.mod() instead of java.lang.Math.floorMod
    • Math.floorMod(a, b) becomes a.mod(b). Kotlin's Int.mod uses floored-division semantics — the result is non-negative when the divisor is positive — matching Math.floorMod. Reads as a receiver call and is multiplatform.
  • org.openrewrite.kotlin.migrate.UseIntRotateLeft$KtRecipe
    • Use Int.rotateLeft(n) instead of Integer.rotateLeft(i, n)
    • Integer.rotateLeft(i, n) is JVM-only; the multiplatform i.rotateLeft(n) extension reads as a receiver call.
  • org.openrewrite.kotlin.migrate.UseIntRotateRight$KtRecipe
    • Use Int.rotateRight(n) instead of Integer.rotateRight(i, n)
    • Integer.rotateRight(i, n) is JVM-only; the multiplatform i.rotateRight(n) extension reads as a receiver call.
  • org.openrewrite.kotlin.migrate.UseIntToString$KtRecipe
    • Use Int.toString() instead of Integer.toString(i)
    • Integer.toString(i) is the JVM-only spelling; i.toString() reads as a receiver call and is multiplatform.
  • org.openrewrite.kotlin.migrate.UseIntToStringBinary$KtRecipe
    • Use Int.toString(2) instead of Integer.toBinaryString
    • Integer.toBinaryString(i) is JVM-only; the multiplatform i.toString(2) produces the same binary text.
  • org.openrewrite.kotlin.migrate.UseIntToStringHex$KtRecipe
    • Use Int.toString(16) instead of Integer.toHexString
    • Integer.toHexString(i) is JVM-only; the multiplatform i.toString(16) produces the same hexadecimal text.
  • org.openrewrite.kotlin.migrate.UseIntToStringOctal$KtRecipe
    • Use Int.toString(8) instead of Integer.toOctalString
    • Integer.toOctalString(i) is JVM-only; the multiplatform i.toString(8) produces the same octal text.
  • org.openrewrite.kotlin.migrate.UseIntToStringWithRadix$KtRecipe
    • Use Int.toString(radix) instead of Integer.toString(i, radix)
    • Integer.toString(i, radix) is the JVM-only spelling; i.toString(radix) is the multiplatform Kotlin receiver call.
  • org.openrewrite.kotlin.migrate.UseKotlinArray$KtRecipe
    • Use Kotlin array extensions instead of java.util.Arrays
    • Replaces JVM-only java.util.Arrays static helpers with the multiplatform Kotlin extensions on each primitive array (and Array<*>): contentToString(), contentEquals(), contentHashCode(), fill(), sort(), binarySearch(), copyOf(), the deep variants for nested arrays, and the String(charArray)/String(byteArray) constructors that become charArray.concatToString()/byteArray.decodeToString().
  • org.openrewrite.kotlin.migrate.UseKotlinChar$KtRecipe
    • Use Char extensions instead of java.lang.Character
    • Replaces JVM-only static helpers on java.lang.Character (isDigit(c), isLetter(c), toUpperCase(c), digit(c, radix), compare(a, b), toString(c), surrogate predicates) with the multiplatform Kotlin extensions on Char (c.isDigit(), c.uppercaseChar(), c.digitToInt(radix), …).
  • org.openrewrite.kotlin.migrate.UseKotlinCollections$KtRecipe
    • Use Kotlin collection extensions instead of java.util.Collections
    • Replaces JVM-only java.util.Collections static helpers with the multiplatform Kotlin equivalents: list.sort(), list.reverse(), list.shuffle(), listOf(x)/setOf(x), and Collection.max()/min().
  • org.openrewrite.kotlin.migrate.UseKotlinIntMax$KtRecipe
    • Use Int.MAX_VALUE instead of java.lang.Integer.MAX_VALUE
    • Integer.MAX_VALUE is the JVM-only spelling; Kotlin's Int.MAX_VALUE is the multiplatform equivalent.
  • org.openrewrite.kotlin.migrate.UseKotlinIntMin$KtRecipe
    • Use Int.MIN_VALUE instead of java.lang.Integer.MIN_VALUE
    • Integer.MIN_VALUE is the JVM-only spelling; Kotlin's Int.MIN_VALUE is the multiplatform equivalent.
  • org.openrewrite.kotlin.migrate.UseKotlinMath$KtRecipe
    • Use kotlin.math instead of java.lang.Math
    • Replaces JVM-only java.lang.Math calls with their multiplatform kotlin.math equivalents — top-level functions (abs, sqrt, sin, …), constants (PI, E), and receiver-style extensions on Double/Int/Long (pow, roundToInt, floorDiv, mod, …).
  • org.openrewrite.kotlin.migrate.UseKotlinMathAbs$KtRecipe
    • Use kotlin.math.abs instead of java.lang.Math.abs
    • kotlin.math.abs is the multiplatform-friendly form. Java's Math.abs only works on the JVM and is a thin pass-through; the Kotlin call site reads more naturally in shared modules.
  • org.openrewrite.kotlin.migrate.UseKotlinMathAcos$KtRecipe
    • Use kotlin.math.acos instead of java.lang.Math.acos
    • Prefer the multiplatform-friendly kotlin.math.acos over the JVM-only Math.acos.
  • org.openrewrite.kotlin.migrate.UseKotlinMathAsin$KtRecipe
    • Use kotlin.math.asin instead of java.lang.Math.asin
    • Prefer the multiplatform-friendly kotlin.math.asin over the JVM-only Math.asin.
  • org.openrewrite.kotlin.migrate.UseKotlinMathAtan$KtRecipe
    • Use kotlin.math.atan instead of java.lang.Math.atan
    • Prefer the multiplatform-friendly kotlin.math.atan over the JVM-only Math.atan.
  • org.openrewrite.kotlin.migrate.UseKotlinMathAtan2$KtRecipe
    • Use kotlin.math.atan2 instead of java.lang.Math.atan2
    • Prefer the multiplatform-friendly kotlin.math.atan2 over the JVM-only Math.atan2.
  • org.openrewrite.kotlin.migrate.UseKotlinMathCbrt$KtRecipe
    • Use kotlin.math.cbrt instead of java.lang.Math.cbrt
    • Prefer the multiplatform-friendly kotlin.math.cbrt over the JVM-only Math.cbrt.
  • org.openrewrite.kotlin.migrate.UseKotlinMathCeil$KtRecipe
    • Use kotlin.math.ceil instead of java.lang.Math.ceil
    • Prefer the multiplatform-friendly kotlin.math.ceil over the JVM-only Math.ceil.
  • org.openrewrite.kotlin.migrate.UseKotlinMathCos$KtRecipe
    • Use kotlin.math.cos instead of java.lang.Math.cos
    • Prefer the multiplatform-friendly kotlin.math.cos over the JVM-only Math.cos.
  • org.openrewrite.kotlin.migrate.UseKotlinMathCosh$KtRecipe
    • Use kotlin.math.cosh instead of java.lang.Math.cosh
    • Prefer the multiplatform-friendly kotlin.math.cosh over the JVM-only Math.cosh.
  • org.openrewrite.kotlin.migrate.UseKotlinMathE$KtRecipe
    • Use kotlin.math.E instead of java.lang.Math.E
    • Prefer the multiplatform-friendly kotlin.math.E over the JVM-only Math.E.
  • org.openrewrite.kotlin.migrate.UseKotlinMathExp$KtRecipe
    • Use kotlin.math.exp instead of java.lang.Math.exp
    • Prefer the multiplatform-friendly kotlin.math.exp over the JVM-only Math.exp.
  • org.openrewrite.kotlin.migrate.UseKotlinMathExpm1$KtRecipe
    • Use kotlin.math.expm1 instead of java.lang.Math.expm1
    • Prefer the multiplatform-friendly kotlin.math.expm1 over the JVM-only Math.expm1.
  • org.openrewrite.kotlin.migrate.UseKotlinMathFloor$KtRecipe
    • Use kotlin.math.floor instead of java.lang.Math.floor
    • Prefer the multiplatform-friendly kotlin.math.floor over the JVM-only Math.floor.
  • org.openrewrite.kotlin.migrate.UseKotlinMathHypot$KtRecipe
    • Use kotlin.math.hypot instead of java.lang.Math.hypot
    • Prefer the multiplatform-friendly kotlin.math.hypot over the JVM-only Math.hypot.
  • org.openrewrite.kotlin.migrate.UseKotlinMathLn$KtRecipe
    • Use kotlin.math.ln instead of java.lang.Math.log
    • Math.log is natural log; the multiplatform kotlin.math package spells it ln to disambiguate from log(b, x) (log base b).
  • org.openrewrite.kotlin.migrate.UseKotlinMathLn1p$KtRecipe
    • Use kotlin.math.ln1p instead of java.lang.Math.log1p
    • Math.log1p(x) computes ln(1 + x). The multiplatform kotlin.math package spells it ln1p, mirroring the logln rename.
  • org.openrewrite.kotlin.migrate.UseKotlinMathLog10$KtRecipe
    • Use kotlin.math.log10 instead of java.lang.Math.log10
    • Prefer the multiplatform-friendly kotlin.math.log10 over the JVM-only Math.log10.
  • org.openrewrite.kotlin.migrate.UseKotlinMathMax$KtRecipe
    • Use kotlin.math.max instead of java.lang.Math.max
    • Prefer the multiplatform-friendly kotlin.math.max over the JVM-only Math.max.
  • org.openrewrite.kotlin.migrate.UseKotlinMathMin$KtRecipe
    • Use kotlin.math.min instead of java.lang.Math.min
    • Prefer the multiplatform-friendly kotlin.math.min over the JVM-only Math.min.
  • org.openrewrite.kotlin.migrate.UseKotlinMathPi$KtRecipe
    • Use kotlin.math.PI instead of java.lang.Math.PI
    • Prefer the multiplatform-friendly kotlin.math.PI over the JVM-only Math.PI.
  • org.openrewrite.kotlin.migrate.UseKotlinMathSign$KtRecipe
    • Use kotlin.math.sign instead of java.lang.Math.signum
    • Math.signum(x) is renamed to kotlin.math.sign(x) in the multiplatform kotlin.math package.
  • org.openrewrite.kotlin.migrate.UseKotlinMathSin$KtRecipe
    • Use kotlin.math.sin instead of java.lang.Math.sin
    • Prefer the multiplatform-friendly kotlin.math.sin over the JVM-only Math.sin.
  • org.openrewrite.kotlin.migrate.UseKotlinMathSinh$KtRecipe
    • Use kotlin.math.sinh instead of java.lang.Math.sinh
    • Prefer the multiplatform-friendly kotlin.math.sinh over the JVM-only Math.sinh.
  • org.openrewrite.kotlin.migrate.UseKotlinMathSqrt$KtRecipe
    • Use kotlin.math.sqrt instead of java.lang.Math.sqrt
    • Prefer the multiplatform-friendly kotlin.math.sqrt over the JVM-only Math.sqrt.
  • org.openrewrite.kotlin.migrate.UseKotlinMathTan$KtRecipe
    • Use kotlin.math.tan instead of java.lang.Math.tan
    • Prefer the multiplatform-friendly kotlin.math.tan over the JVM-only Math.tan.
  • org.openrewrite.kotlin.migrate.UseKotlinMathTanh$KtRecipe
    • Use kotlin.math.tanh instead of java.lang.Math.tanh
    • Prefer the multiplatform-friendly kotlin.math.tanh over the JVM-only Math.tanh.
  • org.openrewrite.kotlin.migrate.UseKotlinNumberApis$KtRecipe
    • Use Kotlin number extensions instead of java.lang.Integer/Long/Double/Float/Boolean
    • Replaces JVM-only static helpers on boxed primitive types with their multiplatform Kotlin counterparts: parsing (s.toInt(), s.toLong(radix), s.toDouble(), s.toBoolean()), formatting (i.toString(2), l.toString(16)), bit operations (i.countOneBits(), l.rotateLeft(n)), constants (Int.MIN_VALUE), and compareTo.
  • org.openrewrite.kotlin.migrate.UseKotlinRegex$KtRecipe
    • Use kotlin.text.Regex instead of java.util.regex.Pattern
    • Replaces JVM-only java.util.regex.Pattern calls with their multiplatform Kotlin equivalents: s.toRegex() and Regex.escape(s).
  • org.openrewrite.kotlin.migrate.UseListOf$KtRecipe
    • Use listOf(x) instead of Collections.singletonList(x)
    • java.util.Collections.singletonList(x) is JVM-only; Kotlin's multiplatform listOf(x) produces an immutable single-element list.
  • org.openrewrite.kotlin.migrate.UseListReverse$KtRecipe
    • Use MutableList.reverse() instead of Collections.reverse(list)
    • java.util.Collections.reverse(list) is JVM-only; Kotlin's MutableList.reverse() extension is multiplatform and reads as a receiver call.
  • org.openrewrite.kotlin.migrate.UseListShuffle$KtRecipe
    • Use MutableList.shuffle() instead of Collections.shuffle(list)
    • java.util.Collections.shuffle(list) is JVM-only; Kotlin's MutableList.shuffle() extension is multiplatform and reads as a receiver call.
  • org.openrewrite.kotlin.migrate.UseListSort$KtRecipe
    • Use MutableList.sort() instead of Collections.sort(list)
    • java.util.Collections.sort(list) is JVM-only; Kotlin's MutableList.sort() extension is multiplatform and reads as a receiver call.
  • org.openrewrite.kotlin.migrate.UseLocalizedMessage$KtRecipe
    • Use Throwable.localizedMessage instead of Throwable.message
    • Prefer the i18n-aware localizedMessage property over message when surfacing exception text to end users. Both are Kotlin properties on java.lang.Throwable — the rewrite is a pure property-access rename.
  • org.openrewrite.kotlin.migrate.UseLongArrayBinarySearch$KtRecipe
    • Use LongArray.binarySearch() instead of Arrays.binarySearch(arr, key)
    • java.util.Arrays.binarySearch(arr: long[], key: Long) is JVM-only; arr.binarySearch(key) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseLongArrayContentEquals$KtRecipe
    • Use LongArray.contentEquals() instead of Arrays.equals(a, b)
    • java.util.Arrays.equals(a: long[], b: long[]) is JVM-only; a.contentEquals(b) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseLongArrayContentHashCode$KtRecipe
    • Use LongArray.contentHashCode() instead of Arrays.hashCode(arr)
    • java.util.Arrays.hashCode(arr: long[]) is JVM-only; arr.contentHashCode() is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseLongArrayContentToString$KtRecipe
    • Use LongArray.contentToString() instead of Arrays.toString(arr)
    • java.util.Arrays.toString(arr: long[]) is JVM-only; arr.contentToString() is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseLongArrayCopyOf$KtRecipe
    • Use LongArray.copyOf() instead of Arrays.copyOf(arr, newLength)
    • java.util.Arrays.copyOf(arr: long[], newLength) is JVM-only; arr.copyOf(newLength) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseLongArrayFill$KtRecipe
    • Use LongArray.fill() instead of Arrays.fill(arr, value)
    • java.util.Arrays.fill(arr: long[], value) is JVM-only; arr.fill(value) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseLongArraySort$KtRecipe
    • Use LongArray.sort() instead of Arrays.sort(arr)
    • java.util.Arrays.sort(arr: long[]) is JVM-only; arr.sort() is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseLongCountOneBits$KtRecipe
    • Use Long.countOneBits() instead of java.lang.Long.bitCount
    • Long.bitCount(l) is the JVM-only popcount; the multiplatform l.countOneBits() extension reads as a receiver call.
  • org.openrewrite.kotlin.migrate.UseLongFloorDiv$KtRecipe
    • Use Long.floorDiv instead of java.lang.Math.floorDiv
    • Math.floorDiv(a, b) becomes a.floorDiv(b) — the multiplatform Kotlin extension on Long.
  • org.openrewrite.kotlin.migrate.UseLongMod$KtRecipe
    • Use Long.mod() instead of java.lang.Math.floorMod
    • Math.floorMod(a, b) becomes a.mod(b) for the (Long, Long) overload. Kotlin's Long.mod matches Math.floorMod floored-division semantics.
  • org.openrewrite.kotlin.migrate.UseLongModInt$KtRecipe
    • Use Long.mod(Int) instead of java.lang.Math.floorMod
    • Math.floorMod(a: Long, b: Int): Int becomes a.mod(b). Like the Java overload, Long.mod(Int) returns an Int.
  • org.openrewrite.kotlin.migrate.UseLongRotateLeft$KtRecipe
    • Use Long.rotateLeft(n) instead of Long.rotateLeft(l, n)
    • java.lang.Long.rotateLeft(l, n) is JVM-only; the multiplatform l.rotateLeft(n) extension reads as a receiver call.
  • org.openrewrite.kotlin.migrate.UseLongRotateRight$KtRecipe
    • Use Long.rotateRight(n) instead of Long.rotateRight(l, n)
    • java.lang.Long.rotateRight(l, n) is JVM-only; the multiplatform l.rotateRight(n) extension reads as a receiver call.
  • org.openrewrite.kotlin.migrate.UseLongToString$KtRecipe
    • Use Long.toString() instead of Long.toString(l)
    • java.lang.Long.toString(l) is the JVM-only spelling; l.toString() reads as a receiver call and is multiplatform.
  • org.openrewrite.kotlin.migrate.UseLongToStringWithRadix$KtRecipe
    • Use Long.toString(radix) instead of Long.toString(l, radix)
    • java.lang.Long.toString(l, radix) is the JVM-only spelling; l.toString(radix) is the multiplatform Kotlin receiver call.
  • org.openrewrite.kotlin.migrate.UseLowercase$KtRecipe
    • Use lowercase() instead of toLowerCase()
    • String.toLowerCase() was deprecated in Kotlin 1.5 in favor of the locale-explicit lowercase().
  • org.openrewrite.kotlin.migrate.UseLowercaseChar$KtRecipe
    • Use Char.lowercaseChar() instead of Char.toLowerCase()
    • Char.toLowerCase() was deprecated in Kotlin 1.5 in favor of the locale-explicit lowercaseChar().
  • org.openrewrite.kotlin.migrate.UseLowercaseWithLocale$KtRecipe
    • Use lowercase(locale) instead of toLowerCase(locale)
    • The JVM String.toLowerCase(Locale) overload was deprecated in Kotlin 1.5 alongside the no-arg form; the replacement lowercase(Locale) keeps the locale parameter and uses the new spelling.
  • org.openrewrite.kotlin.migrate.UseModernKotlinStdlibApis$KtRecipe
    • Use modern Kotlin stdlib idioms
    • Replaces Kotlin stdlib APIs deprecated between 1.4 and 2.0 with their modern equivalents, and adopts newer language features (open-ended range ..<, enumEntries<T>()) where the older spelling still compiles but reads worse.
  • org.openrewrite.kotlin.migrate.UseRangeUntilOperator$KtRecipe
    • Use ..< instead of until
    • Kotlin 1.7.20 introduced the ..< open-ended range operator. Prefer it over the older until infix function.
  • org.openrewrite.kotlin.migrate.UseReadln$KtRecipe
    • Use readln() instead of readLine()!!
    • Kotlin 1.6 introduced readln() to replace the common readLine()!! idiom — the new spelling makes the EOF-throws contract explicit and drops the not-null assertion.
  • org.openrewrite.kotlin.migrate.UseReadlnOrNull$KtRecipe
    • Use readlnOrNull() instead of readLine()
    • readLine() was deprecated in Kotlin 1.6 in favor of readlnOrNull() (and the asserting readln()), which spells the EOF-handling intent explicitly.
  • org.openrewrite.kotlin.migrate.UseRegexEscape$KtRecipe
    • Use Regex.escape(s) instead of Pattern.quote(s)
    • java.util.regex.Pattern.quote(s) migrates to kotlin.text.Regex.escape(s) — same behavior, multiplatform.
  • org.openrewrite.kotlin.migrate.UseRemoveAtLastIndexForRemoveLast$KtRecipe
    • Use removeAt(lastIndex) instead of removeLast()
    • MutableList.removeLast() was deprecated in Kotlin 2.0 alongside removeFirst() for the same SequencedCollection conflict. Prefer removeAt(lastIndex).
  • org.openrewrite.kotlin.migrate.UseRemoveAtZeroForRemoveFirst$KtRecipe
    • Use removeAt(0) instead of removeFirst()
    • MutableList.removeFirst() was deprecated in Kotlin 2.0 because Java 21's SequencedCollection.removeFirst() introduced a conflicting signature with different return-on-empty semantics. Prefer removeAt(0) to keep the throwing behavior unambiguous.
  • org.openrewrite.kotlin.migrate.UseSetOf$KtRecipe
    • Use setOf(x) instead of Collections.singleton(x)
    • java.util.Collections.singleton(x) is JVM-only; Kotlin's multiplatform setOf(x) produces an immutable single-element set.
  • org.openrewrite.kotlin.migrate.UseShortArrayBinarySearch$KtRecipe
    • Use ShortArray.binarySearch() instead of Arrays.binarySearch(arr, key)
    • java.util.Arrays.binarySearch(arr: short[], key: Short) is JVM-only; arr.binarySearch(key) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseShortArrayContentEquals$KtRecipe
    • Use ShortArray.contentEquals() instead of Arrays.equals(a, b)
    • java.util.Arrays.equals(a: short[], b: short[]) is JVM-only; a.contentEquals(b) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseShortArrayContentHashCode$KtRecipe
    • Use ShortArray.contentHashCode() instead of Arrays.hashCode(arr)
    • java.util.Arrays.hashCode(arr: short[]) is JVM-only; arr.contentHashCode() is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseShortArrayContentToString$KtRecipe
    • Use ShortArray.contentToString() instead of Arrays.toString(arr)
    • java.util.Arrays.toString(arr: short[]) is JVM-only; arr.contentToString() is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseShortArrayCopyOf$KtRecipe
    • Use ShortArray.copyOf() instead of Arrays.copyOf(arr, newLength)
    • java.util.Arrays.copyOf(arr: short[], newLength) is JVM-only; arr.copyOf(newLength) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseShortArrayFill$KtRecipe
    • Use ShortArray.fill() instead of Arrays.fill(arr, value)
    • java.util.Arrays.fill(arr: short[], value) is JVM-only; arr.fill(value) is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseShortArraySort$KtRecipe
    • Use ShortArray.sort() instead of Arrays.sort(arr)
    • java.util.Arrays.sort(arr: short[]) is JVM-only; arr.sort() is the multiplatform Kotlin extension.
  • org.openrewrite.kotlin.migrate.UseStringToBoolean$KtRecipe
    • Use String.toBoolean() instead of java.lang.Boolean.parseBoolean(s)
    • Java-idiom Boolean.parseBoolean(s) migrates to the Kotlin extension s.toBoolean(); for strict parsing that throws on non-true/false, use s.toBooleanStrict() instead.
  • org.openrewrite.kotlin.migrate.UseStringToDouble$KtRecipe
    • Use String.toDouble() instead of java.lang.Double.parseDouble(s)
    • Java-idiom Double.parseDouble(s) migrates to the Kotlin extension s.toDouble().
  • org.openrewrite.kotlin.migrate.UseStringToFloat$KtRecipe
    • Use String.toFloat() instead of java.lang.Float.parseFloat(s)
    • Java-idiom Float.parseFloat(s) migrates to the Kotlin extension s.toFloat().
  • org.openrewrite.kotlin.migrate.UseStringToInt$KtRecipe
    • Use String.toInt() instead of Integer.parseInt(s)
    • Java-idiom Integer.parseInt(s) migrates to the Kotlin extension s.toInt(). The behavior is identical — both throw NumberFormatException on invalid input.
  • org.openrewrite.kotlin.migrate.UseStringToIntWithRadix$KtRecipe
    • Use String.toInt(radix) instead of Integer.parseInt(s, radix)
    • Integer.parseInt(s, radix) is JVM-only; the Kotlin extension s.toInt(radix) is multiplatform and reads as a receiver call.
  • org.openrewrite.kotlin.migrate.UseStringToLong$KtRecipe
    • Use String.toLong() instead of java.lang.Long.parseLong(s)
    • Java-idiom Long.parseLong(s) migrates to the Kotlin extension s.toLong().
  • org.openrewrite.kotlin.migrate.UseStringToLongWithRadix$KtRecipe
    • Use String.toLong(radix) instead of Long.parseLong(s, radix)
    • java.lang.Long.parseLong(s, radix) is JVM-only; the Kotlin extension s.toLong(radix) is multiplatform and reads as a receiver call.
  • org.openrewrite.kotlin.migrate.UseStringToRegex$KtRecipe
    • Use String.toRegex() instead of Pattern.compile(s)
    • java.util.regex.Pattern.compile(s) migrates to the Kotlin extension s.toRegex(), which returns a kotlin.text.Regex and reads more naturally.
  • org.openrewrite.kotlin.migrate.UseSumOf$KtRecipe
    • Use sumOf instead of sumBy
    • Iterable.sumBy \{ … \} was deprecated in Kotlin 1.5 in favor of the type-inferred sumOf \{ … \} (which avoids the unchecked Int return).
  • org.openrewrite.kotlin.migrate.UseSumOfDouble$KtRecipe
    • Use sumOf instead of sumByDouble
    • Iterable.sumByDouble \{ … \} was deprecated in Kotlin 1.5 in favor of the type-inferred sumOf \{ … \}.
  • org.openrewrite.kotlin.migrate.UseUppercase$KtRecipe
    • Use uppercase() instead of toUpperCase()
    • String.toUpperCase() was deprecated in Kotlin 1.5 in favor of the locale-explicit uppercase().
  • org.openrewrite.kotlin.migrate.UseUppercaseChar$KtRecipe
    • Use Char.uppercaseChar() instead of Char.toUpperCase()
    • Char.toUpperCase() was deprecated in Kotlin 1.5 in favor of the locale-explicit uppercaseChar().
  • org.openrewrite.kotlin.migrate.UseUppercaseWithLocale$KtRecipe
    • Use uppercase(locale) instead of toUpperCase(locale)
    • The JVM String.toUpperCase(Locale) overload was deprecated in Kotlin 1.5 alongside the no-arg form; the replacement uppercase(Locale) keeps the locale parameter and uses the new spelling.
  • org.openrewrite.kotlin.migrate.UseValueClass$KtRecipe
    • Use @JvmInline value class instead of inline class
    • inline class was deprecated in Kotlin 1.5 in favor of the explicit @JvmInline value class pair; the new spelling separates the JVM mapping (@JvmInline) from the language-level semantics (value).
  • org.openrewrite.kotlin.performance.CollapseFilterTerminals$KtRecipe
    • Collapse filter \{ p \}.<terminal>() chains
    • Folds predicate-taking terminals (first, last, count, any, none, single, firstOrNull, lastOrNull, singleOrNull) and sumOf/maxOf/minOf selectors into the upstream filter or map, avoiding the intermediate list materialization.
  • org.openrewrite.kotlin.performance.CollapseSortAndReverse$KtRecipe
    • Collapse sorted().first/last() and reversed().first/last() chains
    • Replaces O(n log n) sorts whose only purpose is to read one element with the equivalent O(n) min/max/minBy/maxBy, and elides unnecessary reversed() copies before first/last.
  • org.openrewrite.kotlin.performance.FindAllocationsInCollectionLambdas$KtRecipe
    • Find expensive allocations inside collection lambdas
    • Search-only recipes that flag heavyweight allocations sitting inside the trailing lambda of a map / filter / forEach / flatMap call. Each such lambda runs once per element — the same allocation-cost profile as a loop body.
  • org.openrewrite.kotlin.performance.FindAllocationsInHotPaths$KtRecipe
    • Find expensive allocations on hot paths
    • Search-only recipes that flag heavyweight allocations sitting on a path the runtime exercises repeatedly — inside a loop, inside View.onDraw/onMeasure/onLayout. Each match shows up as a SearchResult for review; nothing is rewritten automatically.
  • org.openrewrite.kotlin.performance.FindAllocationsInOnDraw$KtRecipe
    • Find graphics allocations inside View.onDraw
    • Android views call onDraw on every frame. Allocating Paint, Path, Rect, RectF, Region, Matrix, or Bitmap instances per draw causes GC pressure and dropped frames — hoist them into field initializers or lazy \{ \} properties.
  • org.openrewrite.kotlin.performance.FindAllocationsInOnLayout$KtRecipe
    • Find graphics allocations inside View.onLayout
    • onLayout runs whenever a view's children are repositioned. Allocating Paint, Path, Rect, RectF, Region, Matrix, or Bitmap instances per layout causes per-frame GC pressure — hoist them to fields.
  • org.openrewrite.kotlin.performance.FindAllocationsInOnMeasure$KtRecipe
    • Find graphics allocations inside View.onMeasure
    • onMeasure runs whenever the measurement pass walks a view. Allocating Paint, Path, Rect, RectF, Region, Matrix, or Bitmap instances per measure makes layout reflows hit the GC — hoist them to fields.
  • org.openrewrite.kotlin.performance.FindBase64GetDecoderInLoops$KtRecipe
    • Find Base64.getDecoder() calls inside loops
    • Base64.getDecoder() returns a shared singleton, so the call itself is cheap — but reading the decoder from a final field is cheaper still. Hoist the decoder to a top-level private val for clarity.
  • org.openrewrite.kotlin.performance.FindBigDecimalFromStringInLoops$KtRecipe
    • Find BigDecimal("...") allocations inside loops
    • Parsing a BigDecimal from a String literal inside a loop reparses the same value every iteration. Hoist the literal BigDecimal out of the loop or use a cached constant.
  • org.openrewrite.kotlin.performance.FindBigIntegerFromStringInLoops$KtRecipe
    • Find BigInteger("...") allocations inside loops
    • Parsing a BigInteger from a String literal inside a loop reparses the same value every iteration. Hoist the literal BigInteger to a top-level property or BigInteger.valueOf constant.
  • org.openrewrite.kotlin.performance.FindBigIntegerValueOfInLoops$KtRecipe
    • Find BigInteger.valueOf(long) calls inside loops
    • BigInteger.valueOf caches small values (-16..16) but allocates fresh BigInteger instances outside that range. Loop bodies frequently feed it dynamic values — hoist a constant where possible or accept the allocation cost.
  • org.openrewrite.kotlin.performance.FindCalendarGetInstanceInLoops$KtRecipe
    • Find Calendar.getInstance() calls inside loops
    • Calendar.getInstance() resolves the default TimeZone and Locale and allocates a fresh GregorianCalendar on every call. Inside a loop the timezone lookup dominates — hoist the calendar or migrate to java.time types.
  • org.openrewrite.kotlin.performance.FindCharsetForNameInLoops$KtRecipe
    • Find Charset.forName(...) calls inside loops
    • Charset.forName("UTF-8") walks the charset alias map on every call. Prefer the cached constants on kotlin.text.Charsets (Charsets.UTF_8, Charsets.ISO_8859_1, etc.) — or hoist a single Charset out of the loop.
  • org.openrewrite.kotlin.performance.FindClassForNameInLoops$KtRecipe
    • Find Class.forName calls inside loops
    • Class.forName(...) walks the classloader hierarchy on every call. Resolving the same class on every loop iteration burns CPU — cache the resolved Class<*> in a top-level property.
  • org.openrewrite.kotlin.performance.FindDateTimeFormatterInLoops$KtRecipe
    • Find DateTimeFormatter.ofPattern allocations inside loops
    • DateTimeFormatter.ofPattern(...) parses the pattern up-front. Doing that on every loop iteration burns CPU repeatedly — hoist the formatter into a top-level property. Unlike SimpleDateFormat, DateTimeFormatter is thread-safe, so the hoisted instance can be shared.
  • org.openrewrite.kotlin.performance.FindEagerMapOnSequence$KtRecipe
    • Find seq.toList().map \{ ... \} patterns
    • Calling .toList() on a Sequence and then .map \{ … \} materializes the full sequence into a List and then walks it again — defeating the lazy-pipeline purpose of Sequence. Drop the toList() so the map stays in the sequence.
  • org.openrewrite.kotlin.performance.FindFileNewBufferedReaderInLoops$KtRecipe
    • Find File(...).bufferedReader() calls inside loops
    • Constructing a File and opening a BufferedReader per loop iteration multiplies the OS-level open/read/close cost. If the same path is read each pass, read it once before the loop; if every iteration reads a different path, batch the work or reuse a Reader.
  • org.openrewrite.kotlin.performance.FindForEachWithIndexedAccess$KtRecipe
    • Find for (i in xs.indices) \{ val x = xs[i] \} patterns
    • Iterating over xs.indices and indexing back into xs[i] is the explicit-index form of xs.forEachIndexed \{ i, x -> \}. The indexed form is clearer and avoids re-resolving xs[i] on every access.
  • org.openrewrite.kotlin.performance.FindGsonInCollectionLambdas$KtRecipe
    • Find Gson() allocations inside collection lambdas
    • Gson's default constructor builds the type-adapter registry up front. Allocating a fresh Gson per element repeats that work — hoist one Gson instance to a top-level property.
  • org.openrewrite.kotlin.performance.FindGsonInLoops$KtRecipe
    • Find Gson() allocations inside loops
    • Gson's default constructor builds the type-adapter registry up front. Allocating one per loop iteration repeats that work — hoist a single Gson instance to a top-level property.
  • org.openrewrite.kotlin.performance.FindIterationSmells$KtRecipe
    • Find iteration-shape smells
    • Flags iteration idioms that have a clearer, allocation-equivalent Kotlin form — currently the for (i in xs.indices) \{ val x = xs[i] \} shape that forEachIndexed \{ i, x -> \} replaces.
  • org.openrewrite.kotlin.performance.FindLargeListPipeline$KtRecipe
    • Find long List pipelines that should use Sequence
    • A pipeline with three or more chained collection operations (map/filter/flatMap/etc.) on a List materializes an intermediate collection per stage. Long pipelines on large inputs typically run faster (and allocate less) as xs.asSequence().…toList().
  • org.openrewrite.kotlin.performance.FindListOfInLoops$KtRecipe
    • Find listOf(...) calls inside loops
    • An immutable listOf(...) built inside a loop allocates a fresh list every iteration. If the contents are constant, hoist the list to a val outside the loop. If the contents change per iteration, the allocation is necessary — review and accept.
  • org.openrewrite.kotlin.performance.FindLocaleConstructionInLoops$KtRecipe
    • Find Locale(...) allocations inside loops
    • Locale("en", "US") walks the locale provider list on every construction. For common locales prefer the cached constants on Locale (e.g. Locale.US, Locale.ENGLISH). Otherwise hoist a single Locale out of the loop.
  • org.openrewrite.kotlin.performance.FindLoggerFactoryGetLoggerInCollectionLambdas$KtRecipe
    • Find LoggerFactory.getLogger calls inside collection lambdas
    • LoggerFactory.getLogger(...) resolves the logger through SLF4J on every call. Inside a collection lambda that resolves the same logger per element — hoist it to a private val companion property.
  • org.openrewrite.kotlin.performance.FindLoggerFactoryGetLoggerInLoops$KtRecipe
    • Find LoggerFactory.getLogger calls inside loops
    • LoggerFactory.getLogger(...) resolves the logger through the SLF4J binding on every call. Hoist the logger to a private val companion property — there's exactly one logger per class.
  • org.openrewrite.kotlin.performance.FindLoopAllocations$KtRecipe
    • Find collection construction inside loops
    • Flags listOf / mutableListOf / mutableMapOf calls that allocate a fresh collection on every loop iteration. Hoist constants or clear()-and-reuse a single instance.
  • org.openrewrite.kotlin.performance.FindMessageDigestGetInstanceInLoops$KtRecipe
    • Find MessageDigest.getInstance calls inside loops
    • MessageDigest.getInstance("MD5") walks the security-provider list on every call. Hoist the digest to a per-thread cache or reset it per use.
  • org.openrewrite.kotlin.performance.FindMutableListOfInLoops$KtRecipe
    • Find mutableListOf<T>() allocations inside loops
    • A mutableListOf<T>() allocated per iteration produces garbage proportional to the loop count. If the list is filled and consumed each pass, consider clear()-and-reuse on a single hoisted list.
  • org.openrewrite.kotlin.performance.FindMutableMapOfInLoops$KtRecipe
    • Find mutableMapOf<K, V>() allocations inside loops
    • A mutableMapOf<K, V>() allocated per iteration produces garbage proportional to the loop count. If the map is filled and consumed each pass, consider clear()-and-reuse on a single hoisted map.
  • org.openrewrite.kotlin.performance.FindObjectMapperInCollectionLambdas$KtRecipe
    • Find Jackson ObjectMapper() allocations inside collection lambdas
    • Allocating a fresh ObjectMapper per element rebuilds Jackson's module/serializer registry on every call. Hoist one mapper to a top-level property — it is thread-safe once configured.
  • org.openrewrite.kotlin.performance.FindObjectMapperInLoops$KtRecipe
    • Find Jackson ObjectMapper() allocations inside loops
    • Jackson's ObjectMapper is expensive to construct — it builds the default module and serializer registries on every allocation. Hoist a single ObjectMapper instance to a top-level property; it is thread-safe once configured.
  • org.openrewrite.kotlin.performance.FindOptionalGetInLoops$KtRecipe
    • Find Optional.get() calls inside loops
    • Optional.get() throws NoSuchElementException when the optional is empty — the loop body usually relies on a preceding isPresent check. Prefer orElse, orElseThrow, or ifPresent \{ \} to make the empty branch explicit and avoid the double-check.
  • org.openrewrite.kotlin.performance.FindPatternCompileInCollectionLambdas$KtRecipe
    • Find Pattern.compile calls inside collection lambdas
    • Pattern.compile(...) parses the pattern up front; running it inside a map/filter/forEach lambda recompiles the pattern for every element. Hoist the Pattern to a top-level property.
  • org.openrewrite.kotlin.performance.FindPatternCompileInLoops$KtRecipe
    • Find Pattern.compile allocations inside loops
    • Compiling a java.util.regex.Pattern is expensive — allocating one inside a loop recompiles it on every iteration. Hoist the Pattern out of the loop or cache it in a top-level property.
  • org.openrewrite.kotlin.performance.FindRegexAllocationsInCollectionLambdas$KtRecipe
    • Find Regex allocations inside collection lambdas
    • A Regex allocated inside the lambda passed to map, filter, forEach, flatMap, etc. is compiled once per element. Hoist the regex to a top-level property — collection-pipeline lambdas run on every element, just like a loop body.
  • org.openrewrite.kotlin.performance.FindRegexAllocationsInLoops$KtRecipe
    • Find Regex allocations inside loops
    • Compiling a Regex is expensive — allocating one inside a loop pays the cost on every iteration. Hoist the regex out of the loop or cache it in a top-level property.
  • org.openrewrite.kotlin.performance.FindSequencePipelineSmells$KtRecipe
    • Find Sequence/List pipeline shape smells
    • Flags pipelines where the List/Sequence choice fights the data flow — long List pipelines that materialize between every stage, and Sequence pipelines that eagerly fall back to List mid-pipeline.
  • org.openrewrite.kotlin.performance.FindSimpleDateFormatInLoops$KtRecipe
    • Find SimpleDateFormat allocations inside loops
    • SimpleDateFormat parses its pattern string in the constructor and is not thread-safe — allocating one per iteration is a common per-row hot spot. Hoist it out of the loop or use a thread-local cache (or migrate to DateTimeFormatter).
  • org.openrewrite.kotlin.performance.FindStringConcatSmells$KtRecipe
    • Find string-allocation smells
    • Flags s = s + "…"-inside-loop patterns that allocate a fresh String on every iteration. The StringBuilder.length-vs-size rewrite lives in the autofix bundle.
  • org.openrewrite.kotlin.performance.FindStringFormatInCollectionLambdas$KtRecipe
    • Find String.format calls inside collection lambdas
    • String.format(...) reparses the format string on every call. Inside a collection lambda that runs per element — prefer Kotlin string templates ("$\{x\}") or hoist a Formatter.
  • org.openrewrite.kotlin.performance.FindStringFormatInLoops$KtRecipe
    • Find String.format calls inside loops
    • String.format(...) parses the format string on every call. Inside a loop this re-parses the same template every iteration — prefer string templates ("$\{x\}") or extract the Formatter if you must use %-style specifiers.
  • org.openrewrite.kotlin.performance.FindStringPlusInLoop$KtRecipe
    • Find s = s + "..." string concatenation inside loops
    • Repeated String concatenation inside a loop allocates a new String on every iteration — each + produces a fresh StringBuilder. Prefer building once with StringBuilder (or buildString \{ … \}).
  • org.openrewrite.kotlin.performance.FindURIConstructorInLoops$KtRecipe
    • Find URI("...") allocations inside loops
    • java.net.URI's constructor parses and validates the URI string. Doing that per loop iteration burns CPU on the same string — hoist constants out of the loop.
  • org.openrewrite.kotlin.performance.FindURLConstructorInLoops$KtRecipe
    • Find URL("...") allocations inside loops
    • java.net.URL's constructor parses the URL string and dispatches through URLStreamHandlerFactory. Inside a loop that adds up — hoist URLs that don't change per iteration, or migrate to URI/HttpRequest builders.
  • org.openrewrite.kotlin.performance.ImproveKotlinPerformance$KtRecipe
    • Improve performance of Kotlin code
    • Autofix-only performance bundle: collapses allocating call chains (filter/map/sort/reverse), promotes Compose primitive state holders, and rewrites StringBuilder.length to size. Excludes the search-only Find* recipes so the run output is just diffs, not a flood of search results.
  • org.openrewrite.kotlin.performance.Performance$KtRecipe
    • Apply Kotlin performance idioms
    • Opinionated bundle of every performance recipe in this module: chain collapses (filter/map/sort/reverse), Compose primitive-state holders, and hot-path allocation finders. Search-result recipes coexist with rewriting recipes — for diff-only output, use ImproveKotlinPerformance instead.
  • org.openrewrite.kotlin.performance.UseAnyWithPredicate$KtRecipe
    • Use any \{ predicate \} instead of filter \{ predicate \}.isNotEmpty()
    • any \{ predicate \} short-circuits on the first match and avoids materializing the intermediate filtered list.
  • org.openrewrite.kotlin.performance.UseAnyWithPredicateInsteadOfFilterAny$KtRecipe
    • Use any \{ predicate \} instead of filter \{ predicate \}.any()
    • any \{ predicate \} short-circuits on the first match. Calling any() after filter first materializes the entire filtered list.
  • org.openrewrite.kotlin.performance.UseComposePrimitiveStateOf$KtRecipe
    • Use primitive mutable<Int|Long|Float|Double>StateOf in Compose
    • Inside @Composable functions, replaces mutableStateOf(<primitive>) with the matching primitive-specialized mutableIntStateOf/mutableLongStateOf/mutableFloatStateOf/mutableDoubleStateOf. The specialized state holders keep the wrapped value unboxed across reads and writes during recomposition.
  • org.openrewrite.kotlin.performance.UseCountWithPredicate$KtRecipe
    • Use count \{ predicate \} instead of filter \{ predicate \}.count()
    • Folding the predicate into count avoids materializing the intermediate filtered list.
  • org.openrewrite.kotlin.performance.UseFilterNotToForFilterNotToMutableList$KtRecipe
    • Use filterNotTo(mutableListOf(), p) instead of filterNot(p).toMutableList()
    • filterNot \{ p \} allocates a List<T> and toMutableList copies it. filterNotTo(mutableListOf(), p) writes directly into the target without the intermediate.
  • org.openrewrite.kotlin.performance.UseFilterToForFilterToMutableList$KtRecipe
    • Use filterTo(mutableListOf(), p) instead of filter(p).toMutableList()
    • filter \{ p \} allocates a List<T> and toMutableList copies it. filterTo(mutableListOf(), p) writes directly into the target without the intermediate.
  • org.openrewrite.kotlin.performance.UseFirstForReversedLast$KtRecipe
    • Use first() instead of reversed().last()
    • reversed() allocates an intermediate reversed copy. first() returns the same element directly.
  • org.openrewrite.kotlin.performance.UseFirstNotNullOfForMapFirst$KtRecipe
    • Use firstNotNullOf \{ f \} instead of mapNotNull \{ f \}.first()
    • firstNotNullOf short-circuits on the first non-null result. mapNotNull \{ f \}.first() walks the whole input.
  • org.openrewrite.kotlin.performance.UseFirstNotNullOfOrNullForMapFirstOrNull$KtRecipe
    • Use firstNotNullOfOrNull \{ f \} instead of mapNotNull \{ f \}.firstOrNull()
    • firstNotNullOfOrNull short-circuits on the first non-null result. mapNotNull \{ f \}.firstOrNull() walks the whole input.
  • org.openrewrite.kotlin.performance.UseFirstOrNullWithPredicate$KtRecipe
    • Use firstOrNull \{ predicate \} instead of filter \{ predicate \}.firstOrNull()
    • Folding the predicate into firstOrNull short-circuits on the first match and avoids materializing the intermediate filtered list.
  • org.openrewrite.kotlin.performance.UseFirstWithPredicate$KtRecipe
    • Use first \{ predicate \} instead of filter \{ predicate \}.first()
    • Folding the predicate into first avoids materializing the intermediate filtered list and short-circuits on the first match.
  • org.openrewrite.kotlin.performance.UseFlatMapToForFlatMapToMutableList$KtRecipe
    • Use flatMapTo(mutableListOf(), f) instead of flatMap(f).toMutableList()
    • flatMap \{ f \} allocates a List<R> and toMutableList copies it. flatMapTo(mutableListOf(), f) writes directly into the target without the intermediate.
  • org.openrewrite.kotlin.performance.UseLastForReversedFirst$KtRecipe
    • Use last() instead of reversed().first()
    • reversed() allocates an intermediate reversed copy. last() walks to the same element directly.
  • org.openrewrite.kotlin.performance.UseLastOrNullWithPredicate$KtRecipe
    • Use lastOrNull \{ predicate \} instead of filter \{ predicate \}.lastOrNull()
    • Folding the predicate into lastOrNull avoids materializing the intermediate filtered list.
  • org.openrewrite.kotlin.performance.UseLastWithPredicate$KtRecipe
    • Use last \{ predicate \} instead of filter \{ predicate \}.last()
    • Folding the predicate into last avoids materializing the intermediate filtered list.
  • org.openrewrite.kotlin.performance.UseLengthForStringBuilderSize$KtRecipe
    • Use sb.length instead of sb.toString().length
    • Calling toString() on a StringBuilder allocates a snapshot String just to read its length. StringBuilder exposes length directly without the copy.
  • org.openrewrite.kotlin.performance.UseMapNotNullForMapFilterNotNull$KtRecipe
    • Use mapNotNull \{ f \} instead of map \{ f \}.filterNotNull()
    • mapNotNull drops nulls in the same pass that produces them. map \{ f \}.filterNotNull() materializes the full List<R?> first.
  • org.openrewrite.kotlin.performance.UseMapToForMapToMutableList$KtRecipe
    • Use mapTo(mutableListOf(), f) instead of map(f).toMutableList()
    • map \{ f \} allocates a List<R> and toMutableList copies it. mapTo(mutableListOf(), f) writes directly into the target without the intermediate.
  • org.openrewrite.kotlin.performance.UseMaxByForSortedByLast$KtRecipe
    • Use maxBy \{ selector \} instead of sortedBy \{ selector \}.last()
    • sortedBy(f).last() does an O(n log n) sort just to read the maximum-by-f. maxBy(f) finds it in a single linear pass.
  • org.openrewrite.kotlin.performance.UseMaxForSortedDescendingFirst$KtRecipe
    • Use max() instead of sortedDescending().first()
    • sortedDescending().first() does an O(n log n) sort just to read the maximum. max() finds it in a single linear pass.
  • org.openrewrite.kotlin.performance.UseMaxForSortedLast$KtRecipe
    • Use max() instead of sorted().last()
    • sorted().last() does an O(n log n) sort just to read the maximum. max() finds it in a single linear pass.
  • org.openrewrite.kotlin.performance.UseMaxOfWithSelector$KtRecipe
    • Use maxOf \{ selector \} instead of map \{ selector \}.max()
    • maxOf walks the input once and tracks the running maximum without materializing the intermediate List<Int>.
  • org.openrewrite.kotlin.performance.UseMinByForSortedByFirst$KtRecipe
    • Use minBy \{ selector \} instead of sortedBy \{ selector \}.first()
    • sortedBy(f).first() does an O(n log n) sort just to read the minimum-by-f. minBy(f) finds it in a single linear pass.
  • org.openrewrite.kotlin.performance.UseMinForSortedDescendingLast$KtRecipe
    • Use min() instead of sortedDescending().last()
    • sortedDescending().last() does an O(n log n) sort just to read the minimum. min() finds it in a single linear pass.
  • org.openrewrite.kotlin.performance.UseMinForSortedFirst$KtRecipe
    • Use min() instead of sorted().first()
    • sorted().first() does an O(n log n) sort just to read the minimum. min() finds it in a single linear pass.
  • org.openrewrite.kotlin.performance.UseMinOfWithSelector$KtRecipe
    • Use minOf \{ selector \} instead of map \{ selector \}.min()
    • minOf walks the input once and tracks the running minimum without materializing the intermediate List<Int>.
  • org.openrewrite.kotlin.performance.UseMutableDoubleStateOf$KtRecipe
    • Use mutableDoubleStateOf instead of mutableStateOf<Double> in Compose
    • Compose's mutableDoubleStateOf keeps the wrapped value as a primitive Double, avoiding the autobox-and-unbox cost that mutableStateOf<Double> pays on every read and write inside a recomposition.
  • org.openrewrite.kotlin.performance.UseMutableFloatStateOf$KtRecipe
    • Use mutableFloatStateOf instead of mutableStateOf<Float> in Compose
    • Compose's mutableFloatStateOf keeps the wrapped value as a primitive Float, avoiding the autobox-and-unbox cost that mutableStateOf<Float> pays on every read and write inside a recomposition.
  • org.openrewrite.kotlin.performance.UseMutableIntStateOf$KtRecipe
    • Use mutableIntStateOf instead of mutableStateOf<Int> in Compose
    • Compose's mutableIntStateOf keeps the wrapped value as a primitive Int, avoiding the autobox-and-unbox cost that mutableStateOf<Int> pays on every read and write inside a recomposition.
  • org.openrewrite.kotlin.performance.UseMutableLongStateOf$KtRecipe
    • Use mutableLongStateOf instead of mutableStateOf<Long> in Compose
    • Compose's mutableLongStateOf keeps the wrapped value as a primitive Long, avoiding the autobox-and-unbox cost that mutableStateOf<Long> pays on every read and write inside a recomposition.
  • org.openrewrite.kotlin.performance.UseNoneWithPredicate$KtRecipe
    • Use none \{ predicate \} instead of filter \{ predicate \}.isEmpty()
    • none \{ predicate \} short-circuits on the first match and avoids materializing the intermediate filtered list.
  • org.openrewrite.kotlin.performance.UseNoneWithPredicateInsteadOfFilterNone$KtRecipe
    • Use none \{ predicate \} instead of filter \{ predicate \}.none()
    • none \{ predicate \} short-circuits on the first match. Calling none() after filter first materializes the entire filtered list.
  • org.openrewrite.kotlin.performance.UseSingleOrNullWithPredicate$KtRecipe
    • Use singleOrNull \{ predicate \} instead of filter \{ predicate \}.singleOrNull()
    • Folding the predicate into singleOrNull avoids materializing the intermediate filtered list.
  • org.openrewrite.kotlin.performance.UseSingleWithPredicate$KtRecipe
    • Use single \{ predicate \} instead of filter \{ predicate \}.single()
    • Folding the predicate into single avoids materializing the intermediate filtered list and preserves the same throwing semantics on the no-match and multi-match cases.
  • org.openrewrite.kotlin.performance.UseSortedByDescendingForSortedByReversed$KtRecipe
    • Use sortedByDescending \{ f \} instead of sortedBy \{ f \}.reversed()
    • sortedByDescending sorts directly into the target order. sortedBy(f).reversed() allocates an intermediate ascending-sorted list and then a reversed copy.
  • org.openrewrite.kotlin.performance.UseSortedDescendingForSortedReversed$KtRecipe
    • Use sortedDescending() instead of sorted().reversed()
    • sortedDescending sorts directly into descending order. sorted().reversed() allocates an intermediate ascending-sorted list and then a reversed copy.
  • org.openrewrite.kotlin.performance.UseSumOfWithSelector$KtRecipe
    • Use sumOf \{ selector \} instead of map \{ selector \}.sum()
    • sumOf accumulates the result directly without materializing the intermediate List<Int>.
  • org.openrewrite.kotlin.search.FindApiSurface$KtRecipe
    • Find public-API and binary-stability surface
    • Public const val / lateinit var declarations, @OptIn / @RequiresOptIn annotations, @Deprecated declarations, and JVM-interop annotations (@JvmStatic, @JvmField, @JvmOverloads). Each match is a position where API stability or external-consumer constraints are encoded in the source.
  • org.openrewrite.kotlin.search.FindAssertJChains$KtRecipe
    • Find AssertJ assertThat(...) assertion chains
    • AssertJ chains are the heart of the test suite's verification logic. Surfacing them helps a reviewer or LLM agent locate the assertions in a long test method and reason about coverage.
  • org.openrewrite.kotlin.search.FindAtomicAllocations$KtRecipe
    • Find AtomicReference/AtomicInteger/AtomicLong/AtomicBoolean allocations
    • Atomic primitives indicate concurrent state — each one is a place where a reviewer or LLM agent should look for happens-before reasoning. In coroutine-only code, MutableStateFlow or Mutex-guarded state is usually a clearer alternative.
  • org.openrewrite.kotlin.search.FindBuildHygiene$KtRecipe
    • Find build- and source-hygiene smells
    • Wildcard imports and similar source-organization smells that obscure where names come from.
  • org.openrewrite.kotlin.search.FindClassForName$KtRecipe
    • Find Class.forName(...) calls
    • Class.forName is the entrypoint to runtime reflection — the receiver type isn't known at compile time, so type-safety analyses can't follow what happens next. Each match is a position an LLM agent should single out when reasoning about what a function can touch.
  • org.openrewrite.kotlin.search.FindClipboardAccess$KtRecipe
    • Find Android ClipboardManager access
    • Clipboard reads and writes carry user data into and out of a process boundary the user typically does not associate with the app. Each call is a candidate for review of secret-leak and accidental-paste scenarios.
  • org.openrewrite.kotlin.search.FindCommandExecutionSinks$KtRecipe
    • Find process-execution sinks (Runtime.exec, ProcessBuilder.start)
    • Spawning a process with attacker-controlled arguments is the canonical command-injection sink. Each call here is a position where a reviewer or LLM agent should verify that the argument list is statically built or properly quoted.
  • org.openrewrite.kotlin.search.FindConcurrencySurface$KtRecipe
    • Find concurrency primitives
    • Raw threads, executor-service factories, JUC locks, atomic primitives, futures, synchronized blocks, @Volatile fields, ThreadLocal allocations, and JUC coordination primitives (Semaphore, CountDownLatch, CyclicBarrier). Each match is a position to inspect for cancellation semantics, happens-before edges, and pool lifecycle.
  • org.openrewrite.kotlin.search.FindCryptoSeeds$KtRecipe
    • Find cryptographic primitive entries (KeyGenerator.generateKey, Cipher.getInstance, etc.)
    • Every cryptographic operation is a place where algorithm choice and key handling matter — Cipher.getInstance("AES") is not the same as Cipher.getInstance("AES/GCM/NoPadding"). Each match is a position for security review.
  • org.openrewrite.kotlin.search.FindDataFlowSinks$KtRecipe
    • Find data-flow sink positions
    • Locations where data crosses a trust or persistence boundary: SQL execution, filesystem writes, process execution, logger writes, outbound network, cryptographic operations, and Android clipboard access. Each match is a seed an LLM agent can connect back to upstream sources.
  • org.openrewrite.kotlin.search.FindDataFlowSources$KtRecipe
    • Find data-flow source positions
    • Locations where untrusted or configuration-controlled data enters the program: readLine/Scanner reads, environment/system-property reads, HTTP request reads, and filesystem reads. Each match is a seed an LLM agent (or human reviewer) can connect to downstream sinks.
  • org.openrewrite.kotlin.search.FindDatabaseSeeds$KtRecipe
    • Find database-query seeds
    • Individual SQL execution and JPA query construction calls (Statement.executeQuery, Statement.executeUpdate, EntityManager.createQuery). Each match is a position where a SQL/JPQL string crosses into the database layer.
  • org.openrewrite.kotlin.search.FindDeepNesting$KtRecipe
    • Find functions with nesting depth greater than 4
    • Deeply nested control flow is the canonical hard-to-read code smell. For human reviewers and LLM agents alike, nesting beyond 4 levels signals the body should be split or flattened with early returns.
  • org.openrewrite.kotlin.search.FindDeprecatedDeclarations$KtRecipe
    • Find declarations annotated @Deprecated
    • A @Deprecated declaration is API the maintainers want callers to migrate away from. Each match is a candidate to revisit either the deprecation timeline or the replacement strategy.
  • org.openrewrite.kotlin.search.FindEmptyCatchBlocks$KtRecipe
    • Find catch blocks with empty bodies
    • An empty catch block silently swallows failures — usually a bug or, at minimum, a missing comment explaining the intent. Flag for review so a reviewer or LLM agent can either log, rethrow, or document the swallow.
  • org.openrewrite.kotlin.search.FindEmptyFunctions$KtRecipe
    • Find functions with empty bodies
    • An empty function body either belongs to a stub, an abstract-method override that intentionally does nothing, or forgotten work. For a reviewer or LLM agent reading the file, each match is a position where the contract claims something happens but nothing does.
  • org.openrewrite.kotlin.search.FindEntityManagerCreateQuery$KtRecipe
    • Find EntityManager.createQuery(...) calls
    • Each JPA createQuery is a JPQL/HQL execution seed — when the query string is built from user input, the same injection class applies as for raw SQL. Flag for parameter-binding review.
  • org.openrewrite.kotlin.search.FindEnvironmentSources$KtRecipe
    • Find System.getenv(...) / System.getProperty(...) reads
    • Environment variables and system properties are operator-controlled configuration values. Each read is a configuration seam — a reviewer or LLM agent reading the code should know which knobs the program exposes.
  • org.openrewrite.kotlin.search.FindExecutorServiceFactories$KtRecipe
    • Find Executors.newXxxThreadPool factory calls
    • Each Executors.newXxx call allocates a thread pool that needs explicit lifecycle (shutdown on teardown). For services that need bounded resources, each allocation is a candidate for review — and for an LLM agent, the call site reveals where the application's parallelism budget lives.
  • org.openrewrite.kotlin.search.FindFieldReflection$KtRecipe
    • Find java.lang.reflect.Field.get/set calls
    • Direct field reads/writes via reflection bypass property accessors and private visibility. Each call is a position where invariants the surrounding code relies on can be silently violated.
  • org.openrewrite.kotlin.search.FindFileReadBytes$KtRecipe
    • Find File.readBytes() calls
    • Each File.readBytes call reads a file's raw bytes into memory — useful as a filesystem-read seed and as a hint that the program holds the whole file in memory (vs. streaming).
  • org.openrewrite.kotlin.search.FindFileReadSources$KtRecipe
    • Find filesystem read calls (File.readText, Files.readString, etc.)
    • Filesystem reads are the application's IO surface — each call is data crossing a trust boundary set by the deployment's filesystem permissions. Useful as a seed for reasoning about cold-path latency, security boundaries, and what the program depends on at runtime.
  • org.openrewrite.kotlin.search.FindFileReadText$KtRecipe
    • Find File.readText() calls
    • Each File.readText call reads bytes off the filesystem — a trust-boundary position where the file's content becomes program data. Useful as an individual read seed for path-traversal or cold-path latency analysis.
  • org.openrewrite.kotlin.search.FindFileSystemSeeds$KtRecipe
    • Find filesystem-operation seeds
    • Individual filesystem reads, writes, deletes, and copies via java.io.File and java.nio.file.Files. Each match is a position where the program crosses the filesystem trust boundary.
  • org.openrewrite.kotlin.search.FindFileWriteSinks$KtRecipe
    • Find filesystem write calls (File.writeText, Files.write, etc.)
    • Filesystem writes are persistent side effects — each call is data crossing a trust boundary in the other direction. Useful as a seed for reasoning about what the application persists and where path-traversal vulnerabilities can land.
  • org.openrewrite.kotlin.search.FindFileWriteText$KtRecipe
    • Find File.writeText(...) calls
    • Each File.writeText call writes data to disk — a persistent side effect at the application's IO boundary. Useful as an individual write seed for path-traversal or trust-boundary analysis.
  • org.openrewrite.kotlin.search.FindFilesCopy$KtRecipe
    • Find Files.copy(...) calls
    • Each Files.copy call duplicates filesystem content — flag as a write seed where source/destination path provenance and CopyOptions (REPLACE_EXISTING, etc.) should be reviewed.
  • org.openrewrite.kotlin.search.FindFilesDelete$KtRecipe
    • Find Files.delete(...) / Files.deleteIfExists(...) calls
    • Each call deletes a file from disk — a destructive filesystem operation. Flag as a seed for review of path provenance, e.g. whether the path is attacker-controlled and whether the deletion is intentional.
  • org.openrewrite.kotlin.search.FindFilesNewBufferedWriter$KtRecipe
    • Find Files.newBufferedWriter(...) calls
    • Each Files.newBufferedWriter call opens a streaming writer to a file — a long-lived write seed where charset, OpenOptions, and close handling all matter.
  • org.openrewrite.kotlin.search.FindFilesWriteString$KtRecipe
    • Find Files.writeString(...) calls
    • Each java.nio.file.Files.writeString call commits text to disk via the NIO API. Useful as an individual write seed and a position where charset and OpenOptions should be reviewed.
  • org.openrewrite.kotlin.search.FindFutureAllocations$KtRecipe
    • Find CompletableFuture / FutureTask allocations
    • CompletableFuture (and FutureTask) interleave with their own thread pool; from Kotlin, Deferred/async integrates with structured concurrency. Each allocation is a candidate to migrate or at minimum to review for cancellation handling.
  • org.openrewrite.kotlin.search.FindGenericExceptionCatch$KtRecipe
    • Find catch (e: Exception) and catch (e: Throwable) clauses
    • Catching Exception or Throwable is almost always too broad — it sweeps up NullPointerException, IllegalStateException, and OutOfMemoryError into one branch. Narrow the catch to the specific exception types the block actually handles.
  • org.openrewrite.kotlin.search.FindGodClasses$KtRecipe
    • Find classes with more than 25 methods
    • A class with this many methods has likely accreted responsibility over time. Flag for splitting along the methods' natural seams — repository vs mapper, view-model vs presenter, etc.
  • org.openrewrite.kotlin.search.FindHardcodedColorLiterals$KtRecipe
    • Find Color(0xFF...) color-literal constructions
    • Hardcoded ARGB literals inside Color(0xFF...) calls are a design-token leak — they should usually live in a theme or material color scheme. Each match is a candidate to extract to MaterialTheme.colorScheme.X.
  • org.openrewrite.kotlin.search.FindHardcodedLiterals$KtRecipe
    • Find hardcoded literals and error-handling smells
    • Hardcoded design tokens (Color(0xFF...)), empty catch blocks, overly-broad catch (e: Exception) clauses, and throw RuntimeException(...) calls. Each match is a position where intent is unclear or recovery is too broad.
  • org.openrewrite.kotlin.search.FindHotspots$KtRecipe
    • Find hotspots and complexity points
    • Surface large classes, long functions, wide parameter lists, sprawling when expressions, deep nesting, god classes, magic numbers, and unmarked self-recursion. Each match is a candidate for a reviewer (or LLM agent) to refactor or to read carefully when building a mental model of the file.
  • org.openrewrite.kotlin.search.FindHttpClientConstructions$KtRecipe
    • Find HTTP client construction sites
    • Each HTTP client construction is a place where connection pooling, timeouts, retry policy, and TLS settings are committed. Flag for review so a reviewer or LLM agent can confirm the call site picks the right policy rather than the defaults.
  • org.openrewrite.kotlin.search.FindHttpRequestSources$KtRecipe
    • Find HttpServletRequest.getParameter/getHeader/getCookies reads
    • Servlet-API request reads return raw, attacker-controlled strings. Each call is a taint root — anywhere the returned value flows into an SQL query, a filesystem path, or HTML output is a candidate vulnerability the reviewer should trace.
  • org.openrewrite.kotlin.search.FindHttpURLConnection$KtRecipe
    • Find HttpURLConnection references
    • HttpURLConnection is the JVM's built-in HTTP client — each reference is a position where external HTTP traffic is configured (timeouts, redirects, request method). Flag as an outbound-network seed.
  • org.openrewrite.kotlin.search.FindIgnoredTests$KtRecipe
    • Find @Disabled / @Ignore test annotations
    • An ignored test is a regression-detection gap — at minimum it represents technical debt; at worst it's a silenced failure that turned chronic. Each match is a candidate to triage.
  • org.openrewrite.kotlin.search.FindInsecureRandomSources$KtRecipe
    • Find non-cryptographic random sources (Math.random, kotlin.random.Random, java.util.Random)
    • Non-cryptographic randomness is fine for jitter, simulation, sampling — but each call is a position to verify that no security-relevant value (session token, password reset link, nonce) flows from it. Use SecureRandom instead in those positions.
  • org.openrewrite.kotlin.search.FindJacksonObjectMapperReadValue$KtRecipe
    • Find Jackson ObjectMapper.readValue(...) calls
    • Each readValue is a JSON-deserialization sink — when the input bytes are attacker-controlled, the configured polymorphic-typing and visibility settings of the ObjectMapper become security-relevant. Flag as a deserialization seed.
  • org.openrewrite.kotlin.search.FindJacksonObjectMapperWriteValue$KtRecipe
    • Find Jackson ObjectMapper.writeValue*(...) calls
    • Each writeValue / writeValueAsString / writeValueAsBytes call serializes a Kotlin object to JSON — useful as a seed for tracking which types cross the JSON boundary (DTO surface) and where sensitive fields might leak.
  • org.openrewrite.kotlin.search.FindJvmFieldAnnotations$KtRecipe
    • Find @JvmField annotations
    • @JvmField exposes a Kotlin property as a public Java field — bypassing the generated getter/setter and freezing the storage layout in the binary API. Each match is a constraint on future refactors.
  • org.openrewrite.kotlin.search.FindJvmOverloadsAnnotations$KtRecipe
    • Find @JvmOverloads annotations
    • @JvmOverloads emits N synthetic Java overloads for a Kotlin function with default parameters. Each annotation is a hint the function is part of the Java-facing surface; reordering parameters or changing defaults breaks the synthetic overloads.
  • org.openrewrite.kotlin.search.FindJvmStaticAnnotations$KtRecipe
    • Find @JvmStatic annotations
    • @JvmStatic declares a member function that should appear as a JVM static — a Java-interop affordance. Each annotation is a hint that the API is consumed from Java code, which constrains how it can evolve.
  • org.openrewrite.kotlin.search.FindKClassConstructors$KtRecipe
    • Find KClass.constructors access
    • Reflective access to KClass.constructors reveals every constructor of a Kotlin class at runtime. Each match is a seed where reflective allocation is plausible — opaque to static analysis.
  • org.openrewrite.kotlin.search.FindKClassDeclaredFunctions$KtRecipe
    • Find KClass.declaredFunctions / declaredMemberFunctions access
    • Access to KClass.declaredFunctions (and its variants) walks every declared function of a Kotlin class via reflection. Each call is a reflection seed — opaque to static analysis and requires kotlin-reflect.jar at runtime.
  • org.openrewrite.kotlin.search.FindKClassMembers$KtRecipe
    • Find KClass.members / memberFunctions / memberProperties access
    • Access to KClass.members (and its sibling reflective collections) requires kotlin-reflect.jar at runtime and walks every declared member of the class. Each call is a position where reflection over a Kotlin type is happening — opaque to static analysis.
  • org.openrewrite.kotlin.search.FindKClassMembersAccess$KtRecipe
    • Find Kotlin reflection (KClass.memberFunctions, KClass.members, etc.)
    • Kotlin reflection (kotlin.reflect.*) needs kotlin-reflect.jar on the classpath and adds significant cold-start cost. Each call is also a reflective dispatch — invisible to static analysis — so a reviewer/agent should know it's there.
  • org.openrewrite.kotlin.search.FindKotestSpecs$KtRecipe
    • Find Kotest spec classes
    • A class extending a Kotest spec (FunSpec, BehaviorSpec, etc.) is a test entrypoint. Listing them helps a reviewer or LLM agent map a module's test surface without crawling annotations.
  • org.openrewrite.kotlin.search.FindLargeClasses$KtRecipe
    • Find classes with more than 200 statements
    • Large classes accumulate responsibility — they bury invariants and slow every cross-cutting edit. As LLM context, an oversized class dominates the window with details that may not be relevant to the task; flagging them helps a reviewer (or an agent) decide where to split.
  • org.openrewrite.kotlin.search.FindLargeWhenBranches$KtRecipe
    • Find when expressions with more than 10 branches
    • A when with many branches is often hiding a sealed-class or strategy-table refactor — and even when it isn't, it's a hotspot a reviewer or LLM agent should see when scanning a file. Each match is a candidate for restructuring.
  • org.openrewrite.kotlin.search.FindLogWriteSinks$KtRecipe
    • Find logger write calls (info/warn/error/debug)
    • Logger calls can persistently capture user-controlled data into log aggregators — a PII-leak seed. Each match is a candidate to verify that the format arguments don't include sensitive fields or that a redaction layer wraps the call.
  • org.openrewrite.kotlin.search.FindLongFunctions$KtRecipe
    • Find functions with more than 30 statements
    • Long functions hide branching and are harder to test, refactor, and reason about. For an LLM agent reading the file, an oversized body eats tokens disproportionately and obscures the contract — flag for review or extraction.
  • org.openrewrite.kotlin.search.FindMagicNumbers$KtRecipe
    • Find numeric literals other than 0, 1, -1
    • Magic numbers in code hide units, bounds, and protocol constants from a reader. Each match is a candidate to extract to a named const val so a reviewer or LLM agent can see the intent (MAX_RETRIES, BUFFER_BYTES, etc.) rather than the bare literal.
  • org.openrewrite.kotlin.search.FindManyParameters$KtRecipe
    • Find functions with more than 5 parameters
    • A long parameter list usually signals a missing aggregate (data class, builder, parameter object). For a reviewer or LLM agent, the parameter signature is the contract — when it's too wide, the call sites become hard to read and refactor in lockstep.
  • org.openrewrite.kotlin.search.FindMethodInvoke$KtRecipe
    • Find java.lang.reflect.Method.invoke(...) calls
    • Calls through Method.invoke are reflective dispatch — the target body is opaque to static analysis. Flag for review whenever a reader needs to know which functions are actually reachable.
  • org.openrewrite.kotlin.search.FindMockkAllocations$KtRecipe
    • Find mockk<X>() / mockk(...) calls
    • Each mockk allocation is a test-time fake — the production type it stands in for is the seam under test. Flagging them helps a reviewer or LLM agent see what is real and what is faked inside a test.
  • org.openrewrite.kotlin.search.FindNetworkSinks$KtRecipe
    • Find outbound-network sinks (URL.openConnection, OkHttpClient.newCall, etc.)
    • Outbound network calls are SSRF candidates whenever the URL or request body flows from a request parameter. Each match is a seed for tracing where the program reaches into the outside world.
  • org.openrewrite.kotlin.search.FindNetworkingSeeds$KtRecipe
    • Find outbound-networking seeds
    • HTTP client and connection construction sites: HttpURLConnection, okhttp3.OkHttpClient, okhttp3.Request.Builder. Each match is a position where the program reaches out to the network.
  • org.openrewrite.kotlin.search.FindNotNullAssertions$KtRecipe
    • Find !! not-null assertions
    • Each !! is a runtime promise — when the receiver turns out to be null, the program crashes with a NullPointerException. A reviewer or LLM agent reading the code should know which positions are betting against the type system; many of them are candidates for ?.let \{ … \} or a requireNotNull with a better diagnostic.
  • org.openrewrite.kotlin.search.FindObjectOutputStreamWriteObject$KtRecipe
    • Find ObjectOutputStream.writeObject(...) calls
    • Java serialization is brittle and a known security hazard on the read side; the write side is a seed where the on-wire/on-disk format gets fixed. Flag for review when migrating away from Java serialization.
  • org.openrewrite.kotlin.search.FindOkHttpClient$KtRecipe
    • Find okhttp3.OkHttpClient constructions
    • Each OkHttpClient allocation commits a set of timeouts, interceptors, and connection-pool settings for outbound HTTP. Flag as an outbound-network seed and a configuration-policy review point.
  • org.openrewrite.kotlin.search.FindOkHttpRequestBuilder$KtRecipe
    • Find okhttp3.Request.Builder() constructions
    • Each Request.Builder() is the construction site of an outbound OkHttp request. Flag as an outbound-network seed — a reviewer or LLM agent should check the URL source and request body for attacker-controlled data.
  • org.openrewrite.kotlin.search.FindOptInAnnotations$KtRecipe
    • Find @OptIn(...) annotations
    • An @OptIn annotation acknowledges that the annotated declaration uses an experimental API. The site of the opt-in is where the contract risk lives — if the upstream changes the API, this is the file that breaks.
  • org.openrewrite.kotlin.search.FindParameterizedTests$KtRecipe
    • Find @ParameterizedTest annotations
    • Parameterized tests cover families of inputs in a single declaration. Each annotation is a position where one test class line generates many test instances — useful context when reading coverage reports.
  • org.openrewrite.kotlin.search.FindPublicConstants$KtRecipe
    • Find public const val declarations
    • A public const val is part of the binary API surface — changing its value at the source recompiles dependents, but stale clients keep the old constant inlined. Flag for awareness when reviewing API stability.
  • org.openrewrite.kotlin.search.FindPublicLateinit$KtRecipe
    • Find public lateinit var declarations
    • lateinit var defers initialization but exposes a mutable, possibly-uninitialized property. As public API, every caller can both read (and potentially trigger UninitializedPropertyAccessException) and write the field. Flag for review of encapsulation.
  • org.openrewrite.kotlin.search.FindRecursionWithoutTailrec$KtRecipe
    • Find recursive functions not marked tailrec
    • A self-recursive function that doesn't carry the tailrec modifier won't get the Kotlin compiler's stack-elimination transform. Each match is a candidate to either annotate (if the recursive call is in tail position) or rewrite to an iterative form.
  • org.openrewrite.kotlin.search.FindReentrantLockAllocations$KtRecipe
    • Find ReentrantLock() / ReentrantReadWriteLock() allocations
    • Each ReentrantLock allocation is a manual concurrency primitive. Flag for review — in coroutine code, Mutex is usually the cooperative-cancellation-friendly replacement.
  • org.openrewrite.kotlin.search.FindReflectionGetField$KtRecipe
    • Find Class.getDeclaredField / Class.getField calls
    • Each getDeclaredField / getField call is a reflective lookup of a field by name — the name is opaque to static analysis, so the field reference is invisible to rename refactorings. Flag as a reflection seed.
  • org.openrewrite.kotlin.search.FindReflectionSeeds$KtRecipe
    • Find reflection seeds
    • Field/member/constructor reflection over Java and Kotlin types (Class.getDeclaredField, KClass.members, KClass.declaredFunctions, KClass.constructors). Each match is a position where program behavior is opaque to static analysis and depends on runtime symbol lookup.
  • org.openrewrite.kotlin.search.FindReflectionSurface$KtRecipe
    • Find reflection and runtime introspection
    • Reflection entry points (Class.forName, Method.invoke, Field.get/set, kotlin.reflect calls, ServiceLoader.load), visibility overrides (setAccessible(true)), and unsafe as casts. Each match is opaque to static analysis — a reviewer/agent should know it's there before reasoning about what the program touches.
  • org.openrewrite.kotlin.search.FindRequiresOptInDeclarations$KtRecipe
    • Find declarations annotated @RequiresOptIn
    • A @RequiresOptIn annotation defines a new opt-in marker — every caller must explicitly acknowledge it via @OptIn. Each match here is a place where stability semantics are being defined, not just consumed.
  • org.openrewrite.kotlin.search.FindSemaphoreOrLatchAllocations$KtRecipe
    • Find Semaphore / CountDownLatch / CyclicBarrier allocations
    • Classic JUC coordination primitives indicate hand-rolled concurrency. In coroutine code, kotlinx.coroutines.sync.Semaphore and CompletableDeferred are the cooperative equivalents. Flag the call site for review.
  • org.openrewrite.kotlin.search.FindSerializationSeeds$KtRecipe
    • Find serialization seeds
    • Java serialization writes and Jackson read/write calls (ObjectOutputStream.writeObject, ObjectMapper.readValue, ObjectMapper.writeValue*). Each match is a position where Kotlin objects cross an external wire/disk format boundary.
  • org.openrewrite.kotlin.search.FindServiceLoader$KtRecipe
    • Find ServiceLoader.load(...) calls
    • ServiceLoader.load walks META-INF/services at boot time and instantiates each provider via reflection. The set of loaded classes isn't visible to static analysis — each call is a fan-out point for a reviewer to understand.
  • org.openrewrite.kotlin.search.FindSetAccessibleTrue$KtRecipe
    • Find AccessibleObject.setAccessible(true) calls
    • setAccessible(true) bypasses Java/Kotlin visibility. It's a strong signal of either a serialization library at work or a workaround for a missing API — either way, a reviewer/agent reading the code should be aware that visibility cannot be trusted here.
  • org.openrewrite.kotlin.search.FindSqlExecutionSinks$KtRecipe
    • Find SQL execution sinks (Statement.execute*, prepareStatement, createNativeQuery)
    • SQL execution is the canonical SQL-injection sink — every string argument that reaches one of these calls without parameter binding is a candidate vulnerability. As a data-flow seed, the call site is where untrusted strings either become parameter-bound or stay concatenated.
  • org.openrewrite.kotlin.search.FindStatementExecuteQuery$KtRecipe
    • Find Statement.executeQuery(sql) calls
    • Each executeQuery is a SQL read sink — if sql is built from user-controlled strings without binding, it's a SQL-injection candidate. Useful as an individual seed even when the broader FindSqlExecutionSinks composite is too coarse.
  • org.openrewrite.kotlin.search.FindStatementExecuteUpdate$KtRecipe
    • Find Statement.executeUpdate(sql) calls
    • Each executeUpdate is a SQL write sink — INSERT/UPDATE/DELETE built from string concatenation is the canonical injection pattern. Useful as an individual seed even when the broader composite is too coarse.
  • org.openrewrite.kotlin.search.FindStdinSources$KtRecipe
    • Find readLine() / Scanner.next*() calls
    • Standard-input reads are user-controlled bytes — every downstream use of the returned string is a candidate taint root. Tagging the call site lets a downstream analysis (human or LLM agent) trace where untrusted data flows.
  • org.openrewrite.kotlin.search.FindSynchronizedBlocks$KtRecipe
    • Find synchronized(lock) \{ ... \} calls
    • Each synchronized block is a JVM monitor-acquire/release — incompatible with coroutine cancellation and a candidate for Mutex/withLock in suspend code. Flag for review of contention and cancellation semantics.
  • org.openrewrite.kotlin.search.FindTestNameAnnotations$KtRecipe
    • Find @DisplayName(...) test annotations
    • JUnit 5 @DisplayName overrides the rendered test name. Listing them helps a reviewer or LLM agent see where the source's function name and the test's reported name diverge — relevant for triaging CI failures.
  • org.openrewrite.kotlin.search.FindTestSurface$KtRecipe
    • Find test-surface positions
    • Disabled/ignored tests, slow-tagged tests, mockk fakes, AssertJ assertion chains, and Kotest spec classes. Each match helps a reviewer or LLM agent navigate a module's test surface and verification logic.
  • org.openrewrite.kotlin.search.FindTestsTaggedSlow$KtRecipe
    • Find tests tagged @Tag("slow")
    • Slow-tagged tests usually live behind a separate CI lane. Each match is a candidate to either speed up (find the underlying source of slowness) or to verify the tag is wired into the build's test selection.
  • org.openrewrite.kotlin.search.FindThreadConstructors$KtRecipe
    • Find Thread(...) constructor calls
    • Each raw Thread(...) constructor is an unmanaged thread allocation — no pool, no lifecycle. On JVM/Android code that ships with Kotlin coroutines or a structured executor service, these are usually candidates to migrate to a managed scope.
  • org.openrewrite.kotlin.search.FindThreadLocalAllocations$KtRecipe
    • Find ThreadLocal() allocations
    • ThreadLocal ties state to a thread identity that coroutines do not preserve across suspension. Each allocation is a candidate to migrate to a CoroutineContext.Element or to confirm the call site is non-suspending.
  • org.openrewrite.kotlin.search.FindThrowGenericException$KtRecipe
    • Find throw Exception(...) and throw RuntimeException(...) calls
    • Throwing a bare Exception / RuntimeException forces every caller into a generic catch. Each match is a candidate to use a more specific exception type (IllegalArgumentException, IllegalStateException, a domain-specific subclass).
  • org.openrewrite.kotlin.search.FindUnsafeCast$KtRecipe
    • Find bare as casts (unsafe)
    • A bare as cast throws ClassCastException on a mismatch — every cast is a runtime contract the compiler can't enforce. Where the result might legitimately be the wrong type, prefer as? (returning null) so the failure surfaces as a nullable handling decision rather than an exception.
  • org.openrewrite.kotlin.search.FindVolatileFields$KtRecipe
    • Find @Volatile properties
    • @Volatile properties announce concurrent mutation — every read/write is a happens-before edge that downstream code relies on. Each match is a position a reviewer or LLM agent should inspect for memory-ordering bugs.
  • org.openrewrite.kotlin.search.FindWildcardImports$KtRecipe
    • Find import x.* wildcard imports
    • Wildcard imports pull every public name from a package into the file's symbol table — making it harder for a reviewer or LLM agent to tell where a name comes from. Each match is a candidate to expand into explicit imports.
  • org.openrewrite.kotlin.search.Search$KtRecipe
    • Surface impact-analysis findings
    • Search-only recipes that help an LLM coding agent or human reviewer build a mental map of the codebase: hotspots, hardcoded literals, reflection, concurrency primitives, public-API stability surface, dataflow source/sink locations, the test surface, and source-organization smells. Each match is a SearchResult — nothing is rewritten automatically.
  • org.openrewrite.kotlin.security.FindAesDefaultMode$KtRecipe
    • Find Cipher.getInstance("AES") calls without a mode
    • Bare "AES" defaults to AES/ECB/PKCS5Padding on the SunJCE provider — ECB mode is broken for any data with structure. Specify "AES/GCM/NoPadding" explicitly so the cipher is portable and authenticated.
  • org.openrewrite.kotlin.security.FindAllowAllHostnameVerifierLambda$KtRecipe
    • Find HostnameVerifier \{ _, _ -> true \} lambdas
    • A HostnameVerifier that returns true accepts a certificate for any hostname — defeats the purpose of TLS hostname pinning and enables straightforward MITM. Verify the hostname against the cert's CN/SAN, or use the platform default verifier.
  • org.openrewrite.kotlin.security.FindAndroidLogSensitive$KtRecipe
    • Find android.util.Log.\{d,i,v,w,e\} calls with sensitive content
    • android.util.Log writes to logcat, which on rooted devices and via adb logcat is world-readable. Don't put password, token, or any PII into log messages — production builds should strip logging via R8/ProGuard rules.
  • org.openrewrite.kotlin.security.FindAndroidSecuritySmells$KtRecipe
    • Find Android-specific security smells
    • Deprecated world-readable/writeable file modes, WebView JavaScript enablement and addJavascriptInterface exposure, plaintext SharedPreferences for sensitive data, and implicit Intent broadcasts that any app on the device can intercept.
  • org.openrewrite.kotlin.security.FindAwsAccessKeyLiteral$KtRecipe
    • Find AWS Access Key literals (AKIA…)
    • AWS access keys begin with AKIA followed by 16+ uppercase/digit characters. A literal AKIA… in source means the key is in every artifact build, every git commit, and every developer machine. Rotate immediately and load from environment or SecretsManager.
  • org.openrewrite.kotlin.security.FindBasicAuthLiteral$KtRecipe
    • Find "Basic <base64>" literals in source
    • A literal Basic <base64> header in source is a static credential; rotating it requires a deploy. Build the header from credentials loaded at startup.
  • org.openrewrite.kotlin.security.FindCipherCbcWithoutMac$KtRecipe
    • Find Cipher.getInstance("AES/CBC/...") calls — verify integrity
    • AES/CBC is unauthenticated — without a separate MAC, the ciphertext is vulnerable to padding-oracle attacks (BEAST, POODLE family). Prefer AES/GCM/NoPadding for AEAD in one step, or pair CBC with an HMAC under encrypt-then-MAC.
  • org.openrewrite.kotlin.security.FindCipherEcbMode$KtRecipe
    • Find Cipher.getInstance("AES/ECB/...") calls
    • ECB mode encrypts identical plaintext blocks to identical ciphertext blocks, leaking structure (the famous "ECB penguin"). Use AES/GCM/NoPadding or AES/CBC/PKCS5Padding with a random IV per message.
  • org.openrewrite.kotlin.security.FindCipherInitWithoutSecureRandom$KtRecipe
    • Find two-argument Cipher.init(opmode, key) calls
    • The two-argument Cipher.init(opmode, key) lets the JCE pick an IV — that IV is generated from a provider-default SecureRandom, which is fine, but for CBC/GCM you usually want to control the IV explicitly so it can be transmitted alongside the ciphertext. Pass an IvParameterSpec (or GCMParameterSpec) generated from SecureRandom.
  • org.openrewrite.kotlin.security.FindClassForNameWithNonLiteral$KtRecipe
    • Find Class.forName(...) calls with non-literal arguments
    • Class.forName(input) lets the caller choose a class to load — the classic gadget chain for deserialization-style attacks and unsafe reflection. Match against a sealed allowlist instead.
  • org.openrewrite.kotlin.security.FindCookieHttpOnlyFalse$KtRecipe
    • Find Cookie.setHttpOnly(false) calls
    • setHttpOnly(false) makes the cookie readable from JavaScript — directly exfiltratable by any XSS bug in the same origin. Set httpOnly = true for every session cookie.
  • org.openrewrite.kotlin.security.FindCookieSecureFalse$KtRecipe
    • Find Cookie.setSecure(false) calls
    • setSecure(false) lets the cookie travel over plain HTTP — anyone on the path (coffee-shop wifi, ISP) can read it. For any session cookie, set secure = true and httpOnly = true, and prefer SameSite=Strict.
  • org.openrewrite.kotlin.security.FindFilePathConcat$KtRecipe
    • Find File("..." + input) constructions
    • Concatenating user input into a File(...) path is the canonical path-traversal vector (../etc/passwd). Resolve against a fixed base with File(base, name) plus an explicit canonicalPath.startsWith(baseCanonicalPath) check.
  • org.openrewrite.kotlin.security.FindGitHubPatLiteral$KtRecipe
    • Find GitHub PAT literals (ghp_…)
    • GitHub personal access tokens begin with ghp_ and are full-scope unless the PAT is fine-grained. A literal ghp_… in source must be revoked at github.com/settings/tokens immediately.
  • org.openrewrite.kotlin.security.FindGoogleApiKeyLiteral$KtRecipe
    • Find Google API key literals (AIza…)
    • Google Cloud / Firebase / Maps API keys follow the AIza… 39-char pattern. Even when client-restricted, a leaked literal lets attackers fingerprint your project and run up bills via unrestricted endpoints.
  • org.openrewrite.kotlin.security.FindHardCodedSecrets$KtRecipe
    • Find hard-coded secret literals
    • High-confidence regex matches for AWS access keys, GitHub PATs, Stripe API keys, Google API keys, Slack tokens, and JWTs — plus a heuristic match for properties named password/secret/token/apiKey with non-empty string defaults. Each match needs immediate rotation if it is a real credential.
  • org.openrewrite.kotlin.security.FindHttpUrlLiteral$KtRecipe
    • Find URL("http://...") literal constructions
    • Constructing a java.net.URL from an http:// literal opts out of TLS. If the host genuinely is HTTP-only, document the exception; otherwise switch the literal to https://.
  • org.openrewrite.kotlin.security.FindInjectionVectors$KtRecipe
    • Find injection vectors
    • SQL string concatenation into Statement/PreparedStatement, command injection via Runtime.exec and ProcessBuilder, path traversal via File concatenation, unsafe reflection via Class.forName(input), and dynamic-script evaluation via ScriptEngine.
  • org.openrewrite.kotlin.security.FindInsecureSessionConfig$KtRecipe
    • Find insecure cookie / session configuration
    • Cookies missing the Secure or HttpOnly flag leak to plain HTTP or JavaScript. Each match should set both flags to true and consider SameSite=Strict.
  • org.openrewrite.kotlin.security.FindInsecureTls$KtRecipe
    • Find insecure TLS configuration
    • Trust-everything X509TrustManager implementations, allow-all HostnameVerifier lambdas / setters, deprecated SSL/TLS protocol versions, and plain-HTTP URL literals.
  • org.openrewrite.kotlin.security.FindIntentExplicitActionLiteral$KtRecipe
    • Find Intent("some.implicit.action") constructions
    • An Intent constructed with a string action becomes an implicit broadcast — any app declaring a matching <intent-filter> can receive it (and potentially read PII the sender attached). Prefer explicit intents with Intent(context, Activity::class.java), or send with LocalBroadcastManager / setPackage(...).
  • org.openrewrite.kotlin.security.FindJavaUtilRandomForSecurity$KtRecipe
    • Find java.util.Random() allocations
    • java.util.Random is a linear-congruential generator — its state is recoverable from a handful of outputs, so it must not produce session IDs, tokens, salts, IVs, or password reset values. Use java.security.SecureRandom for any security-adjacent randomness.
  • org.openrewrite.kotlin.security.FindJjwtSetSigningKeyLiteral$KtRecipe
    • Find JwtBuilder.setSigningKey("literal".toByteArray()) patterns
    • A hard-coded signing key compromises every token your service ever issues — anyone with the source (or the artifact, since literals end up in the constant pool) can forge tokens. Load the key from a secret store; rotate on a schedule.
  • org.openrewrite.kotlin.security.FindJjwtSignWithNone$KtRecipe
    • Find JwtBuilder.signWith(SignatureAlgorithm.NONE, ...) patterns
    • alg=none lets anyone forge a JWT — there is no signature to verify. Use HS256 (with a strong secret) or RS256/ES256 (with an asymmetric key pair).
  • org.openrewrite.kotlin.security.FindJndiLookupWithNonLiteral$KtRecipe
    • Find InitialContext.lookup(input) calls with non-literal arguments
    • Dynamic JNDI lookups are the Log4Shell (CVE-2021-44228) pattern — a controlled URL can fetch a remote class file and execute it. Pin lookup names to a literal allowlist; disable remote codebase loading.
  • org.openrewrite.kotlin.security.FindJwtAlgNoneLiteral$KtRecipe
    • Find "alg":"none" literal strings
    • Any literal containing alg=none is suspicious — even in tests, copy-paste tends to leak these into production assertions. Replace with HS256/RS256/ES256.
  • org.openrewrite.kotlin.security.FindJwtLiteral$KtRecipe
    • Find JWT literals (eyJ…-prefixed three-segment tokens)
    • A literal JWT in source is a long-lived signed token sitting in git history. Even if it's expired, it documents the claims structure and signing context. Replace with a fixture-generated token in tests; remove entirely from production code.
  • org.openrewrite.kotlin.security.FindJwtMisuse$KtRecipe
    • Find JWT misuse
    • Hard-coded JJWT signing keys, signWith(NONE) patterns that produce unsigned tokens, and literal "alg":"none" strings that show up in headers and test fixtures alike.
  • org.openrewrite.kotlin.security.FindKeyGeneratorDes$KtRecipe
    • Find KeyGenerator.getInstance("DES") calls
    • Generating a DES key feeds a known-broken cipher. Use KeyGenerator.getInstance("AES").apply \{ init(256) \} instead.
  • org.openrewrite.kotlin.security.FindKotlinRandomForSecurity$KtRecipe
    • Find kotlin.random.Random.Default references
    • kotlin.random.Random.Default delegates to a platform default RNG that on JVM is ThreadLocalRandom — not cryptographically secure. For tokens, session IDs, salts, etc. use java.security.SecureRandom.
  • org.openrewrite.kotlin.security.FindModeWorldReadable$KtRecipe
    • Find MODE_WORLD_READABLE references
    • MODE_WORLD_READABLE (and MODE_WORLD_WRITEABLE) were deprecated in API 17 and removed for security reasons — any other app on the device can read/write the file. Use the default MODE_PRIVATE mode and grant explicit cross-app access via FileProvider.
  • org.openrewrite.kotlin.security.FindNullCipher$KtRecipe
    • Find NullCipher() allocations
    • javax.crypto.NullCipher is a no-op cipher — its doFinal returns the plaintext unchanged. Useful only for testing; if it ships in production code, the data is effectively unencrypted.
  • org.openrewrite.kotlin.security.FindObjectInputStream$KtRecipe
    • Find ObjectInputStream(...) constructions
    • Java native deserialization is the source of the CVE-2015-4852 / Apache Commons gadget-chain family — any classpath gadget can fire on readObject. Replace with a JSON or Protobuf decoder; if you must keep Java serialization, install an ObjectInputFilter.
  • org.openrewrite.kotlin.security.FindPathsGetWithConcat$KtRecipe
    • Find Paths.get("..." + input) calls
    • Same path-traversal risk as File(...) concatenation. Resolve against a fixed base with Path.resolve(name) plus an explicit normalize().startsWith(base) check.
  • org.openrewrite.kotlin.security.FindPbkdf2LowIterationCount$KtRecipe
    • Find PBEKeySpec(..., iterations, ...) with low iteration counts
    • OWASP's PBKDF2 guidance (2023) recommends 600,000 iterations for SHA-256, 210,000 for SHA-512. Counts below 10,000 leak passwords to cheap GPU brute force.
  • org.openrewrite.kotlin.security.FindPredictableIv$KtRecipe
    • Find IvParameterSpec(byteArrayOf(...)) constructions with a literal IV
    • A constant IV defeats the IND-CPA guarantees of CBC/GCM/CTR — every message encrypted under the same key/IV pair leaks the same prefix structure. Generate a fresh IV per message from SecureRandom and prepend it to the ciphertext.
  • org.openrewrite.kotlin.security.FindPrepareStatementWithConcat$KtRecipe
    • Find prepareStatement("... " + x) calls
    • PreparedStatement is only safe if the SQL is a fixed template — concatenating user input into the template before prepareStatement defeats the parameter binding. Parameterize the variable portion with ?.
  • org.openrewrite.kotlin.security.FindPrintlnSensitive$KtRecipe
    • Find println("... password ...") patterns
    • println writes to stdout, which on production tends to land in container logs. Treat it like any other log sink — strip sensitive values before printing, or use a structured logger that redacts at the formatter.
  • org.openrewrite.kotlin.security.FindPrivateKeyHeaderLiteral$KtRecipe
    • Find -----BEGIN ... PRIVATE KEY----- literals
    • A PEM-formatted private key in source means the private key is in every artifact, every git commit, and every developer machine. Load from a secrets store or a file outside the build.
  • org.openrewrite.kotlin.security.FindProcessBuilderWithNonLiteral$KtRecipe
    • Find ProcessBuilder(varargs) constructions whose first arg is non-literal
    • ProcessBuilder is safer than Runtime.exec because it bypasses the shell, but a dynamic program name (the first argument) still lets the caller pick any executable on the PATH. Pin the program name to a literal.
  • org.openrewrite.kotlin.security.FindResponseSendRedirectWithNonLiteral$KtRecipe
    • Find HttpServletResponse.sendRedirect(input) calls with non-literal arguments
    • An unvalidated redirect URL lets an attacker craft a link that looks like it leads to your site but bounces to an attacker-controlled page (open-redirect / phishing). Validate against an allowlist or use a relative path.
  • org.openrewrite.kotlin.security.FindRsaKeySizeBelow2048$KtRecipe
    • Find KeyPairGenerator.getInstance("RSA") callers — verify 2048+ key size
    • RSA key sizes below 2048 bits are deprecated by NIST. Without seeing the initialize(...) call this recipe surfaces every getInstance("RSA") for review — confirm the key size is at least 2048 (preferably 3072 or migrate to Ed25519/X25519).
  • org.openrewrite.kotlin.security.FindRuntimeExecWithNonLiteral$KtRecipe
    • Find Runtime.getRuntime().exec(...) calls with non-literal arguments
    • Runtime.exec(...) passes its argument to the shell on some platforms — concatenating any user input invites command injection. Use ProcessBuilder(arrayOf("prog", arg)) so each argument is passed as a discrete argv slot.
  • org.openrewrite.kotlin.security.FindScriptEngineEval$KtRecipe
    • Find ScriptEngine.eval(...) calls
    • ScriptEngine.eval(input) executes its argument as JavaScript (or Groovy / JRuby) — full code execution from a string. Replace with a domain-specific parser, or whitelist the script before evaluation.
  • org.openrewrite.kotlin.security.FindScriptEngineManager$KtRecipe
    • Find ScriptEngineManager.getEngineByName(...) calls
    • Constructing a ScriptEngine at all is usually a smell — once present, the engine is one eval(...) away from a remote-code-execution finding. Confirm the engine is loaded from a trusted source and the inputs it receives are not user-controlled.
  • org.openrewrite.kotlin.security.FindSecretKeySpecDes$KtRecipe
    • Find SecretKeySpec(_, "DES") constructions
    • A SecretKeySpec tagged for "DES" will only feed Cipher.getInstance("DES") — the algorithm name flows through the JCE provider lookup. Replace with "AES" and a 256-bit key.
  • org.openrewrite.kotlin.security.FindSecureRandomSetSeed$KtRecipe
    • Find SecureRandom.setSeed(...) with a literal seed
    • SecureRandom.setSeed(literal) makes the RNG deterministic — defeats the whole point of using a CSPRNG. Let SecureRandom seed itself from the platform entropy source.
  • org.openrewrite.kotlin.security.FindSensitiveDataInLogs$KtRecipe
    • Find sensitive data in log calls
    • Log calls (SLF4J, println, android.util.Log) whose message string mentions password/token/secret/api_key/credit_card — each match likely renders the secret value into a log destination that isn't designed for secret storage.
  • org.openrewrite.kotlin.security.FindSensitiveNamedVariableLiteral$KtRecipe
    • Find variables named password/secret/token/apiKey with a non-empty literal default
    • A property literally named password = "hunter2" (or val token = "…", etc.) is almost always a hard-coded secret. False positives include unit-test fixtures and placeholder strings — review each match before treating as a CVE.
  • org.openrewrite.kotlin.security.FindSetAllHostnameVerifier$KtRecipe
    • Find setHostnameVerifier(ALLOW_ALL) calls
    • Setting an Apache-style ALLOW_ALL (or a custom always-true) hostname verifier disables one of TLS's two integrity checks. Remove the override and let the default verifier run.
  • org.openrewrite.kotlin.security.FindSharedPreferencesForSensitiveData$KtRecipe
    • Find getSharedPreferences(_, MODE_PRIVATE) callers
    • SharedPreferences is stored as plain XML in app-private storage — on rooted or backed-up devices, that's readable. For tokens, refresh credentials, or PII use EncryptedSharedPreferences (androidx.security.crypto).
  • org.openrewrite.kotlin.security.FindSlackTokenLiteral$KtRecipe
    • Find Slack token literals (xoxb-/xoxp-/xoxa-/xoxr-/xoxs-)
    • Slack bot/user/app tokens follow the xox[abprs]- pattern. A leaked token lets a third party read channels, post as your bot, and pull workspace metadata.
  • org.openrewrite.kotlin.security.FindSlf4jLogSensitive$KtRecipe
    • Find SLF4J log calls with sensitive field names in the format string
    • Log messages mentioning password, secret, token, api_key, credit_card, ssn, etc. usually concatenate or substitute the secret itself. Logs propagate to disk, log aggregators, and alerting pipelines — none of which are designed as a secret store.
  • org.openrewrite.kotlin.security.FindSqlExecuteQueryWithConcat$KtRecipe
    • Find Statement.executeQuery("... " + x) calls
    • String concatenation into executeQuery is the canonical SQL-injection vector. Switch to PreparedStatement with ? placeholders so the driver escapes the parameter for you.
  • org.openrewrite.kotlin.security.FindSqlExecuteWithConcat$KtRecipe
    • Find Statement.execute("... " + x) / executeUpdate calls
    • Same injection class as executeQuery — string concatenation into a Statement is unsafe for any execute variant. Use PreparedStatement.setX(index, value).
  • org.openrewrite.kotlin.security.FindStringToByteArrayDefaultCharset$KtRecipe
    • Find String.toByteArray() calls without an explicit charset
    • String.toByteArray() uses the platform default charset, which differs across operating systems and produces non-portable bytes when hashed or signed. Pass Charsets.UTF_8 (or another explicit charset) so the resulting bytes are stable.
  • org.openrewrite.kotlin.security.FindStripeKeyLiteral$KtRecipe
    • Find Stripe API key literals (sk_live_… / sk_test_…)
    • Stripe secret keys grant full account access; sk_live_… lets the holder create charges on your account. Rotate at dashboard.stripe.com/apikeys.
  • org.openrewrite.kotlin.security.FindTrustAllX509TrustManager$KtRecipe
    • Find X509TrustManager implementations with empty checkServerTrusted
    • An X509TrustManager whose checkServerTrusted/checkClientTrusted body is empty accepts any certificate chain, defeating TLS authentication. Remove the override and use the JDK default trust manager, or pin against an explicit CA.
  • org.openrewrite.kotlin.security.FindUnsafeDeserialization$KtRecipe
    • Find unsafe Java deserialization
    • Java native deserialization is the source of the Apache Commons gadget-chain RCE family. Each ObjectInputStream allocation needs an explicit ObjectInputFilter (Java 9+) or a replacement encoding.
  • org.openrewrite.kotlin.security.FindWeakCipherBlowfish$KtRecipe
    • Find Cipher.getInstance("Blowfish") calls
    • Blowfish has a 64-bit block size and is vulnerable to Sweet32 birthday collisions on long-lived sessions. Its successor Twofish is also legacy — prefer AES-GCM.
  • org.openrewrite.kotlin.security.FindWeakCipherDes$KtRecipe
    • Find Cipher.getInstance("DES...") calls
    • DES has a 56-bit effective key length and is brute-forceable in hours on commodity GPUs. Replace with AES-256/GCM for new code; for legacy data, decrypt-and-re-encrypt under AES.
  • org.openrewrite.kotlin.security.FindWeakCipherRc2$KtRecipe
    • Find Cipher.getInstance("RC2") calls
    • RC2 has known cryptanalytic weaknesses and a 40-bit export-grade variant; the JCE accepts both. Migrate to AES-GCM.
  • org.openrewrite.kotlin.security.FindWeakCipherRc4$KtRecipe
    • Find Cipher.getInstance("RC4"/"ARCFOUR") calls
    • RC4 has been removed from TLS for biased-keystream reasons (IETF RFC 7465). Replace with AES-GCM or ChaCha20-Poly1305.
  • org.openrewrite.kotlin.security.FindWeakCipherTripleDes$KtRecipe
    • Find Cipher.getInstance("DESede"/"TripleDES") calls
    • Triple-DES (3DES, DESede) is deprecated by NIST as of 2023 due to its 64-bit block size making it vulnerable to Sweet32-style birthday attacks. Migrate to AES-GCM.
  • org.openrewrite.kotlin.security.FindWeakCryptography$KtRecipe
    • Find weak cryptographic primitives
    • Broken hash algorithms (MD2/MD5/SHA-1), broken or undersized ciphers (DES / 3DES / RC2 / RC4 / Blowfish / bare AES / AES-ECB), weak key material (DES key generation, DES SecretKeySpec, sub-2048-bit RSA), predictable IVs, and non-cryptographic random sources used in security-adjacent code.
  • org.openrewrite.kotlin.security.FindWeakHashMd2$KtRecipe
    • Find MessageDigest.getInstance("MD2") calls
    • MD2 is older and weaker than MD5 — preimage and collision attacks are well-known. It exists in the JDK only for legacy interop and should never appear in new code.
  • org.openrewrite.kotlin.security.FindWeakHashMd5$KtRecipe
    • Find MessageDigest.getInstance("MD5") calls
    • MD5 is cryptographically broken; collisions are computable in seconds on commodity hardware. Use SHA-256 for non-secret hashing or HMAC-SHA-256 / Argon2id for authenticated or derived secrets.
  • org.openrewrite.kotlin.security.FindWeakHashSha1$KtRecipe
    • Find MessageDigest.getInstance("SHA-1") calls
    • SHA-1 collisions are computationally feasible (SHAttered, 2017). NIST has deprecated SHA-1 for signature use; migrate to SHA-256 or a SHA-3 variant.
  • org.openrewrite.kotlin.security.FindWeakSslProtocol$KtRecipe
    • Find SSLContext.getInstance("SSL"/"TLSv1"/"TLSv1.1") calls
    • SSL, TLSv1, and TLSv1.1 are RFC-deprecated and disabled by browsers — POODLE / BEAST / Lucky13 attacks all apply. Use TLSv1.2 or TLSv1.3 (or "TLS" to let the JDK pick the strongest mutually-supported version).
  • org.openrewrite.kotlin.security.FindWebViewAddJsInterface$KtRecipe
    • Find WebView.addJavascriptInterface(...) calls
    • addJavascriptInterface exposes a Kotlin/Java object to in-WebView JavaScript — pre-API-17 devices could call any reflectively-reachable method (CVE-2012-6636). Even on modern devices, every annotated method becomes attack surface for whatever content the WebView loads.
  • org.openrewrite.kotlin.security.FindWebViewJavaScriptEnabled$KtRecipe
    • Find WebView.settings.javaScriptEnabled = true / setJavaScriptEnabled(true)
    • Enabling JavaScript inside a WebView is the precondition for the entire WebView attack surface — addJavascriptInterface exposure, XSS in cached HTML, prompts-as-UI-spoofs. Disable it unless you control the loaded content.
  • org.openrewrite.kotlin.security.FindWebViewLoadUrlHttp$KtRecipe
    • Find WebView.loadUrl("http://...") calls
    • Loading an http:// URL into a WebView opts out of TLS and lets any on-path attacker rewrite the page (script injection, credential theft). Use https://, and if you must load HTTP, set setAllowFileAccess(false) plus a restricted WebViewClient.
  • org.openrewrite.kotlin.security.FindWebViewSavePassword$KtRecipe
    • Find WebView.settings.setSavePassword(true) calls
    • setSavePassword(true) stores form passwords in plaintext inside the WebView database. Deprecated in API 18 for this reason. Don't enable it.
  • org.openrewrite.kotlin.security.FindWebViewSetAllowFileAccessTrue$KtRecipe
    • Find WebView.settings.setAllowFileAccessFromFileURLs(true) calls
    • setAllowFileAccessFromFileURLs(true) (and setAllowUniversalAccessFromFileURLs(true)) let HTML loaded from file:// URLs read arbitrary local files — a popular Android XSS gadget. Default to false.
  • org.openrewrite.kotlin.security.FindWebViewSetMixedContentAlwaysAllow$KtRecipe
    • Find WebView.settings.mixedContentMode = MIXED_CONTENT_ALWAYS_ALLOW settings
    • MIXED_CONTENT_ALWAYS_ALLOW lets an HTTPS page pull HTTP subresources — the moment a single subresource loads over HTTP, the page's integrity is compromised. Use MIXED_CONTENT_NEVER_ALLOW.
  • org.openrewrite.kotlin.security.Security$KtRecipe
    • Find security smells in Kotlin code
    • OWASP-aligned search-only recipes covering weak cryptography, insecure TLS configuration, injection vectors, Java deserialization, JWT misuse, sensitive data in logs, Android-specific security smells, and hard-coded secret literals. Each match is a SearchResult for review — nothing is rewritten automatically because security findings nearly always need a human to pick the migration target.
  • org.openrewrite.kotlin.spring.FindAsyncOnFinal$KtRecipe
    • Find @Async methods on classes that aren't open
    • Spring's @Async proxy is the same CGLIB subclass mechanism @Transactional uses; it can only intercept methods on a non-final, non-private surface. Mark the surrounding class and method open, or apply the kotlin-spring compiler plugin to do it for you.
  • org.openrewrite.kotlin.spring.FindAsyncOnPrivate$KtRecipe
    • Find @Async on private functions
    • Like @Transactional, @Async is implemented by a Spring proxy that intercepts calls through the bean's public interface. private methods bypass the proxy and run synchronously on the caller's thread — the annotation has no effect.
  • org.openrewrite.kotlin.spring.FindAutowiredLogger$KtRecipe
    • Find @Autowired lateinit var Logger fields
    • Injecting a Logger through Spring is unnecessarily exotic — the Logger isn't a Spring bean in any standard configuration, and LoggerFactory.getLogger(MyClass::class.java) produces an identical instance with zero container plumbing. Move the declaration into a companion object: companion object \{ private val log = LoggerFactory.getLogger(MyClass::class.java) \}.
  • org.openrewrite.kotlin.spring.FindAutowiredOnConstructor$KtRecipe
    • Find @Autowired on a single constructor
    • Spring 4.3+ automatically autowires the single primary constructor — the @Autowired annotation is redundant and adds noise. Drop it from the constructor declaration.
  • org.openrewrite.kotlin.spring.FindAutowiredOnField$KtRecipe
    • Find @Autowired lateinit var field injection
    • Field injection through @Autowired lateinit var hides the dependency from the constructor, makes the class harder to test (no compile-time guarantee the field is wired), and breaks immutability. Move the dependency into the primary constructor.
  • org.openrewrite.kotlin.spring.FindAutowiredOnLateinitVar$KtRecipe
    • Find @Autowired lateinit var properties (ctor-injection candidate)
    • @Autowired lateinit var x: X is the most common Kotlin-Spring field-injection shape. Compared with @Autowired constructor(val x: X), it hides the dependency from the constructor signature and prevents the compiler from enforcing initialization order. Constructor inject instead.
  • org.openrewrite.kotlin.spring.FindAutowiredOnVar$KtRecipe
    • Find @Autowired var properties (not lateinit)
    • A @Autowired var x: X property is mutable after wiring — the Spring container sets it once, but any subsequent caller can replace the dependency at runtime. Move the dependency into a primary constructor parameter (val) so it's final end-to-end.
  • org.openrewrite.kotlin.spring.FindBeanLambdaCandidate$KtRecipe
    • Find @Bean fun foo(): X = X() candidates for the beans \{ \} DSL
    • Single-expression @Bean declarations that just construct a bean are one of the cases the Spring Kotlin beans \{ \} DSL was designed for — the DSL form drops the annotation overhead and reads as plain Kotlin. Each match here is a candidate for the migration.
  • org.openrewrite.kotlin.spring.FindCacheableOnPrivate$KtRecipe
    • Find @Cacheable on private functions
    • @Cacheable works through the same proxy mechanism as @Transactional and @Async — invisible on private (and internal) methods. Either widen visibility or move the caching boundary up the call chain.
  • org.openrewrite.kotlin.spring.FindCircularDependencyHint$KtRecipe
    • Find @Lazy annotations on @Autowired properties
    • @Lazy @Autowired is Spring's escape hatch for circular bean references. It works, but each one is a hint that the dependency graph has a cycle that should be untangled by extracting a third bean or reorganising responsibilities. Flag every occurrence for design review.
  • org.openrewrite.kotlin.spring.FindConfigurationPropertiesWithoutData$KtRecipe
    • Find @ConfigurationProperties classes that aren't data class
    • A @ConfigurationProperties carrier should be a data class with val properties: immutable, equals/hashCode/toString for free, and the constructor binder works without @ConstructorBinding. Plain class carriers either require mutable lateinit var or lose the value-class benefits.
  • org.openrewrite.kotlin.spring.FindControllerInsteadOfRestController$KtRecipe
    • Find @Controller classes whose methods all return data (consider @RestController)
    • A @Controller class needs @ResponseBody on each handler that returns data; @RestController applies @ResponseBody to every method in one annotation. Where every method on a @Controller is data-returning, @RestController reads more cleanly. Flag for review — view-rendering controllers are correct as-is.
  • org.openrewrite.kotlin.spring.FindControllerReturningResponseEntity$KtRecipe
    • Find @RestController methods returning ResponseEntity<T>
    • When the only thing a controller does with ResponseEntity is ResponseEntity.ok(body), returning T directly produces the same 200 OK response with less boilerplate. Reserve ResponseEntity for endpoints that actually vary status/headers per call.
  • org.openrewrite.kotlin.spring.FindCoroutineControllerCandidate$KtRecipe
    • Find @GetMapping/@PostMapping/... methods returning Mono<T>
    • A controller method returning Mono<T> is a candidate for suspend fun foo(): T. The suspending form reads as plain Kotlin, integrates with structured concurrency, and Spring WebFlux handles the bridge automatically.
  • org.openrewrite.kotlin.spring.FindCrudRepositoryGenericList$KtRecipe
    • Find repository interfaces extending CrudRepository instead of JpaRepository
    • CrudRepository<T, ID> returns Iterable<T> from findAll() — fine for streaming, awkward for everything else. JpaRepository<T, ID> returns List<T> and adds pagination, sorting, and batch operations. Most JPA repositories should extend JpaRepository.
  • org.openrewrite.kotlin.spring.FindDataAccessSmells$KtRecipe
    • Find Spring Data / repository access smells
    • Repository call patterns that hide a problem: repo.findById(id).get() (use findByIdOrNull or getReferenceById) and findByIdOrNull(id!!) (contradictory nullability).
  • org.openrewrite.kotlin.spring.FindEnableWebMvcOnBootApp$KtRecipe
    • Find @EnableWebMvc on a Spring Boot application
    • @EnableWebMvc opts out of Spring Boot's Web MVC auto-configuration. Most applications shouldn't apply it — they want Boot's defaults plus a WebMvcConfigurer for tweaks. Flag the annotation so reviewers can confirm it's intentional.
  • org.openrewrite.kotlin.spring.FindEnvironmentGetProperty$KtRecipe
    • Find Environment.getProperty(...) calls
    • environment.getProperty("foo") is the lowest-level Spring config API — string-typed, untyped default, no IDE completion. Promote frequently-used properties to a @ConfigurationProperties data class so the property name and type are encoded once.
  • org.openrewrite.kotlin.spring.FindEventListenerWithReturn$KtRecipe
    • Find @EventListener methods with non-Unit return types
    • Spring's @EventListener republishes any non-Unit return value as a new event. That's a useful feature when intentional, but easy to trip over — a function written to return result for the caller's convenience ends up firing the event loop. Make the intent explicit (return Unit if the caller value isn't supposed to publish, or document that it should).
  • org.openrewrite.kotlin.spring.FindFieldInjection$KtRecipe
    • Find @Inject lateinit var field injection
    • The JSR-330 @Inject annotation has the same drawbacks as @Autowired for field injection: hidden dependencies, harder testing, mutable state. Migrate to constructor injection.
  • org.openrewrite.kotlin.spring.FindFieldInjectionOverConstructor$KtRecipe
    • Find @Autowired val field declarations (not in ctor)
    • A @Autowired private val x: X written as a class-body declaration (not a primary-constructor parameter) is functionally close to constructor injection but hides the dependency from the public constructor signature. Pull the parameter up into the primary constructor so callers and tests see the contract.
  • org.openrewrite.kotlin.spring.FindFindByIdOrNullWithNonNullableId$KtRecipe
    • Find findByIdOrNull(id!!) calls
    • Calling findByIdOrNull(id!!) says two contradictory things at once: the caller insists the id is non-null (!!) but is willing to accept a null result if no row matches. If the id is genuinely non-null, the !! is dead weight; if it might be null, the call should branch before the lookup.
  • org.openrewrite.kotlin.spring.FindFluxBlockFirstInNonTest$KtRecipe
    • Find Flux.blockFirst() calls outside @Test methods
    • Flux.blockFirst() blocks the calling thread waiting for the first element of a Flux — fine in tests, a thread-pool hazard in production. Bridge with awaitFirst() / awaitFirstOrNull() from kotlinx-coroutines-reactor inside a suspend fun.
  • org.openrewrite.kotlin.spring.FindFluxBlockLastInNonTest$KtRecipe
    • Find Flux.blockLast() calls outside @Test methods
    • Flux.blockLast() drains the entire Flux on the calling thread to return the final element. In production code, that's almost never the intent — surface the elements through asFlow().collect \{ \} or call from a coroutine with awaitLast().
  • org.openrewrite.kotlin.spring.FindFluxFlatMapReturningFluxJust$KtRecipe
    • Find Flux.flatMap \{ x -> Mono.just(f(x)) \} patterns
    • A Flux.flatMap whose lambda only wraps a value back into Mono.just (or Flux.just) is doing the work of map. Drop the publisher boxing and the runtime cost of subscribing to a one-shot inner publisher per element.
  • org.openrewrite.kotlin.spring.FindFluxFromIterableWithList$KtRecipe
    • Find Flux.fromIterable(listOf(...)) patterns
    • When the source list is a constant listOf(a, b, c) known at compile time, Flux.just(a, b, c) is the same shape with one fewer allocation (no intermediate List). fromIterable only earns its keep when the iterable is already in hand.
  • org.openrewrite.kotlin.spring.FindFluxSubscribeWithoutOnError$KtRecipe
    • Find Flux.subscribe \{ ... \} without an error consumer
    • Single-argument subscribe(consumer) swallows upstream errors into Reactor's default onErrorDropped hook — silent in most environments and frustrating to debug. The two-argument form subscribe(consumer, errorConsumer) (or four-argument with onComplete / Context) forces an explicit choice.
  • org.openrewrite.kotlin.spring.FindHttpServletRequestParameter$KtRecipe
    • Find HttpServletRequest parameters in controllers
    • Reaching for HttpServletRequest inside a controller handler bypasses Spring's argument-resolver chain (@PathVariable, @RequestParam, @RequestHeader, @RequestBody, etc.). Each of those binds the value with type conversion and validation; using the raw servlet request loses that and couples the handler to the servlet API.
  • org.openrewrite.kotlin.spring.FindJpaEntityWithVarsOnly$KtRecipe
    • Find @Entity classes with var properties only
    • JPA entities need mutable properties for the persistence provider to hydrate them, but a class X(var a: A, var b: B) form mixes that JPA requirement with full external mutability. Promote to data class (still mutable for JPA via the kotlin-jpa compiler plugin's synthesized no-arg ctor) to get equals/hashCode/toString and copy.
  • org.openrewrite.kotlin.spring.FindJpaRepositoryFindByIdWithoutOptional$KtRecipe
    • Find repo.findById(id).get() chains
    • Optional.get() on a JPA repository result throws NoSuchElementException when the row is missing — the same outcome as getReferenceById(id) but without the explicit Optional dance. In Kotlin, findByIdOrNull(id) plus a null check (or ?: throw) is even more direct.
  • org.openrewrite.kotlin.spring.FindLateinitInjectedField$KtRecipe
    • Find any lateinit var injected field (@Autowired / @Inject / @Value)
    • Any property wired via lateinit var + injection annotation pattern is a candidate for constructor injection. This recipe catches the union of @Autowired, @Inject, and @Value lateinit-var declarations.
  • org.openrewrite.kotlin.spring.FindMainMethodWithSpringApplicationRun$KtRecipe
    • Find top-level main functions wrapping SpringApplication.run
    • A top-level main(args: Array<String>) \{ SpringApplication.run(MyApp::class.java, *args) \} collapses to one line with the reified runApplication<MyApp>(*args) builder. Flag the main entry point for migration.
  • org.openrewrite.kotlin.spring.FindMissingResponseStatus$KtRecipe
    • Find @PostMapping methods missing @ResponseStatus(HttpStatus.CREATED)
    • By convention, a successful POST that creates a resource should return 201 Created, not the default 200 OK. Add @ResponseStatus(HttpStatus.CREATED) to the controller method so the status is consistent with the action.
  • org.openrewrite.kotlin.spring.FindMockBeanOnField$KtRecipe
    • Find @MockBean on lateinit var fields
    • @MockBean lateinit var x mutates the bean at field-injection time, which works but ties the test to Spring's container even when the unit under test could be exercised with constructor injection of a plain mockk<X>(). Flag for review — preferred where the surrounding test can be a plain unit test.
  • org.openrewrite.kotlin.spring.FindMockMvcStandalone$KtRecipe
    • Find MockMvcBuilders.standaloneSetup(...) calls
    • standaloneSetup wires a single controller into a minimal MockMvc — fast, but misses any application-level configuration (interceptors, exception handlers, argument resolvers). @AutoConfigureMockMvc produces a MockMvc that mirrors the running application; flag standalone setups as candidates for replacement.
  • org.openrewrite.kotlin.spring.FindMonoAwaitSingle$KtRecipe
    • Find mono.awaitSingle() calls inside Flux/Flow collectors
    • awaitSingle() is the right bridge from a single-value Mono into a coroutine. Inside a Flux.collect / Flow.collect over many elements, however, the pattern often signals that the surrounding code is mixing two stream models — flag for review.
  • org.openrewrite.kotlin.spring.FindMonoBlockInNonTest$KtRecipe
    • Find Mono.block() calls outside @Test methods
    • Mono.block() parks the calling thread until the upstream Mono completes, which is fine in a test but a footgun in production code. On Netty's small event-loop pool, one block() can stall every concurrent request. Bridge with awaitSingle() from kotlinx-coroutines-reactor inside a suspend fun instead.
  • org.openrewrite.kotlin.spring.FindMonoErrorInsteadOfThrow$KtRecipe
    • Find throw ... statements inside Mono/Flux operator lambdas
    • Reactor expects errors to be signaled through the publisher (Mono.error(...)) rather than thrown. A raw throw inside flatMap / map works through Reactor's Exceptions.propagate fallback, but loses stack-walking guarantees and trips up the assembly-time error handling.
  • org.openrewrite.kotlin.spring.FindMonoFlatMapBlock$KtRecipe
    • Find Mono.block() calls in non-test code
    • Outside of @Test methods, Mono.block() is almost always a bug: it bridges reactive code into a blocking call, defeating the purpose of WebFlux. In Kotlin, the bridge should go the other direction — awaitSingle() from kotlinx-coroutines-reactor.
  • org.openrewrite.kotlin.spring.FindMonoFlatMapReturningMonoJust$KtRecipe
    • Find Mono.flatMap \{ x -> Mono.just(f(x)) \} patterns
    • When a flatMap lambda's only job is to wrap a synchronous result in Mono.just, the whole step collapses to map \{ x -> f(x) \}. map is cheaper (no inner Mono allocation) and signals that the transform is synchronous.
  • org.openrewrite.kotlin.spring.FindMonoFluxSmells$KtRecipe
    • Find Mono/Flux ergonomic smells
    • Reactive-pipeline shapes that read more naturally a different way: flatMap \{ Mono.just(...) \}map, block() / blockFirst() / blockLast() outside @Test methods, single-argument Flux.subscribe (missing error consumer), raw throw inside operator lambdas (use Mono.error), Mono.zip (verify independent operands), and Flux.fromIterable(listOf(...)) (use Flux.just).
  • org.openrewrite.kotlin.spring.FindMonoZipWithoutAllOperands$KtRecipe
    • Find Mono.zip(...) calls
    • Mono.zip waits for all of its sources to emit, then combines them. Useful when two requests are genuinely independent, but easy to misuse — flag for review to confirm the operands are independent and that the desired error semantics match zip's eager-cancellation behaviour.
  • org.openrewrite.kotlin.spring.FindNoArgConstructorMissing$KtRecipe
    • Find @Entity data class declarations (verify kotlin-jpa plugin)
    • A @Entity data class X(val a: A) only works with JPA when the kotlin-jpa compiler plugin synthesizes a no-arg constructor. Without the plugin, JPA's findById fails at runtime with InstantiationException: No default constructor. Flag entity data classes so reviewers can confirm the plugin is applied.
  • org.openrewrite.kotlin.spring.FindOpenClassForSpring$KtRecipe
    • Find Spring stereotype classes not declared open
    • Kotlin classes are final by default, but Spring needs a non-final target to create CGLIB proxies (which is how @Transactional, @Async, scope-proxied beans, etc. work). The kotlin-spring compiler plugin opens them automatically, but if it isn't applied — or the class is in a module that doesn't apply it — Spring's proxy machinery fails at runtime. Flag stereotype classes that aren't explicitly open for review.
  • org.openrewrite.kotlin.spring.FindPathVariableWithoutName$KtRecipe
    • Find @PathVariable parameters without an explicit name
    • @PathVariable name: String works only as long as the JVM preserves parameter names, which requires the -parameters javac flag and -java-parameters kotlinc flag. If either is missing, Spring resolves the path variable by ordinal — a footgun on rename. Set the name explicitly: @PathVariable("id") id: String.
  • org.openrewrite.kotlin.spring.FindPropertySourceOnNonConfiguration$KtRecipe
    • Find @PropertySource on classes that lack @Configuration
    • @PropertySource only takes effect on a Spring @Configuration class — when applied to a stereotype like @Component or @Service, the property file is silently ignored. Move the annotation to a @Configuration class or change the surrounding class accordingly.
  • org.openrewrite.kotlin.spring.FindQualifierOnLateinitField$KtRecipe
    • Find @Qualifier on lateinit var fields
    • @Qualifier annotating a lateinit var doubles down on field injection. Move both the qualifier and the dependency to a constructor parameter so callers (and tests) can see what's required.
  • org.openrewrite.kotlin.spring.FindReactiveCoroutineInterop$KtRecipe
    • Find reactive / coroutine interop hazards
    • Bridges between Reactor and coroutines that usually point at a missed opportunity: WebClient.bodyToMono(...).block() (use awaitBody<T>()), Mono.deferContextual \{ \} inside a suspend fun (context propagation goes through coroutineContext), and awaitSingle() patterns worth a review.
  • org.openrewrite.kotlin.spring.FindReactiveTestWithoutStepVerifier$KtRecipe
    • Find WebTestClient test classes that don't use StepVerifier
    • WebTestClient makes the call, but assertions on a Mono<T> body typically need StepVerifier.create(...).expectNext(...).verifyComplete() to fully drain the publisher and assert ordering. Without it, a reactive bug can hide behind the test's premature completion.
  • org.openrewrite.kotlin.spring.FindReactorContextInsideSuspendFun$KtRecipe
    • Find Mono.deferContextual \{ ... \} inside suspend fun
    • Reactor's deferContextual reads context from a reactive Subscriber. Inside a suspend fun, that subscriber isn't the active continuation — context propagation should go through kotlin.coroutines.coroutineContext or kotlinx.coroutines.reactor.ReactorContext instead. Flag for review.
  • org.openrewrite.kotlin.spring.FindRepositoryReturnsOptional$KtRecipe
    • Find Spring Data repository methods returning Optional<T>
    • On the JVM, Optional<T> is the only way to model 'maybe absent' in Java APIs. In Kotlin, T? is the language-native equivalent — Spring Data auto-detects nullable return types since 2.0. Convert Optional<T> returns to T?.
  • org.openrewrite.kotlin.spring.FindRequestBodyOnPrimitive$KtRecipe
    • Find @RequestBody on primitive parameters
    • @RequestBody Int or @RequestBody String parses the entire HTTP body as a single value — a fragile contract that breaks the moment the API evolves to include a second field. Wrap the parameter in a DTO so future additions don't require client-side changes.
  • org.openrewrite.kotlin.spring.FindRequestMappingMethodGetMapping$KtRecipe
    • Find @RequestMapping(method = [RequestMethod.GET]) candidates for @GetMapping
    • @RequestMapping with an explicit method = [...] is the long-form spelling of @GetMapping/@PostMapping/etc. The shortcut annotations were introduced specifically to replace this pattern.
  • org.openrewrite.kotlin.spring.FindRequestMappingWithoutVerb$KtRecipe
    • Find @RequestMapping(...) without an HTTP method
    • @RequestMapping("/x") matches every HTTP verb, which is rarely the intent. The verb-specific shortcuts (@GetMapping/@PostMapping/...) are clearer at a glance and prevent accidental dual-method routes.
  • org.openrewrite.kotlin.spring.FindRequiredOnSetter$KtRecipe
    • Find @Required annotations
    • @Required was deprecated in Spring 5.1 and removed in 6.0 — its only purpose was to mandate setter injection. The modern equivalent is mandatory constructor injection, which is enforced by Kotlin's non-null types.
  • org.openrewrite.kotlin.spring.FindResponseEntityWithoutStatus$KtRecipe
    • Find ResponseEntity(body, HttpStatus.OK) constructor calls
    • The two-argument ResponseEntity constructor with HttpStatus.OK is exactly what ResponseEntity.ok(body) produces — using the factory makes the 200-OK intent explicit and removes the dependency on HttpStatus. Save the constructor form for genuinely status-varying responses.
  • org.openrewrite.kotlin.spring.FindRestTemplateUsage$KtRecipe
    • Find RestTemplate allocations
    • RestTemplate was placed in maintenance mode in Spring 5 — Spring's docs explicitly steer new code to WebClient (reactive) or RestClient (Spring 6.1+, synchronous). Each RestTemplate() allocation is a candidate for migration.
  • org.openrewrite.kotlin.spring.FindSpringAnnotationSmells$KtRecipe
    • Find Spring annotation-shape smells
    • Stereotype/injection annotations applied to the wrong Kotlin shape: @Component on data class, @Service on object, @Bean without @Scope, @Autowired on var / lateinit var / class-body val, and @Lazy @Autowired (a hint of circular dependencies).
  • org.openrewrite.kotlin.spring.FindSpringApplicationRunJava$KtRecipe
    • Find SpringApplication.run(MyApp::class.java, ...) calls
    • Kotlin Spring Boot ships a reified helper runApplication<MyApp>(*args) that drops the ::class.java token and the explicit SpringApplication reference. The Java-style form here works but reads as a Java port — flag for migration.
  • org.openrewrite.kotlin.spring.FindSpringBeanWithoutScope$KtRecipe
    • Find @Bean methods without @Scope
    • @Bean without @Scope produces a singleton, which is almost always correct — but for stateful beans (@RequestScope, @SessionScope, prototype-scoped builders) the default is wrong. Flag for review when the bean's nature suggests a scope decision is in order.
  • org.openrewrite.kotlin.spring.FindSpringBootstrappingSmells$KtRecipe
    • Find Spring Boot bootstrapping smells
    • Bootstrap code that hasn't been Kotlinized: Java-style SpringApplication.run(MyApp::class.java, ...) calls and main wrappers that could collapse to a one-line runApplication<MyApp>(*args).
  • org.openrewrite.kotlin.spring.FindSpringComponentOnDataClass$KtRecipe
    • Find @Component / @Service / @Repository on data class
    • Spring stereotype classes have proxy-friendly identity (Spring wires them as singletons keyed by class). data class overrides equals/hashCode over the constructor properties — two beans with the same fields compare equal, which is rarely desirable for a service-shaped component. Promote to a regular class.
  • org.openrewrite.kotlin.spring.FindSpringConfigurationSmells$KtRecipe
    • Find Spring configuration smells
    • Configuration scattered across @Value lateinit-var reads, untyped Environment.getProperty(...) calls, @ConfigurationProperties carriers that aren't data class, @Bean fun foo(): X = X() candidates for the beans \{ \} Kotlin DSL, and misplaced @PropertySource on non-@Configuration classes.
  • org.openrewrite.kotlin.spring.FindSpringCoroutineCandidates$KtRecipe
    • Find Spring coroutine-migration candidates
    • Controllers and clients that work today with Mono/Flux chaining but read more naturally as suspending Kotlin: Mono<T> returns from mapping methods (could be suspend fun foo(): T) and bodyToMono(X::class.java) patterns (awaitBody<X>()).
  • org.openrewrite.kotlin.spring.FindSpringDataSmells$KtRecipe
    • Find Spring Data smells
    • Repository methods returning Optional<T> instead of T?, @Entity classes that should be data class, @Entity data class declarations whose JPA-friendliness depends on the kotlin-jpa plugin, CrudRepository candidates for JpaRepository, and @Transactional annotations on private methods or final classes (Spring's proxy can't intercept them).
  • org.openrewrite.kotlin.spring.FindSpringDependencyInjectionSmells$KtRecipe
    • Find Spring dependency-injection smells
    • Field injection (@Autowired / @Inject / @Qualifier on lateinit var), redundant @Autowired on single ctors, the deprecated @Required setter annotation, and Spring stereotype classes that aren't open (Kotlin's final default breaks CGLIB proxies unless kotlin-spring is applied).
  • org.openrewrite.kotlin.spring.FindSpringLegacyApiSmells$KtRecipe
    • Find Spring legacy / deprecated API smells
    • RestTemplate (in maintenance mode — use WebClient or RestClient), @EnableWebMvc on a Boot application (disables auto-config), HttpServletRequest parameters in controllers (use binding annotations), @Controller whose handlers all return data (consider @RestController), and @Autowired Logger fields (use companion LoggerFactory).
  • org.openrewrite.kotlin.spring.FindSpringProxiedAnnotationSmells$KtRecipe
    • Find Spring proxied-annotation smells
    • Proxy-backed annotations beyond @Transactional that hit the same Kotlin-default-final trap: @Async and @Cacheable on private methods or final classes, plus @EventListener methods that accidentally republish their return values as new events.
  • org.openrewrite.kotlin.spring.FindSpringServiceOnObject$KtRecipe
    • Find @Service object Foo declarations
    • A Spring @Service / @Component declared as object is a singleton at the language level — Spring will still register it as a bean, but autowiring into the object's properties is fragile (object initialization runs at class-load time, before the Spring context exists). Use a regular class so the container controls the lifecycle.
  • org.openrewrite.kotlin.spring.FindSpringTestingSmells$KtRecipe
    • Find Spring testing smells
    • @MockBean / @SpyBean lateinit-var fields (often a plain unit test would do), MockMvcBuilders.standaloneSetup (consider @AutoConfigureMockMvc), and WebTestClient tests that don't drain the publisher with StepVerifier.
  • org.openrewrite.kotlin.spring.FindSpringWebSmells$KtRecipe
    • Find Spring Web / WebFlux smells
    • Controller endpoints worth a closer look: ResponseEntity<T> returns that always emit 200, verb-less @RequestMapping, @RequestMapping(method = [...]) candidates for shortcut annotations, @PathVariable parameters without explicit names, primitive @RequestBody shapes, POST endpoints missing @ResponseStatus(CREATED), and reactive block() calls that stall the event loop.
  • org.openrewrite.kotlin.spring.FindSpyBeanOnField$KtRecipe
    • Find @SpyBean on lateinit var fields
    • @SpyBean carries the same coupling to the Spring container as @MockBean, plus the extra surprise of partially mocking real implementation code. Where possible, exercise the unit under test directly with mockk<X>(relaxed = true) and verify against a spy of a single dependency.
  • org.openrewrite.kotlin.spring.FindTransactionalOnFinal$KtRecipe
    • Find @Transactional methods on classes that aren't open
    • Spring proxies a @Transactional bean by subclassing it (CGLIB); for the subclass to override the method, both the class and the method must be non-final. Kotlin's default final defeats this — either apply the kotlin-spring compiler plugin or mark the class and method open.
  • org.openrewrite.kotlin.spring.FindTransactionalOnPrivate$KtRecipe
    • Find @Transactional on private functions
    • Spring's transaction proxy intercepts calls through the bean's public interface — private (and internal) methods are invoked directly on the target instance, bypassing the proxy entirely. The annotation is silently no-op. Make the method public or move the transaction boundary up the call chain.
  • org.openrewrite.kotlin.spring.FindValueAnnotationOnLateinit$KtRecipe
    • Find @Value on lateinit var properties
    • Individual @Value("\$\{x\}") reads scatter configuration access across the codebase. Grouping related properties under a single @ConfigurationProperties data class produces typed, validated, IDE-discoverable config — and works seamlessly with data class + non-null types in Kotlin.
  • org.openrewrite.kotlin.spring.FindWebClientBlockOnResponse$KtRecipe
    • Find webClient...bodyToMono(X::class).block() chains
    • Chaining block() onto a WebClient.bodyToMono(...) call defeats the reactive request entirely — the calling thread blocks for the HTTP round-trip, throwing away every concurrency benefit of WebClient. In a suspend fun, awaitBody<X>() produces the same value without blocking the event loop.
  • org.openrewrite.kotlin.spring.FindWebClientCreateWithoutBuilder$KtRecipe
    • Find WebClient.create() / WebClient.create(url) calls
    • The static WebClient.create(...) shortcut returns a client with default codecs, no baseUrl chain, no filters, no exchange-strategy tuning. Production WebClients almost always need at least one of those — promote to WebClient.builder().baseUrl(...).build() so the configuration shape is visible at the call site.
  • org.openrewrite.kotlin.spring.FindWebClientRestTemplateSmells$KtRecipe
    • Find WebClient / RestTemplate / ResponseEntity smells
    • HTTP-client and response-shape patterns: RestTemplate allocations (maintenance mode — use WebClient / RestClient), WebClient.create() without the builder (use WebClient.builder().baseUrl(...)), and ResponseEntity(body, HttpStatus.OK) (use the ok(body) factory).
  • org.openrewrite.kotlin.spring.FindWebClientWithoutAwait$KtRecipe
    • Find WebClient.bodyToMono(X::class.java) calls
    • In suspending controllers and services, bodyToMono(X::class.java).awaitSingle() is more naturally spelled as awaitBody<X>() from kotlinx-coroutines-reactor. The reified form removes the ::class.java token and the .awaitSingle() chain.
  • org.openrewrite.kotlin.spring.FindWebFluxBlocking$KtRecipe
    • Find Mono.block / Flux.blockFirst / Flux.blockLast calls
    • Calling block() on a reactive pipeline parks the calling thread until the upstream completes, which is exactly what the reactive runtime is built to avoid. On Netty's small event-loop pool, a single block() can stall every concurrent request the server is processing.
  • org.openrewrite.kotlin.spring.Spring$KtRecipe
    • Modernize Spring Boot Kotlin code
    • Find Kotlin-idiomatic violations in Spring Boot applications: Java-style SpringApplication.run, @Autowired / @Inject field injection, missing open on Spring-proxied classes, blocking Mono.block() calls, @RequestMapping candidates for @GetMapping, Mono<T> controllers that could be suspending, @ConfigurationProperties data class candidates, @Entity data class plugin reminders, @Transactional/@Async/@Cacheable on private/final methods, deprecated RestTemplate allocations, Mono/Flux ergonomic shapes, reactive-coroutine interop hazards, Spring annotation shapes, repository access patterns, and WebClient/ResponseEntity shapes.
  • org.openrewrite.kotlin.stdlib.CollectionShorthands$KtRecipe
    • Apply Kotlin collection shorthands
    • Replaces round-trip conversions (asSequence().toList(), toList().toSet(), toSet().toList(), …) with the dedicated stdlib operator they're imitating.
  • org.openrewrite.kotlin.stdlib.EmptyConstructorShorthands$KtRecipe
    • Prefer emptyList() / emptySet() / emptyMap() over zero-arg builders
    • When listOf() / setOf() / mapOf() are called with no entries, replace them with the explicit emptyList() / emptySet() / emptyMap() factories so the empty-by-construction intent is visible at the call site.
  • org.openrewrite.kotlin.stdlib.Stdlib$KtRecipe
    • Apply Kotlin standard-library idioms
    • Opinionated bundle of every Kotlin stdlib-shorthand recipe in this module: round-trip elimination, empty-factory preference, and string isBlank/take/drop folds. Complementary to Performance (which focuses on chain collapses) and BestPractices (which focuses on flagging smells).
  • org.openrewrite.kotlin.stdlib.StringShorthands$KtRecipe
    • Apply Kotlin string shorthands
    • Folds trim().isEmpty() into isBlank(), and prefers take/drop over substring indexing.
  • org.openrewrite.kotlin.stdlib.UseAsSequenceToListIdentity$KtRecipe
    • Use toList() instead of asSequence().toList()
    • asSequence() wraps the iterable in a Sequence only to immediately tear it back into a List. The intermediate Sequence allocation does no work.
  • org.openrewrite.kotlin.stdlib.UseDistinctForToHashSetToList$KtRecipe
    • Use distinct() instead of toHashSet().toList()
    • Round-tripping through a HashSet to drop duplicates obscures intent and allocates an intermediate. distinct() says what it does and returns a List directly.
  • org.openrewrite.kotlin.stdlib.UseDistinctForToSetToList$KtRecipe
    • Use distinct() instead of toSet().toList()
    • Round-tripping through a Set to drop duplicates obscures intent and allocates an intermediate. distinct() says what it does and returns a List directly.
  • org.openrewrite.kotlin.stdlib.UseDistinctForToSetToMutableList$KtRecipe
    • Use distinct().toMutableList() instead of toSet().toMutableList()
    • Round-tripping through a Set to drop duplicates obscures intent. distinct() says what it does; chain toMutableList() if you actually need a mutable result.
  • org.openrewrite.kotlin.stdlib.UseEmptyListForListOfNoArgs$KtRecipe
    • Use emptyList<T>() instead of listOf<T>()
    • listOf() with no entries delegates to emptyList(). Call the named factory directly to make the empty-by-construction intent visible.
  • org.openrewrite.kotlin.stdlib.UseEmptyMapForMapOfNoArgs$KtRecipe
    • Use emptyMap<K, V>() instead of mapOf<K, V>()
    • mapOf() with no entries delegates to emptyMap(). Call the named factory directly to make the empty-by-construction intent visible.
  • org.openrewrite.kotlin.stdlib.UseEmptySetForSetOfNoArgs$KtRecipe
    • Use emptySet<T>() instead of setOf<T>()
    • setOf() with no entries delegates to emptySet(). Call the named factory directly to make the empty-by-construction intent visible.
  • org.openrewrite.kotlin.stdlib.UseSetForMutableSetToSet$KtRecipe
    • Use toSet() instead of toMutableSet().toSet()
    • toMutableSet() already allocates a fresh set — calling toSet() on it copies again. Go directly to toSet().
  • org.openrewrite.kotlin.stdlib.UseStringDropForSubstring$KtRecipe
    • Use drop(n) instead of substring(n) on a String
    • drop(n) is the named form for skipping the first n characters and returns the empty string for over-long n instead of throwing.
  • org.openrewrite.kotlin.stdlib.UseStringIsBlankForTrimIsEmpty$KtRecipe
    • Use isBlank() instead of trim().isEmpty() on a String
    • trim().isEmpty() allocates a trimmed copy just to check whether the result has no characters. isBlank() answers the same question by scanning in place.
  • org.openrewrite.kotlin.stdlib.UseStringIsNotBlankForTrimIsNotEmpty$KtRecipe
    • Use isNotBlank() instead of trim().isNotEmpty() on a String
    • trim().isNotEmpty() allocates a trimmed copy to check whether anything is left. isNotBlank() answers the same question by scanning in place.
  • org.openrewrite.kotlin.stdlib.UseStringTakeForSubstringFromZero$KtRecipe
    • Use take(n) instead of substring(0, n) on a String
    • take(n) is the named form on CharSequence and uniformly returns the empty string when n is larger than length. substring(0, n) throws on that case — the named form is both clearer and friendlier.
  • org.openrewrite.kotlin.stdlib.UseTakeForSubListFromZero$KtRecipe
    • Use take(n) instead of subList(0, n)
    • take(n) returns the first n elements as a stable copy. subList(0, n) returns a live view backed by the original list — surprising aliasing if the source is mutated.
  • org.openrewrite.kotlin.stdlib.UseToListForListToList$KtRecipe
    • Use toList() instead of toMutableList().toList()
    • toMutableList() already allocates a fresh list — wrapping it in another toList() copies it again. Go directly to toList().
  • org.openrewrite.kotlin.stdlib.UseToSetForToListToSet$KtRecipe
    • Use toSet() instead of toList().toSet()
    • Materializing a List first and then a Set allocates one collection that's thrown away. toSet() builds the deduplicating collection directly.
  • org.openrewrite.kotlin.testing.FindAssertEqualsCandidateForKotest$KtRecipe
    • Find assertEquals(...) calls — Kotest migration candidate
    • Kotest's idiomatic form for assertEquals(expected, actual) is actual shouldBe expected — the receiver is the subject under test, which composes naturally with chained matchers (actual shouldBe expected; actual.shouldBeOfType<T>()). Each match is a candidate when migrating to Kotest.
  • org.openrewrite.kotlin.testing.FindAssertFalseCandidateForKotest$KtRecipe
    • Find assertFalse(...) calls — Kotest migration candidate
    • Same shape as assertTrue: the predicate is collapsed to a boolean and the failure message loses fidelity. Kotest's actual.shouldBeFalse() (or specialized matchers like actual.shouldNotContain(...)) records the original expression.
  • org.openrewrite.kotlin.testing.FindAssertJChainUsingExtractingThenContains$KtRecipe
    • Find AssertJ .extracting(...).contains(...) chains
    • AssertJ's .extracting("name") uses reflection; .extracting \{ it.name \} (lambda form) is type-safe. Each match is a candidate for the lambda form, or for moving to a .allMatch \{ … \} predicate when the collection-level invariant is what you actually want to assert.
  • org.openrewrite.kotlin.testing.FindAssertNotNullCandidateForKotest$KtRecipe
    • Find assertNotNull(...) calls — Kotest migration candidate
    • Kotest's actual.shouldNotBeNull() is a contract function: after it returns, the compiler smart-casts actual to its non-nullable type, so the chained matcher can call methods without !!. JUnit's assertNotNull does not smart-cast.
  • org.openrewrite.kotlin.testing.FindAssertNullCandidateForKotest$KtRecipe
    • Find assertNull(...) calls — Kotest migration candidate
    • assertNull(actual)actual.shouldBeNull() in Kotest. The receiver-style form keeps the subject as the focal point, which composes more cleanly into specialized matchers (actual.shouldBeNullOrEmpty() etc).
  • org.openrewrite.kotlin.testing.FindAssertThrowsCandidateForKotest$KtRecipe
    • Find assertThrows<X> \{ ... \} calls — Kotest migration candidate
    • JUnit 5's assertThrows<X> \{ … \} and Kotest's shouldThrow<X> \{ … \} have the same shape. Migrating gives access to Kotest's shouldThrowExactly<X> (rejects subclass exceptions) and shouldThrowMessage(text) \{ … \}, which are tighter than JUnit's catch-and-introspect pattern.
  • org.openrewrite.kotlin.testing.FindAssertTrueCandidateForKotest$KtRecipe
    • Find assertTrue(...) calls — Kotest migration candidate
    • assertTrue(condition) collapses the predicate into a boolean before failure formatting can capture what the value actually was. Kotest's actual.shouldBeTrue() (and the matcher library generally — actual shouldBe true, actual.shouldStartWith(...)) carries the original expression into the failure message.
  • org.openrewrite.kotlin.testing.FindAssertionLibrarySmells$KtRecipe
    • Find assertion-library smells
    • Search-only bundle for assertion-library specifics: Hamcrest's assertThat(actual, is(expected)) form, and AssertJ's reflective .extracting("name") followed by .contains(...).
  • org.openrewrite.kotlin.testing.FindBeforeEachReinitializingFinal$KtRecipe
    • Find @BeforeEach methods that reassign val properties
    • @BeforeEach runs before every test, but val property assignment only happens at construction. If setUp looks like value = ... against a val, it doesn't compile — but the related anti-pattern (reassigning a lateinit var per test where the type-safe shape would be a val initialized in the constructor) is worth surfacing.
  • org.openrewrite.kotlin.testing.FindCoroutineTestRule$KtRecipe
    • Find JUnit 4 coroutine-test @Rule fields
    • Hand-rolled MainCoroutineRule / CoroutineTestRule patterns predate kotlinx-coroutines-test's Dispatchers.setMain/resetMain helpers. With runTest \{ \} + Dispatchers.setMain(StandardTestDispatcher()) the rule's responsibilities are spread across @BeforeEach/@AfterEach cleanly enough that the rule itself becomes redundant.
  • org.openrewrite.kotlin.testing.FindCoroutineTestSmells$KtRecipe
    • Find coroutine-test patterns
    • Search-only bundle for coroutine-testing primitives: runBlocking inside @Test, runBlockingTest (deprecated), TestCoroutineDispatcher (deprecated), JUnit 4 @Rule fields named after coroutines (hand-rolled MainCoroutineRule-style), and delay(...) calls inside a runBlocking test body.
  • org.openrewrite.kotlin.testing.FindDelayInTest$KtRecipe
    • Find delay(...) calls inside test methods running on a real dispatcher
    • delay(ms) inside a test that uses runBlocking (not runTest) waits the literal duration in real time — a fast suite slows to a crawl. Inside runTest \{ \}, delay advances virtual time instantly; the call shape is identical but the runner makes the difference.
  • org.openrewrite.kotlin.testing.FindDisabledTest$KtRecipe
    • Find @Disabled annotations
    • @Disabled is the JUnit 5 skip annotation — typically used for tests that are flaky, broken, or pending an upstream fix. Each match is a tech-debt marker worth reviewing: confirm the skip is still warranted, the reason still applies, and the test isn't hiding a real regression.
  • org.openrewrite.kotlin.testing.FindEmptyTestBody$KtRecipe
    • Find @Test methods with empty bodies
    • An empty @Test fun foo() \{ \} passes unconditionally. The reasons it lands in a codebase are usually disabled-during-WIP, scaffolded-then-forgotten, or a stand-in for a TODO. Each match should either gain assertions, be annotated @Disabled with a reason, or be deleted.
  • org.openrewrite.kotlin.testing.FindFunctionTestNamedWithUnderscores$KtRecipe
    • Find test functions named with snake_case
    • Kotlin's backtick syntax lets test names read as sentences: fun \returns 404 when user not found`(). Names like fun test_returns_404_when_user_not_found()` predate that convention — usually the result of porting Java tests directly.
  • org.openrewrite.kotlin.testing.FindHamcrestAssertThatUsage$KtRecipe
    • Find MatcherAssert.assertThat(...) (Hamcrest) calls
    • Hamcrest's assertThat(actual, is(expected)) was the inspiration for both AssertJ's fluent chains and Kotest's matcher library. On a Kotlin codebase both alternatives compose better with the language (AssertJ via type-safe builders, Kotest via infix and extension functions).
  • org.openrewrite.kotlin.testing.FindJUnitFunctionWithPublic$KtRecipe
    • Find public modifier on JUnit 5 test functions
    • JUnit 5 dropped the JUnit 4 requirement that test methods be public — package-private (Java) or no modifier (Kotlin default) is the convention. Each public fun test...() is a JUnit 4 holdover that can be dropped.
  • org.openrewrite.kotlin.testing.FindKotestCandidates$KtRecipe
    • Find Kotest migration candidates
    • Search-only bundle for assertion call sites that have direct Kotest equivalents: assertEquals (shouldBe), assertTrue/assertFalse (shouldBeTrue/shouldBeFalse), assertNull/assertNotNull (shouldBeNull/shouldNotBeNull with smart-cast), assertThrows (shouldThrow), plus snake_case test names that Kotlin's backtick syntax can replace with sentence-style names.
  • org.openrewrite.kotlin.testing.FindMockitoArgumentCaptor$KtRecipe
    • Find ArgumentCaptor.forClass(X::class.java) allocations
    • ArgumentCaptor.forClass(X::class.java) plus a later verify(mock).method(captor.capture()) is the Mockito idiom for asserting on the actual argument passed in. mockk's slot<X>() + every \{ mock.method(capture(slot)) \} answers \{ … \} records the value as part of the recording block.
  • org.openrewrite.kotlin.testing.FindMockitoArgumentMatchersAny$KtRecipe
    • Find Mockito argument-matcher any() / eq() / isA() calls
    • Mockito's argument matchers (any(), eq(value), isA(X::class.java)) only work inside a whenever/verify call — they throw if used elsewhere. mockk's matchers (any(), eq(value), match \{ … \}) work the same way but live in every \{ \} / verify \{ \} blocks, so the matcher and the recording context are co-located.
  • org.openrewrite.kotlin.testing.FindMockitoInjectMocks$KtRecipe
    • Find Mockito @InjectMocks fields
    • @InjectMocks asks Mockito to wire @Mock-annotated fields into the target's constructor / setters / fields by reflection. In Kotlin code with constructor injection, the cleaner equivalent is to declare the target inside @BeforeEach: val target = Service(mockA, mockB). mockk has no analogous annotation — the explicit constructor call is the convention.
  • org.openrewrite.kotlin.testing.FindMockitoMockCall$KtRecipe
    • Find Mockito.mock(...) / mock<X>() calls
    • Mockito's mock(X::class.java) (or mockito-kotlin's mock<X>()) builds a relaxed proxy that returns sensible defaults for unstubbed calls. The mockk equivalent is mockk<X>() — strict by default (unstubbed calls throw) with mockk<X>(relaxed = true) for the Mockito-style default behavior.
  • org.openrewrite.kotlin.testing.FindMockitoMockField$KtRecipe
    • Find Mockito @Mock fields
    • @Mock-annotated fields are populated by MockitoAnnotations.openMocks(this) (or the MockitoExtension). In mockk the convention is an inline assignment: private val service = mockk<Service>(relaxed = true). Each match is a candidate for that conversion.
  • org.openrewrite.kotlin.testing.FindMockitoSmells$KtRecipe
    • Find mockito-kotlin / Mockito patterns
    • Search-only bundle covering the Mockito surface most worth reviewing when migrating to mockk: mock/spy allocations, whenever.thenReturn chains, verify(...) calls, argument matchers, ArgumentCaptor.forClass, and @Mock/@InjectMocks field annotations.
  • org.openrewrite.kotlin.testing.FindMockitoSpyCall$KtRecipe
    • Find Mockito.spy(...) / spy(...) calls
    • Mockito's spy(realInstance) wraps a real object so unstubbed methods call the real implementation. mockk uses spyk(realInstance) — same idea, different semantics around every \{ \} returns ... (mockk records the call, Mockito intercepts the invocation).
  • org.openrewrite.kotlin.testing.FindMockitoVerifyCall$KtRecipe
    • Find Mockito verify(mock).method(...) calls
    • Mockito's verify(mock).method(arg) records a verification at the call site. mockk inverts the form: verify \{ mock.method(arg) \} — the lambda block makes the verified invocations explicit and supports exactly = n, atLeast = n, etc., as named arguments to the outer call.
  • org.openrewrite.kotlin.testing.FindMockitoWhenThenReturn$KtRecipe
    • Find Mockito whenever(...).thenReturn(...) chains
    • Mockito's whenever(call).thenReturn(value) (or whenever(call).thenAnswer \{ … \}) intercepts the method invocation as it happens. mockk records the invocation in a DSL block: every \{ mock.foo() \} returns value. The mockk form composes more naturally with property access and suspending calls.
  • org.openrewrite.kotlin.testing.FindParameterizedTestWithValueSourceStrings$KtRecipe
    • Find @ValueSource(strings = [...]) annotations
    • @ValueSource(strings = [...]) is the simplest @ParameterizedTest data source. For tests where each row drives multiple parameters, @CsvSource(...) or @MethodSource(...) carry more information per case and read more like a table.
  • org.openrewrite.kotlin.testing.FindRepeatedTestAnnotation$KtRecipe
    • Find @RepeatedTest(N) annotations
    • @RepeatedTest(N) runs the same test body N times — useful for flaky-test reproduction, suspicious for asserting on randomized inputs (use @ParameterizedTest + @MethodSource for that). Each match is worth a glance at N and at what the repetition is meant to prove.
  • org.openrewrite.kotlin.testing.FindRunBlockingInTest$KtRecipe
    • Find runBlocking \{ ... \} calls inside test methods
    • runBlocking inside a test method ties the test's wait to real wall-clock time — delay(60_000) is a literal minute. runTest \{ … \} from kotlinx-coroutines-test skips virtual time forward instead, so the same test finishes immediately while preserving suspend ordering.
  • org.openrewrite.kotlin.testing.FindRunBlockingTestCall$KtRecipe
    • Find runBlockingTest \{ ... \} calls
    • runBlockingTest was deprecated in kotlinx-coroutines-test 1.6 in favor of runTest \{ … \}, which uses a TestCoroutineScheduler instead of the old DelayController. The new API has a cleaner contract around how child coroutines are awaited.
  • org.openrewrite.kotlin.testing.FindTagAnnotationUsage$KtRecipe
    • Find @Tag(...) annotations
    • @Tag("slow") is JUnit 5's mechanism for grouping tests so the build can include/exclude them by tag. Useful to flag for tag-name consistency review across modules — if every module has its own spelling of "integration", the build's tag filter doesn't catch them uniformly.
  • org.openrewrite.kotlin.testing.FindTestCoroutineDispatcherUsage$KtRecipe
    • Find TestCoroutineDispatcher allocations
    • TestCoroutineDispatcher was deprecated alongside runBlockingTest. The replacements are StandardTestDispatcher (queues all coroutines to a scheduler) and UnconfinedTestDispatcher (runs them eagerly on the current thread) — pick based on whether the test wants explicit advancement of virtual time.
  • org.openrewrite.kotlin.testing.FindTestFixtureSmells$KtRecipe
    • Find test fixture / setup smells
    • Search-only bundle for test-method shape issues: empty @Test bodies, @Test methods without any recognized assertion call, @Test methods with many assertions (consider parameterized), and @BeforeEach/@Before methods reassigning instance state (lateinit var hint).
  • org.openrewrite.kotlin.testing.FindTestFrameworkSetupSmells$KtRecipe
    • Find JUnit 5 setup smells (informational)
    • Search-only bundle for informational @ParameterizedTest / @RepeatedTest / @Tag / @Disabled review markers. None of these are anti-patterns on their own — each one is worth a once-over for parameter-source choice, repetition intent, tag spelling, or whether the disable is still warranted.
  • org.openrewrite.kotlin.testing.FindTestNoAssertions$KtRecipe
    • Find @Test methods with no recognized assertion calls
    • A test with no assert* / should* / assertThat calls relies on its setup to throw on failure — fine for some smoke tests, suspicious for most. Each match is a candidate for adding an explicit assertion that documents what the test is actually verifying.
  • org.openrewrite.kotlin.testing.FindTooManyAssertions$KtRecipe
    • Find @Test methods with many assertions
    • A test with more than ~7 assertions is usually testing several behaviors at once — when one fails the others go unreported, and the failure message rarely points at the right cause. Split into focused tests, or move to @ParameterizedTest if the assertions are repeating with different inputs.
  • org.openrewrite.kotlin.testing.Testing$KtRecipe
    • Modernize Kotlin test code
    • Find Kotlin-specific test patterns: mockito-kotlin usage where mockk would be idiomatic, deprecated runBlocking / TestCoroutineDispatcher patterns, Kotest assertion migration candidates, empty / assertion-less / many-assertion test bodies, snake_case test names, and Hamcrest call sites that fluent assertion libraries (AssertJ, Kotest) replace cleanly. Each match is a SearchResult for review — nothing is rewritten automatically. For bulk JUnit 4 → JUnit 5 annotation/assertion migration, apply JUnit4to5Migration from rewrite-testing-frameworks.

recipes-scala

rewrite-ai

  • io.moderne.ai.FindAgentsInUse
    • Find AI agents configuration files
    • Scans codebases to identify usage of AI agents by looking at the agent configuration files present in the repository.
  • io.moderne.ai.FindLibrariesInUse
    • Find AI libraries in use
    • Scans codebases to identify usage of AI services. Detects AI libraries across Java dependencies. Useful for auditing and understanding AI integration patterns.
  • io.moderne.ai.FindModelsInUse
    • Find AI models in use
    • Scans codebases to identify usage of Large Language Models (LLMs). Detects model references and configuration patterns across Java classes, properties files, YAML configs... Useful for identifying model usage.

rewrite-angular

  • org.openrewrite.angular.UpgradeToAngular10
    • Upgrade to Angular 10
    • Migrates Angular 9.x applications to Angular 10. This includes removing the deprecated es5BrowserSupport option from angular.json, renaming deprecated validator/asyncValidator to their plural forms, renaming browserslist to .browserslistrc, migrating to solution-style tsconfig.json, and upgrading Angular, TypeScript, and related dependency versions.
  • org.openrewrite.angular.UpgradeToAngular11
    • Upgrade to Angular 11
    • Migrates Angular 10.x applications to Angular 11. This includes replacing ViewEncapsulation.Native with ViewEncapsulation.ShadowDom, removing the deprecated extractCss build option from angular.json, flagging deprecated string-based loadChildren and preserveQueryParams usage, and upgrading Angular, TypeScript, and related dependency versions.
  • org.openrewrite.angular.UpgradeToAngular12
    • Upgrade to Angular 12
    • Migrates Angular 11.x applications to Angular 12. This includes adding defaultConfiguration: "production" to build targets in angular.json, replacing node-sass with sass (Dart Sass), flagging deprecated async test helper and View Engine APIs, and upgrading Angular, TypeScript, and related dependency versions.
  • org.openrewrite.angular.UpgradeToAngular13
    • Upgrade to Angular 13
    • Migrates Angular 12.x applications to Angular 13. This includes updating tsconfig.json target to es2017, removing IE11 polyfills, removing defaultProject from angular.json, adding TestBed module teardown, simplifying ComponentFactoryResolver usage, and upgrading Angular, TypeScript, and related dependency versions.
  • org.openrewrite.angular.UpgradeToAngular14
    • Upgrade to Angular 14
    • Migrates Angular 13.x applications to Angular 14. This includes replacing form classes with their Untyped* equivalents for backward compatibility with typed forms, updating deprecated initialNavigation router option values, removing aotSummaries from TestBed calls, and flagging pathMatch properties that may need type narrowing.
  • org.openrewrite.angular.UpgradeToAngular15
    • Upgrade to Angular 15
    • Migrates Angular 14.x applications to Angular 15. This includes removing the relativeLinkResolution option from RouterModule.forRoot(), removing the enableIvy compiler option from tsconfig.json, flagging the deprecated DATE_PIPE_DEFAULT_TIMEZONE token and providedIn: NgModule/'any' usage, and upgrading Angular, TypeScript, and related dependency versions.
  • org.openrewrite.angular.UpgradeToAngular16
    • Upgrade to Angular 16
    • Migrates Angular 15.x applications to Angular 16. This includes removing entryComponents and moduleId from decorators, replacing RouterLinkWithHref with RouterLink, moving the XhrFactory import to @angular/common, and flagging removed APIs like ReflectiveInjector, renderModuleFactory, and BrowserTransferStateModule.
  • org.openrewrite.angular.UpgradeToAngular17
    • Upgrade to Angular 17
    • Migrates Angular 16.x applications to Angular 17. This includes updating Angular package versions, replacing legacy deep zone.js imports, flagging the removed withNoDomReuse and setupTestingRouter APIs, and upgrading TypeScript and zone.js dependencies.
  • org.openrewrite.angular.UpgradeToAngular18
    • Upgrade to Angular 18
    • Migrates Angular 17.x applications to Angular 18. This includes replacing the deprecated async test helper with waitForAsync, migrating HttpClientModule to provideHttpClient(), moving Transfer State APIs to @angular/core, and flagging removed platform APIs.
  • org.openrewrite.angular.UpgradeToAngular19
    • Upgrade to Angular 19
    • Migrates Angular 18.x applications to Angular 19. This includes updating Angular package versions, adjusting the standalone default, renaming ExperimentalPendingTasks to PendingTasks, moving the ApplicationConfig import to @angular/core, and updating zone.js.
  • org.openrewrite.angular.UpgradeToAngular20
    • Upgrade to Angular 20
    • Migrates Angular 19.x applications to Angular 20. This includes running the Angular 19 migration first, then updating Angular package versions, renaming experimental APIs promoted to stable, and upgrading TypeScript to 5.8.x.
  • org.openrewrite.angular.UpgradeToAngular21
    • Upgrade to Angular 21
    • Migrates Angular 20.x applications to Angular 21. This includes running the Angular 20 migration first, flagging Karma test runner usage for Vitest migration, deprecated NgClass, zone.js-dependent test helpers, and upgrading TypeScript to 5.9.x.
  • org.openrewrite.angular.UpgradeToAngular8
    • Upgrade to Angular 8
    • Migrates Angular 7.x applications to Angular 8. This includes adding the now-required static: false to @ViewChild and @ContentChild decorators, moving the DOCUMENT import from @angular/platform-browser to @angular/common, removing rxjs-compat and flagging any remaining RxJS 5-style imports, flagging removed @angular/http imports, converting deprecated string-based loadChildren to dynamic imports, and upgrading Angular, TypeScript, and related dependency versions.
  • org.openrewrite.angular.UpgradeToAngular9
    • Upgrade to Angular 9
    • Migrates Angular 8.x applications to Angular 9. This includes removing the now-default static: false from view query decorators, replacing TestBed.get() with TestBed.inject(), adding generic type parameters to ModuleWithProviders, enabling AOT compilation in angular.json, updating tsconfig.json module settings for Ivy, flagging removed View Engine APIs (Renderer, RenderComponentType, RootRenderer), and upgrading Angular, TypeScript, and related dependency versions.
  • org.openrewrite.angular.migration.add-default-configuration
    • Add defaultConfiguration to build targets
    • Adds "defaultConfiguration": "production" to build architect targets in angular.json. Angular 12 changed ng build to produce production bundles by default.
  • org.openrewrite.angular.migration.add-localize-polyfill
    • Add @angular/localize/init polyfill import
    • Adds import '@angular/localize/init' to polyfills.ts. Angular 9 introduced the $localize runtime API for i18n. Projects using internationalization must import this polyfill or the application will fail at runtime with $localize is not defined. The @angular/localize package must also be added as a dependency.
  • org.openrewrite.angular.migration.add-module-with-providers-generic
    • Add generic type to ModuleWithProviders
    • Adds the required generic type parameter to bare ModuleWithProviders return types. Angular 10 requires ModuleWithProviders<T> where T is the NgModule type. The module type is inferred from the ngModule property in the return statement.
  • org.openrewrite.angular.migration.add-static-false-to-view-queries
    • Add static: false to view queries
    • Adds static: false to @ViewChild and @ContentChild decorators that don't have the static property. Angular 8 requires an explicit static flag for view query decorators. Using static: false preserves the Angular 7 default behavior (queries resolved after change detection).
  • org.openrewrite.angular.migration.add-testbed-teardown
    • Add TestBed module teardown
    • Adds \{ teardown: \{ destroyAfterEach: true \} \} as the third argument to TestBed.initTestEnvironment() calls. Angular 13 changed the default teardown behavior, and this ensures explicit opt-in for module teardown after each test.
  • org.openrewrite.angular.migration.enable-aot-build
    • Enable AOT compilation in angular.json
    • Adds "aot": true to build options in angular.json. Angular 9 made AOT compilation the default, and projects upgrading from Angular 8 should enable it explicitly.
  • org.openrewrite.angular.migration.explicit-standalone-flag
    • Make standalone flag explicit
    • Adds standalone: false to non-standalone Angular components, directives, and pipes, and removes redundant standalone: true since it became the default in Angular 19.
  • org.openrewrite.angular.migration.migrate-constructor-to-inject
    • Migrate constructor injection to inject()
    • Converts constructor parameter properties in Angular classes to field declarations using the inject() function. For example, constructor(private svc: MyService) \{\} becomes private svc = inject(MyService);.
  • org.openrewrite.angular.migration.migrate-input-to-signal
    • Migrate @Input() to signal-based input()
    • Converts @Input() decorated properties in Angular classes to signal-based input() declarations. For example, @Input() name: string becomes name = input<string>(), and @Input(\{ required: true \}) name!: string becomes name = input.required<string>().
  • org.openrewrite.angular.migration.migrate-output-to-signal
    • Migrate @Output() to signal-based output()
    • Converts @Output() decorated properties using EventEmitter in Angular classes to signal-based output() declarations. For example, @Output() clicked = new EventEmitter<void>() becomes clicked = output<void>().
  • org.openrewrite.angular.migration.migrate-query-to-signal
    • Migrate query decorators to signal-based functions
    • Converts @ViewChild(), @ViewChildren(), @ContentChild(), and @ContentChildren() decorated properties to signal-based query functions. For example, @ViewChild('ref') el: ElementRef becomes el = viewChild<ElementRef>('ref').
  • org.openrewrite.angular.migration.migrate-to-solution-style-tsconfig
    • Migrate to solution-style tsconfig
    • Migrates a project to use a solution-style tsconfig.json. The original tsconfig.json content is moved to tsconfig.base.json (with project-specific fields removed), and tsconfig.json is replaced with a solution-style config that references the project's TypeScript configurations. Other tsconfig files that extend ./tsconfig.json are updated to extend ./tsconfig.base.json.
  • org.openrewrite.angular.migration.move-document-import
    • Move DOCUMENT import to @angular/core
    • Moves the DOCUMENT import from older Angular modules to @angular/core.
  • org.openrewrite.angular.migration.remove-aot-summaries
    • Remove aotSummaries from TestBed
    • Removes the aotSummaries property from TestBed.configureTestingModule() and TestBed.initTestEnvironment() calls. The aotSummaries parameter was removed in Angular 14 as it was only needed for the View Engine compiler.
  • org.openrewrite.angular.migration.remove-browser-module-with-server-transition
    • Remove BrowserModule.withServerTransition
    • Replaces BrowserModule.withServerTransition(\{ appId: '...' \}) with BrowserModule and adds \{ provide: APP_ID, useValue: '...' \} to the NgModule providers. The withServerTransition method was removed in Angular 19.
  • org.openrewrite.angular.migration.remove-component-factory-resolver
    • Remove ComponentFactoryResolver
    • Replaces resolver.resolveComponentFactory(Component) with just Component and removes the ComponentFactoryResolver import. Since Ivy, ViewContainerRef.createComponent accepts the component class directly. ComponentFactoryResolver was deprecated in Angular 13 and removed in Angular 16.
  • org.openrewrite.angular.migration.remove-default-project
    • Remove defaultProject from angular.json
    • Removes the deprecated defaultProject property from angular.json. The defaultProject option was deprecated in Angular 13 and the CLI infers the default project from the workspace.
  • org.openrewrite.angular.migration.remove-empty-ng-on-init
    • Remove empty ngOnInit lifecycle hooks
    • Removes empty ngOnInit lifecycle hook methods and OnInit interface from Angular components.
  • org.openrewrite.angular.migration.remove-enable-ivy
    • Remove enableIvy compiler option
    • Removes the enableIvy option from angularCompilerOptions in tsconfig.json. Ivy is the only rendering engine since Angular 12, and the option was removed in Angular 15.
  • org.openrewrite.angular.migration.remove-entry-components
    • Remove entryComponents
    • Removes the entryComponents property from @NgModule and @Component decorators, and removes the ANALYZE_FOR_ENTRY_COMPONENTS import. These were removed in Angular 16 as they served no purpose since Ivy.
  • org.openrewrite.angular.migration.remove-es5-browser-support
    • Remove es5BrowserSupport from angular.json
    • Removes the deprecated es5BrowserSupport option from angular.json. es5BrowserSupport was deprecated in Angular 7.3 and removed in Angular 10. Differential loading is now handled automatically by the Angular CLI based on the project's browserslist configuration.
  • org.openrewrite.angular.migration.remove-extract-css
    • Remove extractCss from angular.json
    • Removes the deprecated extractCss build option from angular.json. In Angular 11, CSS extraction became the default behavior for production builds and the option was deprecated.
  • org.openrewrite.angular.migration.remove-ie-polyfills
    • Remove IE11 polyfills
    • Removes IE11-specific polyfill imports (core-js, classlist.js, web-animations-js) from polyfills.ts and angular.json. Angular 13 dropped IE11 support, making these polyfills unnecessary.
  • org.openrewrite.angular.migration.remove-module-id
    • Remove moduleId
    • Removes the moduleId property from @Component and @Directive decorators. moduleId was deprecated in Angular 16 and removed in Angular 17 as it served no purpose since Ivy.
  • org.openrewrite.angular.migration.remove-relative-link-resolution
    • Remove relativeLinkResolution
    • Removes the relativeLinkResolution option from RouterModule.forRoot() calls. This option was deprecated in Angular 14 and removed in Angular 15.
  • org.openrewrite.angular.migration.remove-standalone-true
    • Remove redundant standalone: true
    • Removes the standalone: true property from Angular component, directive, and pipe decorators since standalone is the default in Angular 19+.
  • org.openrewrite.angular.migration.remove-static-false
    • Remove static: false from view queries
    • Removes static: false from @ViewChild, @ContentChild, @ViewChildren, and @ContentChildren decorators. In Angular 9 with Ivy, static: false became the default behavior, making the explicit option unnecessary.
  • org.openrewrite.angular.migration.remove-zone-js-polyfill
    • Remove zone.js polyfill from angular.json
    • Removes zone.js entries from the polyfills array in angular.json. Angular 20 supports zoneless change detection via provideZonelessChangeDetection(), making the zone.js polyfill unnecessary.
  • org.openrewrite.angular.migration.rename-after-render
    • Rename afterRender to afterEveryRender
    • Renames afterRender to afterEveryRender in imports and usages. The afterRender function was renamed to afterEveryRender in Angular 20, and Angular provides no migration schematic for this change.
  • org.openrewrite.angular.migration.rename-check-no-changes
    • Rename provideExperimentalCheckNoChangesForDebug to provideCheckNoChangesForDebug
    • Renames provideExperimentalCheckNoChangesForDebug to provideCheckNoChangesForDebug in imports and usages. The experimental API was promoted to developer preview in Angular 20.
  • org.openrewrite.angular.migration.rename-file
    • Rename file
    • Renames files matching a glob pattern to a new file name, preserving the directory.
  • org.openrewrite.angular.migration.rename-pending-tasks
    • Rename ExperimentalPendingTasks to PendingTasks
    • Renames ExperimentalPendingTasks to PendingTasks in imports and usages. ExperimentalPendingTasks was renamed in Angular 19.
  • org.openrewrite.angular.migration.rename-zoneless-provider
    • Rename provideExperimentalZonelessChangeDetection to provideZonelessChangeDetection
    • Renames provideExperimentalZonelessChangeDetection to provideZonelessChangeDetection in imports and usages. The experimental API was promoted to developer preview in Angular 20.
  • org.openrewrite.angular.migration.replace-async-with-wait-for-async
    • Replace async with waitForAsync
    • Replaces the removed async test helper from @angular/core/testing with waitForAsync. The async function was deprecated in Angular 11 and removed in Angular 18.
  • org.openrewrite.angular.migration.replace-deep-zone-js-imports
    • Replace deep zone.js imports
    • Replaces legacy deep imports from zone.js such as zone.js/dist/zone or zone.js/bundles/zone-testing.js with the standard zone.js or zone.js/testing imports, in both TypeScript files and angular.json polyfills. Deep imports are no longer allowed in Angular 17.
  • org.openrewrite.angular.migration.replace-http-client-module
    • Replace HttpClientModule with provideHttpClient()
    • Replaces deprecated HttpClientModule, HttpClientJsonpModule, HttpClientXsrfModule, and HttpClientTestingModule with their functional equivalents: provideHttpClient() with feature functions and provideHttpClientTesting().
  • org.openrewrite.angular.migration.replace-initial-navigation
    • Replace initialNavigation option values
    • Replaces deprecated initialNavigation router option values: 'legacy_enabled' and true become 'enabledBlocking', 'legacy_disabled' and false become 'disabled', and 'enabled' becomes 'enabledNonBlocking'. The legacy values were removed in Angular 11; 'enabled' was renamed in Angular 14.
  • org.openrewrite.angular.migration.replace-inject-flags
    • Replace InjectFlags with options object
    • Replaces deprecated InjectFlags enum usage in inject() calls with the corresponding options object. For example, inject(MyService, InjectFlags.Optional) becomes inject(MyService, \{ optional: true \}).
  • org.openrewrite.angular.migration.replace-load-children-string
    • Replace string-based loadChildren with dynamic import()
    • Converts the deprecated string-based loadChildren: 'path#Module' syntax to dynamic imports: loadChildren: () => import('path').then(m => m.Module).
  • org.openrewrite.angular.migration.replace-node-sass-with-sass
    • Replace node-sass with sass
    • Replaces the deprecated node-sass package with sass (Dart Sass). Angular 12 requires Dart Sass; node-sass is no longer supported.
  • org.openrewrite.angular.migration.replace-router-link-with-href
    • Replace RouterLinkWithHref with RouterLink
    • Replaces RouterLinkWithHref with RouterLink in imports and usages. RouterLinkWithHref was merged into RouterLink in Angular 16.
  • org.openrewrite.angular.migration.replace-testbed-get-with-inject
    • Replace TestBed.get() with TestBed.inject()
    • Replaces deprecated TestBed.get() calls with TestBed.inject(). TestBed.get() was deprecated in Angular 9 and removed in Angular 13.
  • org.openrewrite.angular.migration.replace-untyped-forms
    • Replace form classes with untyped variants
    • Renames FormControl, FormGroup, FormArray, and FormBuilder to their Untyped* equivalents in imports and usages. Angular 14 introduced strictly typed forms, requiring existing untyped usages to migrate to the Untyped* aliases. Classes used in parameterized type positions (e.g. FormGroup<T>) are left unchanged because the user already opted into typed forms.
  • org.openrewrite.angular.migration.replace-validator-with-validators
    • Replace validator/asyncValidator with plural forms
    • Renames the deprecated singular validator and asyncValidator property names to validators and asyncValidators (plural). Angular 10 deprecated the singular forms in favor of AbstractControlOptions.
  • org.openrewrite.angular.migration.replace-view-encapsulation-native
    • Replace ViewEncapsulation.Native with ViewEncapsulation.ShadowDom
    • Replaces ViewEncapsulation.Native with ViewEncapsulation.ShadowDom. ViewEncapsulation.Native was deprecated in Angular 6 and removed in Angular 11.
  • org.openrewrite.angular.migration.update-component-template-url
    • Update component templateUrl
    • Updates the templateUrl property value in Angular @Component decorators. Useful for refactoring template file paths or standardizing path conventions.
  • org.openrewrite.angular.migration.update-tsconfig-module
    • Update tsconfig.json module settings for Ivy
    • Updates compilerOptions.module to esnext and compilerOptions.moduleResolution to node in tsconfig.json. Angular 9's Ivy compiler requires ES module format. Already-current values like es2020, node16, nodenext, or bundler are left unchanged.
  • org.openrewrite.angular.migration.update-tsconfig-target
    • Update tsconfig.json target to es2017
    • Updates the compilerOptions.target in tsconfig.json from es5, es2015, or es2016 to es2017. Angular 13 dropped IE11 support and requires at least ES2017.
  • org.openrewrite.angular.search.FindAngularComponent
    • Find Angular component
    • Locates usages of Angular components across the codebase including template elements and other references. If componentName is null, finds all Angular components.
  • org.openrewrite.angular.search.find-analyze-for-entry-components-usage
    • Find deprecated ANALYZE_FOR_ENTRY_COMPONENTS usage
    • Finds usages of the deprecated ANALYZE_FOR_ENTRY_COMPONENTS injection token from @angular/core. ANALYZE_FOR_ENTRY_COMPONENTS was deprecated in Angular 9 and removed in Angular 13.
  • org.openrewrite.angular.search.find-angular-decorator
    • Find Angular decorators
    • Finds all Angular decorators like @Component, @Directive, @Injectable, etc.
  • org.openrewrite.angular.search.find-angular-http-usage
    • Find removed @angular/http usage
    • Finds imports from the @angular/http module, which was deprecated in Angular 5 and removed in Angular 8. Use @angular/common/http (HttpClient, HttpClientModule) instead.
  • org.openrewrite.angular.search.find-animation-driver-matches-element
    • Find AnimationDriver.matchesElement usage
    • Finds imports of AnimationDriver from @angular/animations/browser, which had its matchesElement method removed in Angular 18.
  • org.openrewrite.angular.search.find-async-test-helper-usage
    • Find deprecated async test helper usage
    • Finds usages of the deprecated async test helper from @angular/core/testing. The async function was deprecated in Angular 11 and should be replaced with waitForAsync.
  • org.openrewrite.angular.search.find-bare-module-with-providers
    • Find ModuleWithProviders without generic type
    • Finds imports of ModuleWithProviders from @angular/core. Starting in Angular 10, ModuleWithProviders requires a generic type parameter (e.g. ModuleWithProviders<MyModule>). Ensure all usages specify the module type.
  • org.openrewrite.angular.search.find-browser-transfer-state-module-usage
    • Find BrowserTransferStateModule usage
    • Finds usages of BrowserTransferStateModule from @angular/platform-browser which was removed in Angular 16. TransferState can be used directly without this module.
  • org.openrewrite.angular.search.find-common-module-usage
    • Find CommonModule usage
    • Finds imports of CommonModule from @angular/common. Since Angular 19, standalone components are the default and CommonModule is no longer needed in component imports arrays. Built-in directives and pipes are available automatically.
  • org.openrewrite.angular.search.find-compiler-factory-usage
    • Find View Engine API usage
    • Finds usages of View Engine APIs from @angular/core (CompilerFactory, Compiler, CompilerOptions, ModuleWithComponentFactories, NgModuleFactory, NgModuleFactoryLoader) which were deprecated in Angular 13.
  • org.openrewrite.angular.search.find-date-pipe-default-timezone-usage
    • Find DATE_PIPE_DEFAULT_TIMEZONE usage
    • Finds usages of DATE_PIPE_DEFAULT_TIMEZONE which was deprecated in Angular 15. Use DATE_PIPE_DEFAULT_OPTIONS with a \{timezone: '...'\} object value instead.
  • org.openrewrite.angular.search.find-effect-timing-usage
    • Find effect() usage affected by Angular 19 timing changes
    • Finds effect() calls from @angular/core. In Angular 19, effects triggered outside change detection now run as part of the change detection process instead of as a microtask, and effects triggered during change detection run earlier, before the component's template.
  • org.openrewrite.angular.search.find-empty-projectable-nodes
    • Find createComponent calls with empty projectableNodes
    • Finds createComponent() calls that pass empty arrays in projectableNodes. In Angular 19, passing an empty array now renders the default ng-content fallback content. To suppress fallback content, pass [document.createTextNode('')] instead.
  • org.openrewrite.angular.search.find-fake-async-usage
    • Find zone.js-dependent test helper usage
    • Finds fakeAsync(), tick(), and waitForAsync() calls from @angular/core/testing. These zone.js-dependent test helpers are incompatible with Vitest, the default test runner in Angular 21. Migrate to native async/await patterns instead.
  • org.openrewrite.angular.search.find-hammer-js-usage
    • Find HammerJS usage
    • Finds HammerModule imports and HammerJS references. Angular has deprecated HammerJS support and it will be removed in Angular 21.
  • org.openrewrite.angular.search.find-i18n-usage
    • Find i18n usage
    • Finds i18n usage indicators: legacy i18n configuration in angular.json (i18nLocale, i18nFile, i18nFormat, i18nMissingTranslation), $localize tagged template literals, and @angular/localize imports. Projects with these markers need @angular/localize installed and import '@angular/localize/init' in polyfills.ts for Angular 9+.
  • org.openrewrite.angular.search.find-karma-usage
    • Find Karma test runner usage
    • Finds Karma test runner configuration in package.json dependencies and angular.json test builder. Angular 21 replaces Karma with Vitest as the default test runner.
  • org.openrewrite.angular.search.find-load-children-string-usage
    • Find deprecated string-based loadChildren usage
    • Finds usages of the deprecated string-based loadChildren syntax (e.g. loadChildren: './path/to/module#ModuleName'). String-based lazy loading was deprecated in Angular 8 and removed in Angular 11. Use dynamic imports instead: loadChildren: () => import('./path/to/module').then(m => m.ModuleName).
  • org.openrewrite.angular.search.find-missing-injectable
    • Find classes with DI dependencies but missing @Injectable()
    • Finds classes that have constructor parameters (suggesting dependency injection) but lack an @Injectable() or other Angular class-level decorator. Angular 9 with Ivy requires an explicit @Injectable() decorator for all services that use dependency injection.
  • org.openrewrite.angular.search.find-ng-class-usage
    • Find NgClass usage
    • Finds imports of NgClass from @angular/common. The ngClass directive is soft deprecated in Angular 21 in favor of native [class.*] bindings.
  • org.openrewrite.angular.search.find-ng-style-usage
    • Find NgStyle usage
    • Finds imports of NgStyle from @angular/common. The ngStyle directive is soft deprecated in Angular 21 in favor of native [style.*] bindings.
  • org.openrewrite.angular.search.find-path-match-type-usage
    • Find pathMatch route properties that may need type narrowing
    • Finds pathMatch property assignments in route configurations. In Angular 14, the pathMatch type was narrowed from string to 'full' | 'prefix'. Routes defined as plain objects without explicit Route or Routes typing may fail type checking.
  • org.openrewrite.angular.search.find-platform-dynamic-server-usage
    • Find platformDynamicServer usage
    • Finds usages of the removed platformDynamicServer API from @angular/platform-server. In Angular 18, replace with platformServer and add import '@angular/compiler'.
  • org.openrewrite.angular.search.find-platform-webworker-usage
    • Find removed @angular/platform-webworker usage
    • Finds imports from @angular/platform-webworker and @angular/platform-webworker-dynamic, which were removed in Angular 8 with no direct replacement.
  • org.openrewrite.angular.search.find-platform-worker-usage
    • Find isPlatformWorkerUi and isPlatformWorkerApp usage
    • Finds usages of the removed isPlatformWorkerUi and isPlatformWorkerApp APIs from @angular/common. These were removed in Angular 18 with no replacement, as they served no purpose since the removal of the WebWorker platform.
  • org.openrewrite.angular.search.find-preserve-fragment-usage
    • Find deprecated preserveFragment usage
    • Finds usages of the deprecated preserveFragment navigation option. preserveFragment was deprecated in Angular 4 and removed in Angular 11. Fragments are now preserved by default.
  • org.openrewrite.angular.search.find-preserve-query-params-usage
    • Find deprecated preserveQueryParams usage
    • Finds usages of the deprecated preserveQueryParams navigation option. preserveQueryParams was deprecated in Angular 4 and removed in Angular 11. Use queryParamsHandling: 'preserve' instead.
  • org.openrewrite.angular.search.find-provided-in-deprecated-usage
    • Find deprecated providedIn values
    • Finds usages of providedIn: 'any' and providedIn: NgModule in @Injectable and InjectionToken declarations. These were deprecated in Angular 15. Use providedIn: 'root' or add the service to NgModule.providers instead.
  • org.openrewrite.angular.search.find-reflective-injector-usage
    • Find ReflectiveInjector usage
    • Finds usages of ReflectiveInjector which was removed in Angular 16. Use Injector.create as a replacement.
  • org.openrewrite.angular.search.find-render-application-usage
    • Find renderApplication usage
    • Finds usages of renderApplication from @angular/platform-server. In Angular 16 the signature changed: it no longer accepts a root component as the first argument. Use a bootstrapping function that returns Promise<ApplicationRef> instead.
  • org.openrewrite.angular.search.find-render-component-type-usage
    • Find deprecated RenderComponentType usage
    • Finds imports of the deprecated RenderComponentType from @angular/core. RenderComponentType was part of the View Engine API, deprecated in Angular 4, and removed in Angular 9.
  • org.openrewrite.angular.search.find-render-module-factory-usage
    • Find renderModuleFactory usage
    • Finds usages of renderModuleFactory from @angular/platform-server which was removed in Angular 16. Use renderModule instead.
  • org.openrewrite.angular.search.find-renderer-usage
    • Find deprecated Renderer usage
    • Finds imports of the deprecated Renderer from @angular/core. Renderer was deprecated in Angular 4 and removed in Angular 9. Users should use Renderer2 instead.
  • org.openrewrite.angular.search.find-resource-cache-provider-usage
    • Find RESOURCE_CACHE_PROVIDER usage
    • Finds usages of the removed RESOURCE_CACHE_PROVIDER from @angular/platform-browser-dynamic. This unused API was removed in Angular 18.
  • org.openrewrite.angular.search.find-root-renderer-usage
    • Find deprecated RootRenderer usage
    • Finds imports of the deprecated RootRenderer from @angular/core. RootRenderer was part of the View Engine API, deprecated in Angular 4, and removed in Angular 9. Use RendererFactory2 instead.
  • org.openrewrite.angular.search.find-rxjs-compat-usage
    • Find RxJS 5-style imports requiring rxjs-compat
    • Finds imports using RxJS 5-style deep import paths (e.g. rxjs/Observable, rxjs/add/operator/map) that require the rxjs-compat package. These should be migrated to RxJS 6+ import paths before removing rxjs-compat.
  • org.openrewrite.angular.search.find-server-transfer-state-module-usage
    • Find ServerTransferStateModule usage
    • Finds usages of the removed ServerTransferStateModule from @angular/platform-server. In Angular 18, TransferState works without providing this module.
  • org.openrewrite.angular.search.find-setup-testing-router-usage
    • Find setupTestingRouter usage
    • Finds usages of the removed setupTestingRouter function from @angular/router/testing. This function was removed in Angular 17. Use RouterModule.forRoot or provideRouter to set up the Router for tests instead.
  • org.openrewrite.angular.search.find-testability-pending-request-usage
    • Find removed Testability pending request methods
    • Finds imports of Testability from @angular/core, which had increasePendingRequestCount, decreasePendingRequestCount, and getPendingRequestCount removed in Angular 18. These are now tracked with zones.
  • org.openrewrite.angular.search.find-undecorated-angular-class
    • Find undecorated classes with Angular features
    • Finds classes that use Angular member decorators (@Input, @Output, @ViewChild, etc.) or implement lifecycle hooks (ngOnInit, ngOnDestroy, etc.) but lack a class-level Angular decorator. Angular 9 with Ivy requires all classes using Angular features to have an explicit decorator.
  • org.openrewrite.angular.search.find-with-no-dom-reuse-usage
    • Find withNoDomReuse usage
    • Finds usages of the removed withNoDomReuse function from @angular/platform-browser. This function was removed in Angular 17. To disable hydration, remove the provideClientHydration() call from your providers or use the ngSkipHydration attribute on specific components.
  • org.openrewrite.angular.search.find-wrapped-value-usage
    • Find deprecated WrappedValue usage
    • Finds usages of the deprecated WrappedValue from @angular/core. WrappedValue was deprecated in Angular 11 and removed in Angular 13.
  • org.openrewrite.angular.search.find-zone-js-usage
    • Find zone.js usage
    • Finds zone.js imports and NgZone references. Angular 20 supports zoneless change detection via provideZonelessChangeDetection(), making zone.js optional.
  • org.openrewrite.primeng.AddPrimengProvider
    • Add providePrimeNG with a detected theme preset to the root NgModule
    • Wires the v18 styled mode into an NgModule-based app by adding providePrimeNG(\{ theme: \{ preset: <Preset> \} \}) to the root @NgModule's providers array (detected by the presence of a bootstrap: field). The preset is chosen by scanning angular.json for a primeng/resources/themes/<themeName>/theme.css entry: lara-* maps to Lara, md-*/mdc-* to Material, nora/nano to Nora, and any other v17 theme (mira, nova, saga, vela, soho, fluent, viva, rhea, tailwind, bootstrap4, arya, luna, ...) falls back to Aura. The matching imports for providePrimeNG and the chosen preset are added automatically. Also deletes the now-defunct primeng/resources style entries from angular.json so the build doesn't try to load missing files. Idempotent: skips files that already call providePrimeNG.
  • org.openrewrite.primeng.MarkDeprecatedPrimengComponents
    • Mark deprecated PrimeNG components with TODO comments
    • For every TS file that imports a component / module deprecated in PrimeNG 18 (TabMenu, Steps, InlineMessage, TabView, pDefer), prepends a TODO comment to the import describing the recommended v18 replacement and writes a row to the ManualMigrationSteps data table. The import itself is left intact — these modules still exist in v18 but their replacements have different APIs that require manual migration.
  • org.openrewrite.primeng.MarkDeprecatedPrimengCssClasses
    • Mark deprecated PrimeNG CSS classes with TODO comments
    • For every HTML template that references a CSS class removed in PrimeNG 18 (.p-link, .p-highlight, .p-fluid), inserts a <!-- TODO: ... --> comment immediately before the offending element and writes a row to the ManualMigrationSteps data table. The class itself is left in place — the replacements are context-dependent (component-specific selectors, the new fluid input, etc.) and need a human or AI agent to apply.
  • org.openrewrite.primeng.MarkDrawerSize
    • Mark <p-drawer> / <p-sidebar> size usages with TODO comments
    • Inserts an HTML <!-- TODO: ... --> comment before any <p-drawer> or <p-sidebar> element that binds the removed size input, and records the site in the ManualMigrationSteps data table. Both [size]="..." and size="..." attribute forms are matched. The attribute is left untouched — the v18 replacement (responsive CSS via [style] / styleClass) depends on the desired layout and needs manual review.
  • org.openrewrite.primeng.MarkRemovedPrimengModules
    • Mark imports of removed PrimeNG modules with TODO stubs
    • For each import of a PrimeNG module that no longer exists in v18 (primeng/chips, primeng/tristatecheckbox, primeng/messages, primeng/dataviewlayoutoptions), replaces the broken import statement with a const <Name>: any = null; stub annotated by a TODO comment that describes the v18 replacement. Also strips the corresponding entries from @NgModule imports, declarations, and exports arrays since Angular's compiler rejects null values there. Each flagged site is also recorded in the ManualMigrationSteps data table so downstream tooling can enumerate the remaining work.
  • org.openrewrite.primeng.MigrateMessagesToMessageLoop
    • Migrate <p-messages> to <p-message> with @for loop
    • Rewrites <p-messages [value]="expr">…</p-messages> to @for (msg of expr; track msg) \{ <p-message [severity]="msg.severity" [text]="msg.detail"></p-message> \}. The Messages component was removed in PrimeNG 18 in favor of looping over the new Message component. Each rewritten site is recorded in the ManualMigrationSteps data table for follow-up review.
  • org.openrewrite.primeng.MigratePFluidToWrapper
    • Migrate .p-fluid to <p-fluid> wrapper
    • Rewrites <div class="…p-fluid…">…</div> to <p-fluid class="…">…</p-fluid> and adds a FluidModule import from primeng/fluid to the corresponding component file. PrimeNG 18 removed the .p-fluid CSS class; the <p-fluid> wrapper component is its replacement.
  • org.openrewrite.primeng.MigratePrimeNGSignalAssignments
    • Migrate PrimeNG config field assignments to .set()
    • In PrimeNG 18, fields on the PrimeNG config service like ripple, inputStyle, inputVariant, and csp are WritableSignal<T> rather than plain fields. Direct assignment (service.ripple = true) no longer compiles. This recipe rewrites such assignments to use the signal's set() method (service.ripple.set(true)) when the file imports PrimeNG from primeng/config.
  • org.openrewrite.primeng.MigratePrimeNgConfigToPrimeNG
    • Migrate PrimeNGConfig to PrimeNG
    • Renames the PrimeNGConfig import from primeng/api to PrimeNG from primeng/config, renames all identifier usages, and flags injection sites that should be migrated to providePrimeNG() in application providers.
  • org.openrewrite.primeng.RenameCalendarToDatePicker
    • Rename Calendar to DatePicker
    • Renames Calendar and CalendarModule imports from primeng/calendar to DatePicker and DatePickerModule from primeng/datepicker, and updates all identifier usages. The old names are deprecated in PrimeNG 18.
  • org.openrewrite.primeng.RenameDropdownToSelect
    • Rename Dropdown to Select
    • Renames Dropdown and DropdownModule imports from primeng/dropdown to Select and SelectModule from primeng/select, and updates all identifier usages. The old names are deprecated in PrimeNG 18.
  • org.openrewrite.primeng.RenameInputSwitchToToggleSwitch
    • Rename InputSwitch to ToggleSwitch
    • Renames InputSwitch and InputSwitchModule imports from primeng/inputswitch to ToggleSwitch and ToggleSwitchModule from primeng/toggleswitch, and updates all identifier usages. The old names are deprecated in PrimeNG 18.
  • org.openrewrite.primeng.RenameMessageInterface
    • Rename Message interface to ToastMessageOptions
    • Renames the Message interface import from primeng/api to ToastMessageOptions and updates all identifier usages. The Message interface was renamed in PrimeNG 18 due to name collision with the Message component.
  • org.openrewrite.primeng.RenameOverlayPanelToPopover
    • Rename OverlayPanel to Popover
    • Renames OverlayPanel and OverlayPanelModule imports from primeng/overlaypanel to Popover and PopoverModule from primeng/popover, and updates all identifier usages. The old names are deprecated in PrimeNG 18.
  • org.openrewrite.primeng.RenameSidebarToDrawer
    • Rename Sidebar to Drawer
    • Renames Sidebar and SidebarModule imports from primeng/sidebar to Drawer and DrawerModule from primeng/drawer, and updates all identifier usages. The old names are deprecated in PrimeNG 18.
  • org.openrewrite.primeng.RenameTemplateSelectors
    • Rename PrimeNG selectors in HTML templates to their v18 equivalents
    • Renames v17 PrimeNG component selectors in .html templates to their v18 names: <p-calendar><p-datepicker>, <p-dropdown><p-select>, <p-inputSwitch><p-toggleSwitch>, <p-overlayPanel><p-popover>, <p-sidebar><p-drawer>. Both opening and closing tags are rewritten.
  • org.openrewrite.primeng.UpgradeComponentsTo18
    • Upgrade PrimeNG components to 18
    • Handles component renames, deprecations, and removals for PrimeNG 18. Renames Calendar to DatePicker, Dropdown to Select, InputSwitch to ToggleSwitch, OverlayPanel to Popover, and Sidebar to Drawer (TS imports + identifier usages + HTML selectors). Migrates the Messages template usage to the <p-message> + @for loop. Marks removed modules (Chips, TriStateCheckbox, Messages, DataViewLayoutOptions, pAnimate) with TODO stubs, marks deprecated components (TabMenu, Steps, InlineMessage, TabView, pDefer) with TODO comments on their imports, and marks deprecated CSS classes (.p-link, .p-highlight, .p-fluid) and <p-drawer>/<p-sidebar> size usages with HTML TODO comments. All marked sites are written to the ManualMigrationSteps data table.
  • org.openrewrite.primeng.UpgradeTo18
    • Upgrade to PrimeNG 18
    • Migrates PrimeNG 17.x applications to PrimeNG 18. Renames components, migrates PrimeNGConfig to PrimeNG (with signal-backed setters), comments out the obsolete primeng/resources style entries in angular.json, wires providePrimeNG(\{ theme: \{ preset: Aura \} \}) into the root NgModule and adds @primeng/themes to package.json. Anything that can't be deterministically migrated (removed-and-no-direct-replacement components, deprecated CSS classes, structural template changes) gets a TODO comment in source plus a row in the ManualMigrationSteps data table for an agent or human to finish.

rewrite-cryptography

  • io.moderne.cryptography.FindCryptoVulnerabilitiesPipeline
    • Find cryptographic vulnerability chains
    • Detects cryptographic vulnerabilities that span multiple operations, tracking flow from hardcoded algorithms through key material to encryption operations.
  • io.moderne.cryptography.FindDirectSSLConfigurationEditing
    • Find direct SSL configuration editing
    • Detects direct configuration of protocols or cipher suites on SSL objects like SSLSocket, SSLServerSocket, or SSLEngine. This pattern makes SSL/TLS configuration scattered throughout the codebase and prevents centralized security policy management, hindering crypto-agility.
  • io.moderne.cryptography.FindHardcodedAlgorithmChoice
    • Find hardcoded algorithm choices
    • Detects hardcoded algorithm choices in cryptographic operations. Hardcoded algorithms prevent easy migration to stronger or quantum-resistant algorithms when needed. This is a critical crypto-agility issue that makes systems vulnerable to future attacks.
  • io.moderne.cryptography.FindHardcodedAlgorithmParameters
    • Find hardcoded algorithm-specific parameters
    • Detects hardcoded algorithm-specific parameters like RSA public exponents or EC curve parameters. These hardcoded values prevent algorithm agility and may use weak or non-standard parameters that compromise security.
  • io.moderne.cryptography.FindHardcodedCertificate
    • Find hardcoded certificates
    • Detects hardcoded certificates in the code, including certificates that are hardcoded as strings and used to generate X509Certificate instances via CertificateFactory. Hardcoded certificates can lead to security issues when they expire or need to be revoked.
  • io.moderne.cryptography.FindHardcodedCiphersuiteChoice
    • Find hardcoded cipher suite choices
    • Detects hardcoded cipher suite choices used in SSL/TLS configurations. Hardcoded cipher suites prevent easy updates when cipher suites become weak or need to be changed for compliance reasons.
  • io.moderne.cryptography.FindHardcodedKeyLength
    • Find hardcoded cryptographic key lengths
    • Detects hardcoded key lengths used in cryptographic operations like KeyGenerator.init(), KeyPairGenerator.initialize(), RSAKeyGenParameterSpec, and PBEKeySpec. Hardcoded key lengths reduce flexibility and may not meet changing security requirements.
  • io.moderne.cryptography.FindHardcodedPrivateKey
    • Find hardcoded private keys
    • Detects hardcoded private keys in the code, including PEM-encoded keys that flow into KeyFactory.generatePrivate() calls. Hardcoded private keys are a severe security vulnerability as they compromise the entire cryptographic system.
  • io.moderne.cryptography.FindHardcodedProtocolChoice
    • Find hardcoded SSL/TLS protocol choices
    • Detects hardcoded SSL/TLS protocol choices like 'TLSv1.2', 'SSLv3' used in SSLContext.getInstance() and setProtocols() calls. Hardcoded protocols prevent easy updates when protocols become obsolete or insecure.
  • io.moderne.cryptography.FindHardcodedProviderName
    • Find hardcoded cryptographic provider names
    • Detects hardcoded cryptographic provider names (like 'BC', 'SunJCE') used in getInstance() calls. Hardcoding provider names reduces portability and can cause issues when the provider is not available on different systems.
  • io.moderne.cryptography.FindProgrammaticProviderEditing
    • Find programmatic security provider editing
    • Detects programmatic modifications to the Java Security Provider list through Security.addProvider(), insertProviderAt(), or removeProvider() calls. Modifying providers at runtime makes the security configuration unpredictable and prevents crypto-agility by hardcoding provider dependencies.
  • io.moderne.cryptography.FindRSAKeyGenParameters
    • Find RSA key generation parameters
    • Finds RSAKeyGenParameterSpec instantiations and extracts their parameter values into a data table.
  • io.moderne.cryptography.FindSSLContextSetDefault
    • Find SSLContext.setDefault() usage
    • Detects calls to SSLContext.setDefault() which sets the system-wide default SSL context. This is problematic because it affects all SSL/TLS connections in the JVM, potentially overriding security configurations set by other parts of the application or libraries. It also prevents crypto-agility as the configuration becomes global.
  • io.moderne.cryptography.FindSSLSocketParameters
    • Find SSL socket configuration parameters
    • Finds SSLSocket setter method invocations and extracts their parameter values into a data table.
  • io.moderne.cryptography.FindSecurityModifications
    • Find Security class modifications
    • Finds invocations of java.security.Security methods that modify security configuration such as removeProvider, addProvider, insertProviderAt, setProperty, and removeProperty.
  • io.moderne.cryptography.FindSecuritySetProperties
    • Find Security.setProperty(..) calls for certain properties
    • There is a defined set of properties that should not be set using Security.setProperty(..) as they can lead to security vulnerabilities.
  • io.moderne.cryptography.PostQuantumCryptography
    • Post quantum cryptography
    • This recipe searches for instances in code that may be impacted by post quantum cryptography. Applications may need to support larger key sizes, different algorithms, or use crypto agility to handle the migration. The recipe includes detection of hardcoded values that affect behavior in a post-quantum world, programmatic configuration that may prevent algorithm changes, and general cryptographic usage patterns that should be reviewed.

rewrite-devcenter

rewrite-dropwizard

rewrite-elastic

  • io.moderne.elastic.elastic9.ChangeApiNumericFieldType
    • Change numeric field type with conversion
    • Adds conversion methods with null checks for numeric type changes in Elasticsearch 9 API.
  • io.moderne.elastic.elastic9.ChangeApiNumericFieldTypes
    • Change numeric field types for Elasticsearch 9
    • Handles changes between different numeric types (Long to Integer, int to Long...) in Elasticsearch 9 API responses by adding appropriate conversion methods with null checks.
  • io.moderne.elastic.elastic9.MigrateDenseVectorElementType
    • Migrate DenseVectorProperty.elementType from String to DenseVectorElementType enum
    • In Elasticsearch 9, DenseVectorProperty.elementType() returns DenseVectorElementType enum instead of String, and the builder method elementType(String) now accepts the enum type. This recipe handles both builder calls and getter calls.
  • io.moderne.elastic.elastic9.MigrateDenseVectorSimilarity
    • Migrate DenseVectorProperty.similarity from String to DenseVectorSimilarity enum
    • In Elasticsearch 9, DenseVectorProperty.similarity() returns DenseVectorSimilarity enum instead of String, and the builder method similarity(String) now accepts the enum type. This recipe handles both builder calls and getter calls.
  • io.moderne.elastic.elastic9.MigrateMatchedQueries
    • Migrate matchedQueries from List to Map
    • In Elasticsearch Java Client 9.0, Hit.matchedQueries() changed from returning List<String> to Map<String, Double>. This recipe migrates the usage by adding .keySet() for iterations and using new ArrayList<>(result.keySet()) for assignments.
  • io.moderne.elastic.elastic9.MigrateScriptSource
    • Migrate script source from String to Script/ScriptSource
    • Migrates Script.source(String) calls to use ScriptSource.scriptString(String) wrapper in Elasticsearch Java client 9.x.
  • io.moderne.elastic.elastic9.MigrateSpanTermQueryValue
    • Migrate SpanTermQuery.value() from String to FieldValue
    • In Elasticsearch 9, SpanTermQuery.value() returns a FieldValue instead of String. This recipe updates calls to handle the new return type by checking if it's a string and extracting the string value.
  • io.moderne.elastic.elastic9.MigrateToElasticsearch9
    • Migrate from Elasticsearch 8 to 9
    • This recipe performs a comprehensive migration from Elasticsearch 8 to Elasticsearch 9, addressing breaking changes, API removals, deprecations, and required code modifications.
  • io.moderne.elastic.elastic9.RenameApiField
    • Rename Elasticsearch valueBody() methods
    • In Elasticsearch Java Client 9.0, the generic valueBody() method and valueBody(...) builder methods have been replaced with specific getter and setter methods that better reflect the type of data being returned. Similarly, for GetRepositoryResponse, the result field also got altered to repositories.
  • io.moderne.elastic.elastic9.RenameApiFields
    • Rename API fields for Elasticsearch 9
    • Renames various API response fields from valueBody to align with Elasticsearch 9 specifications.
  • io.moderne.elastic.elastic9.UseNamedValueParameters
    • Use NamedValue parameters instead of Map
    • Migrates indicesBoost and dynamicTemplates parameters from Map to NamedValue in Elasticsearch Java client 9.x.

rewrite-hibernate

rewrite-jasperreports

rewrite-java-application-server

rewrite-kafka

  • io.moderne.kafka.MigrateAdminListConsumerGroups
    • Migrate Admin.listConsumerGroups() to listGroups()
    • Migrates the deprecated Admin.listConsumerGroups() method to listGroups() and updates related types for Kafka 4.1 compatibility.
  • io.moderne.kafka.MigrateAlterConfigsToIncrementalAlterConfigs
    • Migrate AdminClient.alterConfigs() to incrementalAlterConfigs()
    • Migrates the removed AdminClient.alterConfigs() method to incrementalAlterConfigs() for Kafka 4.0 compatibility.
  • io.moderne.kafka.MigrateConsumerCommittedToSet
    • Migrate KafkaConsumer.committed(TopicPartition) to committed(Set<TopicPartition>)
    • Migrates from the removed KafkaConsumer.committed(TopicPartition) to committed(Set<TopicPartition>) for Kafka 4.0 compatibility. Converts single TopicPartition arguments to Collections.singleton() calls.
  • io.moderne.kafka.MigrateConsumerGroupStateToGroupState
    • Migrate ConsumerGroupState to GroupState
    • Migrates from the deprecated ConsumerGroupState to GroupState for Kafka 4.0 compatibility. ConsumerGroupState was deprecated in favor of GroupState which supports both consumer groups and share groups.
  • io.moderne.kafka.MigrateConsumerPollToDuration
    • Migrate KafkaConsumer.poll(long) to poll(Duration)
    • Migrates from the deprecated KafkaConsumer.poll(long) to poll(Duration) for Kafka 4.0 compatibility. Converts millisecond timeout values to Duration.ofMillis() calls.
  • io.moderne.kafka.MigrateSendOffsetsToTransaction
    • Migrate deprecated sendOffsetsToTransaction to use ConsumerGroupMetadata
    • Migrates from the deprecated KafkaProducer.sendOffsetsToTransaction(Map, String) to sendOffsetsToTransaction(Map, ConsumerGroupMetadata) for Kafka 4.0 compatibility. This recipe uses a conservative approach with new ConsumerGroupMetadata(groupId).
  • io.moderne.kafka.MigrateToKafka23
    • Migrate to Kafka 2.3
    • Migrate applications to the latest Kafka 2.3 release.
  • io.moderne.kafka.MigrateToKafka24
    • Migrate to Kafka 2.4
    • Migrate applications to the latest Kafka 2.4 release.
  • io.moderne.kafka.MigrateToKafka25
    • Migrate to Kafka 2.5
    • Migrate applications to the latest Kafka 2.5 release.
  • io.moderne.kafka.MigrateToKafka26
    • Migrate to Kafka 2.6
    • Migrate applications to the latest Kafka 2.6 release.
  • io.moderne.kafka.MigrateToKafka27
    • Migrate to Kafka 2.7
    • Migrate applications to the latest Kafka 2.7 release.
  • io.moderne.kafka.MigrateToKafka28
    • Migrate to Kafka 2.8
    • Migrate applications to the latest Kafka 2.8 release.
  • io.moderne.kafka.MigrateToKafka30
    • Migrate to Kafka 3.0
    • Migrate applications to the latest Kafka 3.0 release.
  • io.moderne.kafka.MigrateToKafka31
    • Migrate to Kafka 3.1
    • Migrate applications to the latest Kafka 3.1 release.
  • io.moderne.kafka.MigrateToKafka32
    • Migrate to Kafka 3.2
    • Migrate applications to the latest Kafka 3.2 release.
  • io.moderne.kafka.MigrateToKafka33
    • Migrate to Kafka 3.3
    • Migrate applications to the latest Kafka 3.3 release.
  • io.moderne.kafka.MigrateToKafka40
    • Migrate to Kafka 4.0
    • Migrate applications to the latest Kafka 4.0 release. This includes updating dependencies to 4.0.x, ensuring Java 11+ for clients and Java 17+ for brokers/tools, and handling changes.
  • io.moderne.kafka.MigrateToKafka41
    • Migrate to Kafka 4.1
    • Migrate applications to the latest Kafka 4.1 release. This includes updating dependencies to 4.1.x, migrating deprecated Admin API methods, updating Streams configuration properties, and removing deprecated broker properties.
  • io.moderne.kafka.RemoveDeprecatedKafkaProperties
    • Remove deprecated Kafka property
    • Removes a specific Kafka property that is no longer supported in Kafka 4.0.
  • io.moderne.kafka.UpgradeJavaForKafkaBroker
    • Upgrade Java to 17+ for Kafka broker/tools
    • Ensures Java 17 or higher is used when Kafka broker or tools dependencies are present.
  • io.moderne.kafka.UpgradeJavaForKafkaClients
    • Upgrade Java to 11+ for Kafka clients
    • Ensures Java 11 or higher is used when Kafka client libraries are present.
  • io.moderne.kafka.streams.MigrateJoinedNameMethod
    • Migrate Joined.named() to Joined.as()
    • In Kafka Streams 2.3, Joined.named() was deprecated in favor of Joined.as(). Additionally, the name() method was deprecated for removal and should not be used.
  • io.moderne.kafka.streams.MigrateKStreamToTable
    • Migrate KStream to KTable conversion to use toTable() method
    • In Kafka Streams 2.5, a new toTable() method was added to simplify converting a KStream to a KTable. This recipe replaces the manual aggregation pattern .groupByKey().reduce((oldVal, newVal) -> newVal) with the more concise .toTable() method.
  • io.moderne.kafka.streams.MigrateKafkaStreamsStoreMethod
    • Migrate deprecated KafkaStreams#store method
    • In Kafka Streams 2.5, the method KafkaStreams#store(String storeName, QueryableStoreType<T> storeType) was deprecated. It only allowed querying active stores and did not support any additional query options. Use the new StoreQueryParameters API instead.
  • io.moderne.kafka.streams.MigrateRetryConfiguration
    • Migrate deprecated retry configuration to task timeout
    • In Kafka 2.7, RETRIES_CONFIG and RETRY_BACKOFF_MS_CONFIG were deprecated in favor of TASK_TIMEOUT_MS_CONFIG. This recipe migrates the old retry configuration to the new task timeout configuration, attempting to preserve the retry budget by multiplying retries × backoff time. If only one config is present, it falls back to 60000ms (1 minute).
  • io.moderne.kafka.streams.MigrateStreamsUncaughtExceptionHandler
    • Migrate to StreamsUncaughtExceptionHandler API
    • Migrates from the JVM-level Thread.UncaughtExceptionHandler to Kafka Streams' StreamsUncaughtExceptionHandler API introduced in version 2.8. This new API provides explicit control over how the Streams client should respond to uncaught exceptions (REPLACE_THREAD, SHUTDOWN_CLIENT, or SHUTDOWN_APPLICATION).
  • io.moderne.kafka.streams.MigrateTaskAndThreadMetadata
    • Migrate TaskMetadata and ThreadMetadata
    • Migrates TaskMetadata and ThreadMetadata from org.apache.kafka.streams.processor package to org.apache.kafka.streams package, and updates TaskMetadata.taskId() calls to include .toString() for String compatibility.
  • io.moderne.kafka.streams.MigrateTaskMetadataTaskId
    • Migrate TaskMetadata.taskId() to return TaskId
    • In Kafka Streams 3.0, TaskMetadata.taskId() changed its return type from String to TaskId. This recipe adds .toString() calls where necessary to maintain String compatibility.
  • io.moderne.kafka.streams.MigrateWindowStorePutMethod
    • Migrate WindowStore.put() to include timestamp
    • In Kafka Streams 2.4, WindowStore.put() requires a timestamp parameter. This recipe adds context.timestamp() as the third parameter.
  • io.moderne.kafka.streams.ProcessingGuaranteeExactlyOnceToBeta
    • Migrate exactly_once to exactly_once_beta
    • Kafka Streams 2.6 introduces the exactly-once semantics v2, which is a more efficient implementation with improved internal handling. Though it is beta, it’s fully backward-compatible from the API standpoint, but internally it uses a different transaction/commit protocol. Starting from 3.0, it becomes the default "exactly_once_v2".
  • io.moderne.kafka.streams.ProcessingGuaranteeExactlyOnceToV2
    • Migrate exactly_once and exactly_once_beta to exactly_once_v2
    • Kafka Streams 2.6 introduces the exactly-once semantics v2, which is a more efficient implementation with improved internal handling. Starting from 3.0, it becomes the default "exactly_once_v2".
  • io.moderne.kafka.streams.RemovePartitionGrouperConfiguration
    • Remove PartitionGrouper configuration
    • Starting with Kafka Streams 2.4, the PartitionGrouper API was deprecated and partition grouping is now fully handled internally by the library. This recipe removes the deprecated PARTITION_GROUPER_CLASS_CONFIG configuration.

rewrite-prethink

rewrite-program-analysis

  • io.moderne.recipe.rewrite-program-analysis.InlineDeprecatedMethods
    • Inline deprecated delegating methods
    • Automatically generated recipes to inline deprecated method calls that delegate to other methods in the same class.
  • org.openrewrite.analysis.java.FindNullPointerIssues
    • Find null pointer issues
    • Detects potential null pointer dereferences using path-sensitive analysis to distinguish between definite NPEs, possible NPEs, and safe dereferences.
  • org.openrewrite.analysis.java.controlflow.FindUnusedDefinitions
    • Find unused variable definitions
    • Identifies variable assignments whose values are never used before being overwritten.
  • org.openrewrite.analysis.java.controlflow.search.FindCyclomaticComplexity
    • Find cyclomatic complexity
    • Calculates the cyclomatic complexity of methods and produces a data table containing the class name, method name, argument types, complexity value, and complexity threshold.
  • org.openrewrite.analysis.java.controlflow.search.FindUnreachableCode
    • Find unreachable code
    • Uses control flow analysis to identify statements that can never be executed.
  • org.openrewrite.analysis.java.dataflow.FindDeadStores
    • Find dead stores
    • Identifies variable assignments whose values are never used before being overwritten or going out of scope.
  • org.openrewrite.analysis.java.dataflow.FindUnclosedResources
    • Find unclosed resources (S2095)
    • Identifies resources implementing AutoCloseable/Closeable that are opened but not properly closed on all execution paths. Unclosed resources can lead to resource leaks that degrade application performance and stability.
  • org.openrewrite.analysis.java.datalineage.TrackDataLineage
    • Track data lineage
    • Tracks the flow of data from database sources to API sinks to understand data dependencies and support compliance requirements. ## Prerequisites for detecting a data flow All of the following conditions must be met for the recipe to report a flow: 1. The source code must contain at least one method call matching a recognized source (see below). 2. The source code must contain at least one method call matching a recognized sink (see below). 3. The tainted data must propagate from the source to the sink through variable assignments within the same method or via fields across methods in the same compilation unit. 4. No flow breaker (see below) may appear on the path between source and sink. 5. The relevant library types (e.g., java.sql.ResultSet, javax.ws.rs.core.Response) must be on the classpath so that OpenRewrite can resolve types. If types are unresolved, method matchers will not trigger and no flows will be detected. ## Recognized sources (database reads) | Category | Classes | | --- | --- | | JDBC | java.sql.ResultSet | | JPA (javax) | javax.persistence.EntityManager, Query, TypedQuery | | JPA (jakarta) | jakarta.persistence.EntityManager, Query, TypedQuery | | Hibernate | org.hibernate.Session, org.hibernate.query.Query | | Spring Data | org.springframework.data.repository.CrudRepository | | Spring JDBC | org.springframework.jdbc.core.JdbcTemplate | | MyBatis | org.apache.ibatis.session.SqlSession, org.mybatis.spring.SqlSessionTemplate | | MongoDB | com.mongodb.client.MongoCollection, org.springframework.data.mongodb.core.MongoTemplate | | Redis | redis.clients.jedis.Jedis, org.springframework.data.redis.core.RedisTemplate, ValueOperations, HashOperations | | Cassandra | com.datastax.driver.core.Session, org.springframework.data.cassandra.core.CassandraTemplate | | Elasticsearch | org.elasticsearch.client.RestHighLevelClient, org.springframework.data.elasticsearch.core.ElasticsearchTemplate | | Heuristic | Any class with Repository, Dao, or Mapper in its name calling methods starting with find, get, query, search, load, fetch, or select | ## Recognized sinks (API responses) | Category | Classes | | --- | --- | | JAX-RS (javax) | javax.ws.rs.core.Response, Response.ResponseBuilder | | JAX-RS (jakarta) | jakarta.ws.rs.core.Response, Response.ResponseBuilder | | Spring MVC | org.springframework.http.ResponseEntity, ResponseEntity.BodyBuilder | | Servlet (javax) | javax.servlet.http.HttpServletResponse, javax.servlet.ServletOutputStream | | Servlet (jakarta) | jakarta.servlet.http.HttpServletResponse, jakarta.servlet.ServletOutputStream | | Java I/O | java.io.PrintWriter, java.io.Writer, java.io.OutputStream | | Jackson | com.fasterxml.jackson.databind.ObjectMapper, com.fasterxml.jackson.core.JsonGenerator | | Gson | com.google.gson.Gson, com.google.gson.JsonWriter | | GraphQL | graphql.schema.DataFetcher, graphql.schema.PropertyDataFetcher | | Spring WebFlux | ServerResponse, reactor.core.publisher.Mono, reactor.core.publisher.Flux | | gRPC | io.grpc.stub.StreamObserver | | WebSocket | javax.websocket.Session, RemoteEndpoint.Basic, jakarta.websocket.*, org.springframework.web.socket.WebSocketSession | ## Flow breakers Flows are broken by methods matching common sanitization patterns (anonymize, redact, mask, encrypt, hash, sanitize, etc.) or authorization checks (isAuthorized, hasPermission, hasRole, etc.).
  • org.openrewrite.analysis.java.privacy.FindPiiExposure
    • Find PII exposure in logs and external APIs
    • Detects when Personally Identifiable Information (PII) is exposed through logging statements or sent to external APIs without proper sanitization. This helps prevent data leaks and ensures compliance with privacy regulations like GDPR and CCPA.
  • org.openrewrite.analysis.java.security.FindArrayIndexInjection
    • Find improper validation of array index
    • Detects when user-controlled input flows into array or collection index expressions without proper bounds validation, which could allow out-of-bounds access or denial of service (CWE-129).
  • org.openrewrite.analysis.java.security.FindCommandInjection
    • Find command injection vulnerabilities
    • Detects when user-controlled input flows into system command execution methods like Runtime.exec() or ProcessBuilder, which could allow attackers to execute arbitrary commands.
  • org.openrewrite.analysis.java.security.FindInsecureCryptoComparison
    • Find non-constant-time comparison of cryptographic digests
    • Detects when the output of MessageDigest.digest(..) or Mac.doFinal(..) flows into Arrays.equals(byte[], byte[]), a non-constant-time comparison that is vulnerable to timing attacks (CWE-208). Use MessageDigest.isEqual(byte[], byte[]) for security-sensitive byte-array comparisons.
  • org.openrewrite.analysis.java.security.FindJndiInjection
    • Find JNDI injection vulnerabilities
    • Detects when user-controlled input flows into JNDI lookup operations without proper validation, which could allow an attacker to connect to malicious naming/directory services (CWE-99).
  • org.openrewrite.analysis.java.security.FindLdapInjection
    • Find LDAP injection vulnerabilities
    • Finds LDAP injection vulnerabilities by tracking tainted data flow from user input to LDAP queries.
  • org.openrewrite.analysis.java.security.FindLogInjection
    • Find log injection vulnerabilities
    • Detects when user-controlled input flows into logging methods without sanitization, which could allow attackers to forge log entries by injecting newline characters.
  • org.openrewrite.analysis.java.security.FindPathTraversal
    • Find path traversal vulnerabilities
    • Detects potential path traversal vulnerabilities where user input flows to file system operations without proper validation.
  • org.openrewrite.analysis.java.security.FindProcessControlInjection
    • Find process control vulnerabilities
    • Detects when user-controlled input flows into native library loading methods without proper validation, which could allow an attacker to load arbitrary native code (CWE-114).
  • org.openrewrite.analysis.java.security.FindSecurityVulnerabilities
    • Find security vulnerabilities using taint analysis
    • Identifies potential security vulnerabilities where untrusted data from sources flows to sensitive sinks without proper sanitization.
  • org.openrewrite.analysis.java.security.FindSqlInjection
    • Find SQL injection vulnerabilities
    • Detects potential SQL injection vulnerabilities where user input flows to SQL execution methods without proper sanitization.
  • org.openrewrite.analysis.java.security.FindUnencryptedPiiStorage
    • Find unencrypted PII storage
    • Identifies when personally identifiable information (PII) is stored in databases, files, or other persistent storage without encryption.
  • org.openrewrite.analysis.java.security.FindUnsafeReflectionInjection
    • Find unsafe reflection vulnerabilities
    • Detects when user-controlled input flows into reflection-based class loading or instantiation without proper validation, which could allow an attacker to instantiate arbitrary classes (CWE-470).
  • org.openrewrite.analysis.java.security.FindXssVulnerability
    • Find XSS vulnerabilities
    • Detects potential cross-site scripting vulnerabilities where user input flows to output methods without proper sanitization.
  • org.openrewrite.analysis.java.security.FindXxeVulnerability
    • Find XXE vulnerabilities
    • Locates XML parsers that are not configured to prevent XML External Entity (XXE) attacks.
  • org.openrewrite.analysis.java.security.SanitizeLogInjection
    • Sanitize log injection vulnerabilities
    • Sanitizes user-controlled input before it flows into logging methods by stripping newline, carriage return, and tab characters that could enable log forging.

rewrite-react

rewrite-release-metromap

rewrite-spring

  • io.moderne.java.jsf.MigrateToJsf_2_3
    • Migrate to JSF 2.3
    • Complete migration to JSF 2.3, including associated technologies like RichFaces. Updates dependencies, transforms XHTML views, and migrates Java APIs.
  • io.moderne.java.jsf.richfaces.ConvertExtendedDataTableHeightToStyle
    • Convert height/width attributes to extendedDataTable style
    • Converts height and width attributes to inline style attribute for RichFaces extendedDataTable components.
  • io.moderne.java.jsf.richfaces.MigrateRichFaces_4_5
    • Migrate RichFaces 3.x to 4.5
    • Complete RichFaces 3.x to 4.5 migration including tag renames, attribute migrations, and Java API updates.
  • io.moderne.java.jsf.richfaces.update45.UpdateXHTMLTags
    • Migrate RichFaces tags in xhtml files
    • Migrate RichFaces tags in xhtml files to RichFaces 4.
  • io.moderne.java.spring.boot.AddSpringBootApplication
    • Add @SpringBootApplication class
    • Adds a @SpringBootApplication class containing a main method to bootify your Spring Framework application.
  • io.moderne.java.spring.boot.FieldToConstructorInjection
    • Convert field injection to constructor injection
    • Converts @Autowired field injection to constructor injection pattern. For non-final classes, adds both a no-args constructor and the autowired constructor to maintain compatibility with extending classes. Moves @Qualifier annotations to constructor parameters.
  • io.moderne.java.spring.boot.IsLikelyNotSpringBoot
    • Is likely not a Spring Boot project
    • Marks the project if it's likely not a Spring Boot project.
  • io.moderne.java.spring.boot.IsLikelySpringBoot
    • Is likely a Spring Boot project
    • Marks the project if it's likely a Spring Boot project.
  • io.moderne.java.spring.boot.MarkEmbeddedServerProvidedForWar
    • Mark embedded server as provided for WAR projects
    • For WAR-packaged projects migrating to Spring Boot, add the embedded Tomcat starter with provided scope to prevent conflicts with the external servlet container.
  • io.moderne.java.spring.boot.MigrateSpringFrameworkDependenciesToSpringBoot
    • Migrate Spring Framework dependencies to Spring Boot
    • Migrate Spring Framework dependencies to Spring Boot.
  • io.moderne.java.spring.boot.ReplaceSpringFrameworkDepsWithBootStarters
    • Replace Spring Framework dependencies with Spring Boot starters
    • Replace common Spring Framework dependencies with their Spring Boot starter equivalents. This recipe handles the direct dependency replacement; any remaining Spring Framework dependencies that become transitively available through starters are cleaned up separately by RemoveRedundantDependencies.
  • io.moderne.java.spring.boot.SpringToSpringBoot
    • Migrate Spring Framework to Spring Boot
    • Migrate non Spring Boot applications to the latest compatible Spring Boot release. This recipe will modify an application's build files introducing Maven dependency management for Spring Boot, or adding the Gradle Spring Boot build plugin.
  • io.moderne.java.spring.boot3.AddValidToConfigurationPropertiesFields
    • Add @Valid annotation to fields
    • In Spring Boot 3.4, validation of @ConfigurationProperties classes annotated with @Validated now follows the Bean Validation specification, only cascading to nested properties if the corresponding field is annotated with @Valid. The recipe will add a @Valid annotation to each field which has a type that has a field which is annotated with a jakarta.validation.constraints.* annotation.
  • io.moderne.java.spring.boot3.CommentDeprecations
    • Comment deprecated methods in Spring 3.4
    • Spring Boot 3.4 deprecates methods that are not commonly used or need manual interaction.
  • io.moderne.java.spring.boot3.CommentOnMockAndSpyBeansInConfigSpring34
    • Comment on @MockitoSpyBean and @MockitoBean in @Configuration
    • Deprecated: use io.moderne.java.spring.boot3.ReplaceMockitoBeanWithBeanMethod instead, which rewrites the field into a working @Bean method rather than adding a TODO comment. As stated in Spring Docs @MockitoSpyBean and @MockitoBean will only work in tests, explicitly not in @Configuration annotated classes.
  • io.moderne.java.spring.boot3.ConditionalOnAvailableEndpointMigrationSpring34
    • Migrate ConditionalOnAvailableEndpoint for Spring Boot 3.4
    • Migrate @ConditionalOnAvailableEndpoint(EndpointExposure.CLOUD_FOUNDRY) to @ConditionalOnAvailableEndpoint(EndpointExposure.WEB) for Spring Boot 3.4.
  • io.moderne.java.spring.boot3.MigrateAbstractDiscoveredEndpointConstructor
    • Migrate AbstractDiscoveredEndpoint deprecated constructor
    • The boolean-parameter constructor of AbstractDiscoveredEndpoint has been deprecated in Spring Boot 3.4. This recipe transforms it to use the new constructor with an Access parameter.
  • io.moderne.java.spring.boot3.MigrateAbstractExposableEndpointConstructor
    • Migrate AbstractExposableEndpoint deprecated constructor
    • The boolean-parameter constructor of AbstractExposableEndpoint has been deprecated in Spring Boot 3.4. This recipe transforms it to use the new constructor with an Access parameter instead of boolean enableByDefault.
  • io.moderne.java.spring.boot3.MigrateEndpointAnnotationAccessValueSpring34
    • Migrate @Endpoints defaultAccess value
    • Since Spring Boot 3.4 the @Endpoint access configuration values are no longer true|false but none|read-only|unrestricted.
  • io.moderne.java.spring.boot3.MigrateEndpointDiscovererConstructor
    • Migrate EndpointDiscoverer deprecated constructor
    • The 4-parameter constructor of EndpointDiscoverer has been deprecated in Spring Boot 3.4. This recipe transforms it to use the new 5-parameter constructor with an additional Collection parameter.
  • io.moderne.java.spring.boot3.MigrateEntityManagerFactoryBuilderConstructor
    • Migrate EntityManagerFactoryBuilder deprecated constructor
    • The constructors of EntityManagerFactoryBuilder have been deprecated in Spring Boot 3.4. This recipe transforms them to use the new constructor with a Function parameter for property mapping.
  • io.moderne.java.spring.boot3.MigrateJmxEndpointDiscovererConstructor
    • Migrate JmxEndpointDiscoverer deprecated constructor
    • The 4-parameter constructor of JmxEndpointDiscoverer has been deprecated in Spring Boot 3.4. This recipe transforms it to use the new 5-parameter constructor with an additional Collection parameter.
  • io.moderne.java.spring.boot3.MigrateRestTemplateToRestClient
    • Migrate RestTemplate to RestClient
    • Migrates Spring's RestTemplate to the modern RestClient API introduced in Spring Framework 6.1. RestClient provides a fluent, synchronous API that is the recommended approach for new development. This recipe converts constructor calls, type declarations, and common method invocations (getForObject, getForEntity, postForObject, postForEntity, patchForObject, put, delete, headForHeaders, postForLocation, optionsForAllow, exchange) to their RestClient equivalents.
  • io.moderne.java.spring.boot3.MigrateWebEndpointDiscovererConstructor
    • Migrate WebEndpointDiscoverer 6-parameter constructor to 8-parameter
    • The 6-parameter constructor of WebEndpointDiscoverer has been deprecated in Spring Boot 3.3. This recipe adds two new parameters (AdditionalPathsMapper and OperationFilter<WebOperation>) to the constructor and updates the Bean method signature to inject them as ObjectProvider types.
  • io.moderne.java.spring.boot3.RemoveDeprecatedConditions
    • Remove Spring Boot 3.5 deprecated conditions
    • Replace Spring Boot 3.5 deprecated condition classes with their corresponding conditional annotations.
  • io.moderne.java.spring.boot3.RemoveReplaceNoneFromAutoConfigureTestDatabase
    • Remove Replace.NONE from @AutoConfigureTestDatabase
    • Replace.NONE is the default value for @AutoConfigureTestDatabase since Spring Boot 3.4.
  • io.moderne.java.spring.boot3.RemoveTestRestTemplateEnableRedirectsOptionRecipe
    • Remove TestRestTemplate.HttpClientOption.ENABLE_REDIRECTS option
    • The TestRestTemplate now uses the same follow redirects settings as the regular RestTemplate. The HttpOption.ENABLE_REDIRECTS option has also been deprecated. This recipe removes the option from the TestRestTemplate constructor arguments.
  • io.moderne.java.spring.boot3.ReplaceConditionalOutcomeInverse
    • Replace ConditionOutcome.inverse() with constructor
    • Replace deprecated ConditionOutcome.inverse(ConditionOutcome outcome) calls with new ConditionOutcome(!outcome.isMatch(), outcome.getConditionMessage()).
  • io.moderne.java.spring.boot3.ReplaceDeprecatedKafkaConnectionDetailsBootstrapServerGetters
    • Replace deprecated KafkaConnectionDetails bootstrap server methods
    • Replace deprecated KafkaConnectionDetails bootstrap server methods with chained calls. For example, getProducerBootstrapServers() becomes getProducer().getBootstrapServers().
  • io.moderne.java.spring.boot3.ReplaceDeprecatedThreadPoolTaskSchedulerConstructor
    • Replace deprecated ThreadPoolTaskSchedulerBuilder 5-argument constructor
    • The 5-parameter constructor of ThreadPoolTaskSchedulerBuilder has been deprecated in Spring Boot 3.5. This recipe transforms it to use the builder pattern instead, omitting null values and defaults.
  • io.moderne.java.spring.boot3.ReplaceKafkaTransactionManagerSetter
    • Use kafkaAwareTransactionManager setter
    • Replace deprecated ContainerProperties#setTransactionManager(org.springframework.transaction.PlatformTransactionManager) method with ContainerProperties#setKafkaAwareTransactionManager(org.springframework.kafka.transaction.KafkaAwareTransactionManager). The method will be replaced only if its argument has the type KafkaAwareTransactionManager.
  • io.moderne.java.spring.boot3.ReplaceMockitoBeanWithBeanMethod
    • Replace @MockitoBean and @MockitoSpyBean with @Bean methods in @Configuration classes
    • @MockitoBean and @MockitoSpyBean only work in test classes, not in @Configuration classes. This recipe converts annotated fields into @Bean methods using Mockito.mock() or Mockito.spy().
  • io.moderne.java.spring.boot3.ReplaceTaskExecutorNameByApplicationTaskExecutorName
    • Use bean name applicationTaskExecutor instead of taskExecutor
    • Spring Boot 3.5 removed the bean name taskExecutor. Where this bean name is used, the recipe replaces the bean name to applicationTaskExecutor. This also includes instances where the developer provided their own bean named taskExecutor. This also includes scenarios where JSR-250's @Resource annotation is used.
  • io.moderne.java.spring.boot3.ResolveDeprecationsSpringBoot_3_3
    • Resolve Deprecations in Spring Boot 3.3
    • Migrates Deprecations in the Spring Boot 3.3 Release. Contains the removal of DefaultJmsListenerContainerFactoryConfigurer.setObservationRegistry and adds new parameter of WebEndpointDiscoverer constructor.
  • io.moderne.java.spring.boot3.ResolveTaskExecutorFromContext
    • Replace taskExecutor with applicationTaskExecutor
    • Use bean name applicationTaskExecutor instead of taskExecutor when resolving TaskExecutor Bean from application context.
  • io.moderne.java.spring.boot3.SpringBoot34Deprecations
    • Migrate Spring Boot 3.4 deprecated classes and methods
    • Migrate deprecated classes and methods that have been marked for removal in Spring Boot 4.0. This includes constructor changes for EntityManagerFactoryBuilder, HikariCheckpointRestoreLifecycle, and various actuator endpoint discovery classes.
  • io.moderne.java.spring.boot3.SpringBoot35Deprecations
    • Migrate Spring Boot 3.5 deprecated classes and methods
    • Migrate deprecated classes and methods that have been marked for removal in Spring Boot 3.5.
  • io.moderne.java.spring.boot3.SpringBoot3BestPractices
    • Spring Boot 3.5 best practices
    • Applies best practices to Spring Boot 3.5+ applications.
  • io.moderne.java.spring.boot3.SpringBootProperties_3_4
    • Migrate @Endpoint Security properties to 3.4 (Moderne Edition)
    • Migrate the settings for Spring Boot Management Endpoint Security from true|false to read-only|none.
  • io.moderne.java.spring.boot3.UpdateOpenTelemetryResourceAttributes
    • Update OpenTelemetry resource attributes
    • The service.group resource attribute has been deprecated for OpenTelemetry in Spring Boot 3.5. Consider using alternative attributes or remove the deprecated attribute.
  • io.moderne.java.spring.boot3.UpgradeGradle7Spring34
    • Upgrade Gradle to 7.6.4+ for Spring Boot 3.4
    • Spring Boot 3.4 requires Gradle 7.6.4.
  • io.moderne.java.spring.boot3.UpgradeGradle8Spring34
    • Upgrade Gradle 8 to 8.4+ for Spring Boot 3.4
    • Spring Boot 3.4 requires Gradle 8.4+.
  • io.moderne.java.spring.boot3.UpgradeMyBatisToSpringBoot_3_4
    • Upgrade MyBatis to Spring Boot 3.4
    • Upgrade MyBatis Spring modules to a version corresponding to Spring Boot 3.4.
  • io.moderne.java.spring.boot3.UpgradeMyBatisToSpringBoot_3_5
    • Upgrade MyBatis to Spring Boot 3.5
    • Upgrade MyBatis Spring modules to a version corresponding to Spring Boot 3.5.
  • io.moderne.java.spring.boot3.UpgradeSpringBoot_3_4
    • Migrate to Spring Boot 3.4 (Moderne Edition)
    • Migrate applications to the latest Spring Boot 3.4 release. This recipe will modify an application's build files, make changes to deprecated/preferred APIs, and migrate configuration settings that have changes between versions. This recipe will also chain additional framework migrations (Spring Framework, Spring Data, etc) that are required as part of the migration to Spring Boot 3.4.
  • io.moderne.java.spring.boot3.UpgradeSpringBoot_3_5
    • Migrate to Spring Boot 3.5 (Moderne Edition)
    • Migrate applications to the latest Spring Boot 3.5 release. This recipe will modify an application's build files, make changes to deprecated/preferred APIs, and migrate configuration settings that have changes between versions. This recipe will also chain additional framework migrations (Spring Framework, Spring Data, etc) that are required as part of the migration to Spring Boot 3.5.
  • io.moderne.java.spring.boot3.UpgradeSpringCloudAWSToSpringBoot_3_4
    • Upgrade Spring Cloud AWS to Spring Boot 3.4 compatible version
    • Upgrade the Spring Cloud AWS dependency to a version compatible with Spring Boot 3.4.
  • io.moderne.java.spring.boot3.UpgradeSpringKafka_3_3
    • Migrate to Spring Kafka 3.3
    • Migrate applications to the latest Spring Kafka 3.3 release.
  • io.moderne.java.spring.boot4.AddAutoConfigureMockMvc
    • Add @AutoConfigureMockMvc to @SpringBootTest classes using MockMvc
    • Adds @AutoConfigureMockMvc annotation to classes annotated with @SpringBootTest that use MockMvc.
  • io.moderne.java.spring.boot4.AddFlywayStarters
    • Add Flyway starters
    • Adds spring-boot-starter-flyway and spring-boot-starter-flyway-test dependencies when Flyway usage is detected in the module.
  • io.moderne.java.spring.boot4.AddJackson2ForJerseyJson
    • Add Jackson2 for Jersey using JSON
    • Check whether a module uses Jersey on combination with JSON and adds the needed spring-boot-jackson dependency and conditionally spring-boot-jackson2 dependency.
  • io.moderne.java.spring.boot4.AddLenientMockitoSettings
    • Add @MockitoSettings(strictness = Strictness.LENIENT) for @MockitoBean tests
    • When migrating from @MockBean to @MockitoBean, the implicit LENIENT Mockito strictness from Spring Boot's MockitoPostProcessor is lost. If @ExtendWith(MockitoExtension.class) is present, Mockito enforces STRICT_STUBS by default, causing UnnecessaryStubbingException for tests with unused stubs. This recipe adds @MockitoSettings(strictness = Strictness.LENIENT) to preserve the original behavior.
  • io.moderne.java.spring.boot4.AddLiquibaseStarters
    • Add Liquibase starters
    • Adds spring-boot-starter-liquibase and spring-boot-starter-liquibase-test dependencies when Liquibase usage is detected in the module.
  • io.moderne.java.spring.boot4.AddModularStarters
    • Add Spring Boot 4.0 modular starters
    • Add Spring Boot 4.0 starter dependencies based on package usage. Note: Higher-level starters (like data-jpa) include lower-level ones (like jdbc) transitively, so only the highest-level detected starter is added for each technology.
  • io.moderne.java.spring.boot4.AddMongoDbRepresentationProperties
    • Add MongoDB representation properties for UUID and BigDecimal
    • Adds the 'spring.mongodb.representation.uuid' property with value 'standard' and the 'spring.data.mongodb.representation.big-decimal' property with the value 'decimal128' to Spring configuration files when a MongoDB dependency is detected.
  • io.moderne.java.spring.boot4.AddMssqlKerberosJaasConfig
    • Add useDefaultJaasConfig=true to MSSQL Kerberos JDBC URLs
    • For MSSQL JDBC connections using Kerberos authentication (authenticationScheme=JavaKerberos or integratedSecurity=true), adds useDefaultJaasConfig=true to the connection string. This is required for compatibility with Keycloak 26.4+ which changes JAAS configuration handling.
  • io.moderne.java.spring.boot4.AddValidationStarterDependency
    • Add spring-boot-starter-validation dependency
    • In Spring Boot 4, validation is no longer auto-included from the web starter. This recipe adds the spring-boot-starter-validation dependency when Jakarta Validation annotations are used in the project.
  • io.moderne.java.spring.boot4.AddWithHttpClientDefaultsToReactorBuilders
    • Preserve system-proxy defaults on Reactor HTTP client builders
    • Spring Boot 4.1 no longer applies proxyWithSystemProperties() by default on ReactorClientHttpRequestFactoryBuilder and ReactorClientHttpConnectorBuilder. This recipe appends .withHttpClientDefaults() to chains starting at ClientHttpRequestFactoryBuilder.reactor() or ClientHttpConnectorBuilder.reactor() to restore the previous behavior. Chains that already call withHttpClientDefaults(..) or proxyWithSystemProperties(..) are left untouched.
  • io.moderne.java.spring.boot4.AdoptJackson3
    • Adopt Jackson 3
    • Adopt Jackson 3 which is supported by Spring Boot 4 and Jackson 2 support is deprecated.
  • io.moderne.java.spring.boot4.FlagDeprecatedReactorNettyHttpClientMapper
    • Flag deprecated ReactorNettyHttpClientMapper for migration
    • Adds a TODO comment to classes implementing the deprecated ReactorNettyHttpClientMapper interface. Migration to ClientHttpConnectorBuilderCustomizer<ReactorClientHttpConnectorBuilder> requires wrapping the HttpClient configuration in builder.withHttpClientCustomizer(...).
  • io.moderne.java.spring.boot4.InsertPropertyMapperAlwaysMethodInvocation
    • Preserve PropertyMapper null-passing behavior
    • Spring Boot 4.0 changes the PropertyMapper behavior so that from() no longer calls to() when the source value is null. This recipe inserts .always() before terminal mapping methods to preserve the previous behavior. Chains that already contain .whenNonNull() or .alwaysApplyingWhenNonNull() are skipped, as they explicitly opted into null-skipping behavior which is now the default.
  • io.moderne.java.spring.boot4.MigrateAutoConfigureMockMvcHtmlUnit
    • Migrate @AutoConfigureMockMvc HtmlUnit attributes to nested @HtmlUnit
    • Spring Boot 4 moved webClientEnabled and webDriverEnabled on @AutoConfigureMockMvc under a nested @HtmlUnit annotation as webClient and webDriver, and relocated the annotation to org.springframework.boot.webmvc.test.autoconfigure. This recipe rewrites the attribute syntax and relocates the annotation in one step, so it must run before any package-relocation recipe touches @AutoConfigureMockMvc.
  • io.moderne.java.spring.boot4.MigrateHazelcastSpringSession
    • Migrate Spring Session Hazelcast to Hazelcast Spring Session
    • Spring Boot 4.0 removed direct support for Spring Session Hazelcast. The Hazelcast team now maintains their own Spring Session integration. This recipe changes the dependency from org.springframework.session:spring-session-hazelcast to com.hazelcast.spring:hazelcast-spring-session and updates the package from org.springframework.session.hazelcast to com.hazelcast.spring.session.
  • io.moderne.java.spring.boot4.MigrateJsonFactoryDecorator
    • Migrate JsonFactoryDecorator to TokenStreamFactoryBuilderDecorator
    • Migrates classes that implement net.logstash.logback.decorate.JsonFactoryDecorator (removed in logstash-logback-encoder 9.0) to implement TokenStreamFactoryBuilderDecorator<JsonFactory, JsonFactoryBuilder>. The decorate(JsonFactory) method is rewritten to take and return a JsonFactoryBuilder, and mutator calls inside the body (e.g. setCharacterEscapes) are folded into the equivalent builder calls (e.g. characterEscapes) where a mapping is known. Unmapped mutators are kept by name with a TODO comment for manual review.
  • io.moderne.java.spring.boot4.MigrateLayertoolsToTools_4_1
    • Migrate layertools jarmode to tools
    • The layertools jar mode was deprecated in Spring Boot 3.3 and removed in Spring Boot 4.1. Replace -Djarmode=layertools invocations (commonly found in Dockerfiles and shell scripts) with -Djarmode=tools, which provides equivalent and expanded functionality.
  • io.moderne.java.spring.boot4.MigrateMockMvcToAssertJ
    • Migrate MockMvc to AssertJ assertions
    • Migrates Spring MockMvc tests from Hamcrest-style andExpect() assertions to AssertJ-style fluent assertions. Changes MockMvc to MockMvcTester and converts assertion chains.
  • io.moderne.java.spring.boot4.MigratePropertyMapper
    • Migrate PropertyMapper API for Spring Boot 4.0
    • Migrates PropertyMapper usage to accommodate Spring Boot 4.0 behavioral changes. In Boot 4.0, PropertyMapper.from() no longer calls to() when the source value is null. This recipe first inserts .always() on bare chains to preserve null-passing behavior, then removes the now-redundant .whenNonNull() and .alwaysApplyingWhenNonNull() calls. Guarded by a Spring Boot < 4.0 precondition so that on subsequent recipe cycles (after the version is bumped by the parent migration recipe), this recipe becomes a no-op — preventing it from incorrectly adding .always() to chains that just had .whenNonNull() stripped.
  • io.moderne.java.spring.boot4.MigrateRestAssured
    • Add explicit version for REST Assured
    • REST Assured is no longer managed by Spring Boot 4.0. This recipe adds an explicit version to REST Assured dependencies.
  • io.moderne.java.spring.boot4.MigrateSpringRetry
    • Migrate Spring Retry to Spring Resilience
    • Handle spring-retry no longer managed by Spring Boot and the possible migration to Spring Core Resilience.
  • io.moderne.java.spring.boot4.MigrateSpringRetryToSpringFramework7
    • Migrate spring-retry to Spring Framework resilience
    • Migrate spring-retrys @Retryable and @Backoff annotation to Spring Framework 7 Resilience annotations.
  • io.moderne.java.spring.boot4.MigrateToModularStarters
    • Migrate to Spring Boot 4.0 modular starters (Moderne Edition)
    • Adds Spring Boot 4.0 modular starter dependencies based on package usage and rewrites the classic starters to the minimal spring-boot-starter / spring-boot-starter-test. The minimal starter is retained so that modules whose code only references core Spring annotations (e.g. @SpringBootApplication, @Configuration, @Component) still compile after migration.
  • io.moderne.java.spring.boot4.MigrateToModularStarters_4_1
    • Migrate to Spring Boot 4.1 modular starters
    • Add Spring Boot 4.1 starter dependencies for modules introduced in 4.1 (gRPC server, gRPC client, and Spring Batch with MongoDB support). This recipe complements MigrateToModularStarters from 4.0 and only adds the new starters; it does not rewrite or remove anything else.
  • io.moderne.java.spring.boot4.MockMvcAssertionsToAssertJ
    • Migrate MockMvc andExpect() chains to AssertJ assertions
    • Converts MockMvc Hamcrest-style andExpect() assertion chains to AssertJ-style fluent assertions using assertThat(). Handles status, content, JSON path, header, redirect, and forward assertions.
  • io.moderne.java.spring.boot4.MockMvcRequestBuildersToMockMvcTester
    • Migrate MockMvcRequestBuilders to MockMvcTester request methods
    • Converts mockMvcTester.perform(get(&quot;/api&quot;).param(&quot;k&quot;,&quot;v&quot;)) to mockMvcTester.get().uri(&quot;/api&quot;).param(&quot;k&quot;,&quot;v&quot;), removing the perform() wrapper and MockMvcRequestBuilders static method calls.
  • io.moderne.java.spring.boot4.MockMvcToMockMvcTester
    • Migrate MockMvc to MockMvcTester
    • Converts MockMvc fields and initialization to MockMvcTester. Changes field types, renames fields from mockMvc to mockMvcTester, and converts MockMvcBuilders.standaloneSetup().build() to MockMvcTester.of() and MockMvcBuilders.webAppContextSetup().build() to MockMvcTester.from().
  • io.moderne.java.spring.boot4.ModuleHasMonolithicStarter
    • Module has monolithic Spring Boot starter
    • Precondition that matches modules with the monolithic Spring Boot starters that need to be migrated to modular starters. Matches the production monolithic spring-boot-starter and spring-boot-starter-classic, but not specific modular starters like spring-boot-starter-test or spring-boot-starter-ldap.
  • io.moderne.java.spring.boot4.ModuleStarterRelocations
    • Spring Boot 4.0 Module Starter Relocations
    • Relocate types and packages for Spring Boot 4.0 modular starters.
  • io.moderne.java.spring.boot4.ModuleUsesFlyway
    • Module uses Flyway
    • Precondition that marks all files in a module if Flyway usage is detected. Detection is based on having a Flyway dependency, using Flyway types, or having migration files.
  • io.moderne.java.spring.boot4.ModuleUsesLiquibase
    • Module uses Liquibase
    • Precondition that marks all files in a module if Liquibase usage is detected. Detection is based on having a Liquibase dependency, using Liquibase types, or having changelog files.
  • io.moderne.java.spring.boot4.RemoveContentNegotiationFavorPathExtension
    • Remove ContentNegotiationConfigurer.favorPathExtension() calls
    • Spring Framework 7 removed favorPathExtension() from ContentNegotiationConfigurer. Path extension content negotiation is no longer supported. This recipe removes calls to favorPathExtension().
  • io.moderne.java.spring.boot4.RemoveDevtoolsLiveReloadProperties_4_1
    • Comment out deprecated DevTools LiveReload properties
    • Spring Boot 4.1.0-M3 deprecated the LiveReload feature in DevTools with no replacement. The feature still functions in 4.1, so this recipe comments out spring.devtools.livereload.* properties (rather than deleting them) to flag the deprecation while leaving the values recoverable.
  • io.moderne.java.spring.boot4.RemoveGradleUberJarLoaderImplementationConfig
    • Remove loaderImplementation from Gradle
    • Removes the Spring Boot Uber-Jar loaderImplementation configuration from Gradle build files.
  • io.moderne.java.spring.boot4.RemoveHttpMessageConvertersAutoConfigurationReferences
    • Remove HttpMessageConvertersAutoConfiguration references
    • Removes references to the deprecated HttpMessageConvertersAutoConfiguration class which was removed in Spring Boot 4.0. For @AutoConfigureAfter and @AutoConfigureBefore annotations, the reference is removed. For @Import annotations, a TODO comment is added since manual migration may be required.
  • io.moderne.java.spring.boot4.RemoveKafkaPropertiesSslBundlesParameter
    • Remove SslBundles parameter from KafkaProperties build methods
    • In Spring Boot 4.0, the SslBundles parameter was removed from KafkaProperties.buildProducerProperties, buildConsumerProperties, buildAdminProperties, and buildStreamsProperties. This recipe removes the argument from method calls.
  • io.moderne.java.spring.boot4.RemoveSpringPulsarReactive
    • Remove Spring Pulsar Reactive support
    • Spring Boot 4.0 removed support for Spring Pulsar Reactive as it is no longer maintained. This recipe removes the Spring Pulsar Reactive dependencies.
  • io.moderne.java.spring.boot4.RemoveZipkinAutoConfigurationExclude
    • Remove ZipkinAutoConfiguration
    • Zipkin is no longer auto-configured by default in Spring Boot 4.0; remove references to it from exclusions on annotations.
  • io.moderne.java.spring.boot4.ReplaceDeprecatedAutoconfigureMongoApi
    • Replace deprecated org.springframework.boot.autoconfigure.mongo API
    • Replace deprecated org.springframework.boot.autoconfigure.mongo API.
  • io.moderne.java.spring.boot4.ReplaceDeprecatedDockerApi
    • Replace deprecated DockerApi
    • Replaces deprecated DockerApi constructors and configuration methods with their modern equivalents.
  • io.moderne.java.spring.boot4.ReplaceDeprecatedRequestMatcherProvider
    • Replace deprecated RequestMatcherProvider with new API
    • Replaces the deprecated org.springframework.boot.autoconfigure.security.servlet.RequestMatcherProvider with org.springframework.boot.security.autoconfigure.actuate.web.servlet.RequestMatcherProvider. The new interface adds an HttpMethod parameter to the getRequestMatcher method.
  • io.moderne.java.spring.boot4.ReplaceDeprecatedThreadPoolTaskSchedulerBuilderApi
    • Replace deprecated ThreadPoolTaskSchedulerBuilder constructor
    • Replaces the deprecated 5-argument constructor of ThreadPoolTaskSchedulerBuilder with the builder pattern.
  • io.moderne.java.spring.boot4.SimplifyOptionalConfigurationPropertiesNullChecks
    • Simplify null checks on Optional @ConfigurationProperties parameters
    • Spring Boot 4.1 changes constructor-bound @ConfigurationProperties so that Optional&lt;T&gt; parameters bind to Optional.empty() rather than null. This recipe replaces == null / != null checks against such parameters (or same-named fields in the binding constructor's class) with the constant they will always evaluate to, then runs SimplifyConstantIfBranchExecution to remove the dead branches.
  • io.moderne.java.spring.boot4.SpringBoot4BestPractices
    • Spring Boot 4.0 best practices
    • Applies best practices to Spring Boot 4.+ applications.
  • io.moderne.java.spring.boot4.SpringBootProperties_4_1
    • Migrate Spring Boot properties to 4.1
    • Migrate properties found in application.properties and application.yml.
  • io.moderne.java.spring.boot4.UpgradeMyBatisToSpringBoot_4_0
    • Upgrade MyBatis to Spring Boot 4.0
    • Upgrade MyBatis Spring modules to a version corresponding to Spring Boot 4.0.
  • io.moderne.java.spring.boot4.UpgradeSpringBoot_4_0
    • Migrate to Spring Boot 4.0 (Moderne Edition)
    • Migrate applications to the latest Spring Boot 4.0 release. This recipe will modify an application's build files, make changes to deprecated/preferred APIs, and migrate configuration settings that have changes between versions. This recipe will also chain additional framework migrations (Spring Framework, Spring Data, etc) that are required as part of the migration to Spring Boot 4.0.
  • io.moderne.java.spring.boot4.UpgradeSpringBoot_4_1
    • Migrate to Spring Boot 4.1
    • Migrate applications to the latest Spring Boot 4.1 release. This recipe will modify an application's build files, make changes to deprecated/preferred APIs, and migrate configuration settings that have changes between versions. This recipe will also chain additional framework migrations (Spring Framework, Spring Data, etc) that are required as part of the migration to Spring Boot 4.1.
  • io.moderne.java.spring.boot4.UpgradeSpringKafka_4_0
    • Migrate to Spring Kafka 4.0
    • Migrate applications to Spring Kafka 4.0. This includes removing deprecated configuration options that are no longer supported.
  • io.moderne.java.spring.cloud2020.SpringCloudProperties_2020
    • Migrate Spring Cloud properties to 2020
    • Migrate properties found in application.properties and application.yml.
  • io.moderne.java.spring.cloud2021.SpringCloudProperties_2021
    • Migrate Spring Cloud properties to 2021
    • Migrate properties found in application.properties and application.yml.
  • io.moderne.java.spring.cloud2022.SpringCloudProperties_2022
    • Migrate Spring Cloud properties to 2022
    • Migrate properties found in application.properties and application.yml.
  • io.moderne.java.spring.cloud2023.SpringCloudProperties_2023
    • Migrate Spring Cloud properties to 2023
    • Migrate properties found in application.properties and application.yml.
  • io.moderne.java.spring.cloud2024.SpringCloudProperties_2024
    • Migrate Spring Cloud properties to 2024
    • Migrate properties found in application.properties and application.yml.
  • io.moderne.java.spring.cloud2025.SpringCloudProperties_2025
    • Migrate Spring Cloud properties to 2025
    • Migrate properties found in application.properties and application.yml.
  • io.moderne.java.spring.cloud20251.SpringCloudProperties_2025_1
    • Migrate Spring Cloud properties to 2025.1
    • Migrate properties found in application.properties and application.yml for Spring Cloud 2025.1 (Oakwood). This includes the stubrunner property prefix migration from stubrunner. to spring.cloud.contract.stubrunner..
  • io.moderne.java.spring.cloud20251.UpgradeSpringCloud_2025_1
    • Upgrade to Spring Cloud 2025.1
    • Upgrade to Spring Cloud 2025.1 (Oakwood). This release is based on Spring Framework 7 and Spring Boot 4. Each Spring Cloud project has been updated to version 5.0.0.
  • io.moderne.java.spring.framework.AddSetUseSuffixPatternMatch
    • Add setUseSuffixPatternMatch(true) in Spring MVC configuration
    • In Spring Framework 5.2.4 and earlier, suffix pattern matching was enabled by default. This meant a controller method mapped to /users would also match /users.json, /users.xml, etc. Spring Framework 5.3 deprecated this behavior and changed the default to false. This recipe adds setUseSuffixPatternMatch(true) to WebMvcConfigurer implementations to preserve the legacy behavior during migration. Note: This only applies to Spring MVC; Spring WebFlux does not support suffix pattern matching.
  • io.moderne.java.spring.framework.AddSetUseSuffixPatternMatchIfPreSpring53
    • Add setUseSuffixPatternMatch(true) for pre-Spring Framework 5.3 projects
    • Only adds setUseSuffixPatternMatch(true) when the project is on Spring Framework < 5.3, where suffix pattern matching was enabled by default. Projects already on 5.3+ have been running with the new default (false) and should not get this configuration added.
  • io.moderne.java.spring.framework.FindDeprecatedPathMatcherUsage
    • Find deprecated PathMatcher usage
    • In Spring Framework 7.0, PathMatcher and AntPathMatcher are deprecated in favor of PathPatternParser. This recipe finds usages of the deprecated AntPathMatcher class that may require manual migration to PathPatternParser.
  • io.moderne.java.spring.framework.FlagSuffixPatternMatchUsage
    • Flag deprecated suffix pattern matching usage for manual review
    • Handles deprecated setUseSuffixPatternMatch() and setUseRegisteredSuffixPatternMatch() calls. When suffix pattern matching is explicitly enabled, adds TODO comments and search markers since there is no automatic migration path. When explicitly disabled, the call is safely removed since false is already the default since Spring Framework 5.3.
  • io.moderne.java.spring.framework.IsLikelySpringFramework
    • Is likely a Spring Framework project
    • Marks the project if it's likely a Spring Framework project.
  • io.moderne.java.spring.framework.JaxRsToSpringWeb
    • Convert JAX-RS annotations to Spring Web
    • Converts JAX-RS annotations such as @Path, @GET, @POST, etc., to their Spring Web equivalents like @RestController, @RequestMapping, @GetMapping, etc.
  • io.moderne.java.spring.framework.MigrateConverterSetObjectMapper
    • Replace setObjectMapper with constructor injection
    • Folds setObjectMapper calls on MappingJackson2HttpMessageConverter into the constructor. If the converter is instantiated in the same block with no other invocations, the setter call is replaced with constructor injection. Otherwise, a TODO comment is added.
  • io.moderne.java.spring.framework.MigrateDefaultResponseErrorHandler
    • Migrate DefaultResponseErrorHandler.handleError method signature
    • Migrates overridden handleError(ClientHttpResponse response) methods to the new signature handleError(URI url, HttpMethod method, ClientHttpResponse response) in classes extending DefaultResponseErrorHandler. The old single-argument method was removed in Spring Framework 7.0.
  • io.moderne.java.spring.framework.MigrateDeprecatedBeanXmlProperties
    • Migrate Bean XML properties deprecated in Spring Framework 3.0
    • Migrate Bean XML properties that were deprecated in Spring Framework 3.0.
  • io.moderne.java.spring.framework.MigrateFilterToOncePerRequestFilter
    • Migrate Filter to OncePerRequestFilter
    • Migrates classes that implement javax.servlet.Filter (or jakarta.servlet.Filter) to extend org.springframework.web.filter.OncePerRequestFilter. This transformation renames doFilter to doFilterInternal, changes parameter types to HTTP variants, removes manual casting, and removes empty init() and destroy() methods.
  • io.moderne.java.spring.framework.MigrateHandleErrorMethodInvocations
    • Migrate handleError method invocations to new signature
    • Updates invocations of handleError(ClientHttpResponse) to the new handleError(URI, HttpMethod, ClientHttpResponse) signature introduced in Spring Framework 7.0. In test sources, example values are used. In main sources, null is passed with a TODO comment.
  • io.moderne.java.spring.framework.MigrateHttpHeadersMultiValueMapMethods
    • Migrate HttpHeaders methods removed when MultiValueMap contract was dropped
    • Spring Framework 7.0 changed HttpHeaders to no longer implement MultiValueMap. This recipe replaces removed inherited method calls: containsKey() with containsHeader(), keySet() with headerNames(), and entrySet() with headerSet().
  • io.moderne.java.spring.framework.MigrateTrailingSlashMatch
    • Migrate trailing slash matching to explicit routes
    • Migrates deprecated setUseTrailingSlashMatch() configuration removed in Spring Framework 7.0. Only adds explicit trailing slash routes when the project previously enabled trailing slash matching via setUseTrailingSlashMatch(true). The deprecated configuration calls are always removed.
  • io.moderne.java.spring.framework.ModularSpringFrameworkDependencies
    • Add Spring Framework modular dependencies
    • Adds Spring Framework modular dependencies based on package usage, replacing legacy monolithic org.springframework:spring.
  • io.moderne.java.spring.framework.NullableSpringWebParameters
    • Add @Nullable to optional Spring web parameters
    • In Spring Boot 4, JSpecify's @Nullable annotation should be used to indicate that a parameter can be null. This recipe adds @Nullable to parameters annotated with @PathVariable(required = false) or @RequestParam(required = false) and removes the now-redundant required = false attribute.
  • io.moderne.java.spring.framework.RemoveDeprecatedPathMappingOptions
    • Migrate deprecated path mapping options
    • Migrates deprecated path mapping configuration options that have been removed in Spring Framework 7.0. For trailing slash matching, this recipe adds explicit dual routes to controller methods before removing the deprecated configuration. For suffix pattern matching, this recipe flags usages for manual review since there is no automatic migration path. Path extension content negotiation options are removed as they should be replaced with query parameter-based negotiation.
  • io.moderne.java.spring.framework.RemoveEmptyPathMatchConfiguration
    • Remove empty path match configuration methods
    • Removes empty configurePathMatch (WebMvc) and configurePathMatching (WebFlux) method overrides. These methods may become empty after deprecated path matching options are removed.
  • io.moderne.java.spring.framework.RemovePathExtensionContentNegotiation
    • Remove path extension content negotiation methods
    • Remove calls to favorPathExtension() and ignoreUnknownPathExtensions() on ContentNegotiationConfigurer. These methods and the underlying PathExtensionContentNegotiationStrategy were removed in Spring Framework 7.0. Path extension content negotiation was deprecated due to URI handling issues. Use query parameter-based negotiation with favorParameter(true) as an alternative.
  • io.moderne.java.spring.framework.RemoveSetPathMatcherCall
    • Remove deprecated setPathMatcher() calls
    • In Spring Framework 7.0, PathMatcher and AntPathMatcher are deprecated in favor of PathPatternParser, which has been the default in Spring MVC since 6.0. This recipe removes calls to setPathMatcher(new AntPathMatcher()) since they are now redundant. The default PathPatternParser provides better performance through pre-parsed patterns.
  • io.moderne.java.spring.framework.ReplaceControllerWithRestController
    • Replace @Controller with @RestController
    • When a class is annotated with @Controller and either the class itself or all of its handler methods are annotated with @ResponseBody, the class can use @RestController instead. This removes the need for individual @ResponseBody annotations.
  • io.moderne.java.spring.framework.UpgradeSpringFramework_3_0
    • Migrate to Spring Framework 3.x
    • Migrate applications to the latest Spring Framework 3 release, pulling in additional proprietary Moderne recipes.
  • io.moderne.java.spring.framework.UpgradeSpringFramework_5_3
    • Migrate to Spring Framework 5.3 (Moderne Edition)
    • Migrate applications to the latest Spring Framework 5.3 release, pulling in additional proprietary Moderne recipes.
  • io.moderne.java.spring.framework.beansxml.BeansXmlToConfiguration
    • Migrate beans.xml to Spring Framework configuration class
    • Converts Java/Jakarta EE beans.xml configuration files to Spring Framework @Configuration classes.
  • io.moderne.java.spring.framework.jsf23.MigrateFacesConfig
    • Migrate JSF variable-resolver to el-resolver
    • Migrates JSF faces-config.xml from namespaces, tags and values that was deprecated in JSF 1.2 and removed in later versions, to the JSF 2.3 compatible constructs.
  • io.moderne.java.spring.framework.webxml.FindWelcomeFileConfiguration
    • Add landing page controller for welcome file configuration
    • Generates a LandingPageController when welcome-file-list is found in web.xml or context-root in jboss-web.xml. When migrating to Spring Framework 5.3+, applications that rely on these server-side landing page configurations need a @Controller with a @RequestMapping for / to avoid 404 errors, as Spring MVC can take over the root mapping. Skips generation if a controller already maps to /.
  • io.moderne.java.spring.framework.webxml.WebXmlToWebApplicationInitializer
    • Migrate web.xml to WebApplicationInitializer
    • Migrate web.xml to WebApplicationInitializer for Spring applications. This allows for programmatic configuration of the web application context, replacing the need for XML-based configuration. This recipe only picks up web.xml files located in the src/main/webapp/WEB-INF directory to avoid inference with tests. It creates a WebXmlWebAppInitializer class in src/main/java with respect to submodules if they contain java files. If it finds an existing WebXmlWebAppInitializer, it skips the creation.
  • io.moderne.java.spring.framework7.AddDynamicDestinationResolverToJmsTemplate
    • Explicitly set DynamicDestinationResolver on JmsTemplate
    • Spring Framework 7.0 changed the default DestinationResolver for JmsTemplate from DynamicDestinationResolver to SimpleDestinationResolver, which caches Session-resolved Queue and Topic instances. This recipe adds an explicit call to setDestinationResolver(new DynamicDestinationResolver()) to preserve the previous behavior. The caching behavior of SimpleDestinationResolver should be fine for most JMS brokers, so this explicit configuration can be removed once compatibility with the new default is verified.
  • io.moderne.java.spring.framework7.AddSpringExtensionConfigForNestedTests
    • Add @SpringExtensionConfig for nested tests
    • Spring Framework 7.0 changed SpringExtension to use test-method scoped ExtensionContext instead of test-class scoped. This can break @Nested test class hierarchies. Adding @SpringExtensionConfig(useTestClassScopedExtensionContext = true) restores the previous behavior.
  • io.moderne.java.spring.framework7.FindOkHttp3IntegrationUsage
    • Find Spring OkHttp3 integration usage
    • Spring Framework 7.0 removes OkHttp3 integration classes. This recipe identifies usages of OkHttp3ClientHttpRequestFactory and OkHttp3ClientHttpConnector that need to be replaced. Consider migrating to Java's built-in HttpClient with JdkClientHttpRequestFactory or JdkClientHttpConnector, or to Apache HttpClient 5 with HttpComponentsClientHttpRequestFactory.
  • io.moderne.java.spring.framework7.FindRemovedAPIs
    • Find removed APIs in Spring Framework 7.0
    • Finds usages of APIs that were removed in Spring Framework 7.0 and require manual intervention. This includes Theme support, OkHttp3 integration, and servlet view document/feed classes which have no direct automated replacement.
  • io.moderne.java.spring.framework7.FindServletViewSupportUsage
    • Find removed Spring servlet view classes
    • Spring Framework 7.0 removes the org.springframework.web.servlet.view.document and org.springframework.web.servlet.view.feed packages. This recipe adds TODO comments to imports of AbstractPdfView, AbstractXlsView, AbstractXlsxView, AbstractXlsxStreamingView, AbstractPdfStampView, AbstractFeedView, AbstractAtomFeedView, and AbstractRssFeedView that need to be replaced with direct usage of the underlying libraries (Apache POI, OpenPDF/iText, ROME) in web handlers.
  • io.moderne.java.spring.framework7.FindThemeSupportUsage
    • Find Spring Theme support usage
    • Spring Framework 7.0 removes Theme support entirely. This recipe identifies usages of Theme-related classes like ThemeResolver, ThemeSource, and ThemeChangeInterceptor that need to be removed or replaced with CSS-based alternatives. The Spring team recommends using CSS directly for theming functionality.
  • io.moderne.java.spring.framework7.MigrateDeprecatedAPIs
    • Migrate deprecated APIs removed in Spring Framework 7.0
    • Migrates deprecated APIs that were removed in Spring Framework 7.0. This includes ListenableFuture to CompletableFuture migration, ContentCachingRequestWrapper constructor changes, and NestedServletException to ServletException type migration.
  • io.moderne.java.spring.framework7.MigrateHttpStatusToRfc9110
    • Migrate HttpStatus enum values to RFC 9110 names
    • Spring Framework 7.0 aligns HttpStatus enum values with RFC 9110. This recipe replaces deprecated status code constants with their RFC 9110 equivalents: PAYLOAD_TOO_LARGE becomes CONTENT_TOO_LARGE and UNPROCESSABLE_ENTITY becomes UNPROCESSABLE_CONTENT.
  • io.moderne.java.spring.framework7.MigrateJackson2ObjectMapperBuilder
    • Migrate Jackson2ObjectMapperBuilder to mapper builder pattern
    • Replaces Jackson2ObjectMapperBuilder.json().build() and similar factory methods with the corresponding Jackson mapper builder pattern (e.g. JsonMapper.builder()...build()). Setter calls on the resulting mapper are folded into the builder chain when safe, or annotated with a TODO comment when automatic migration is not possible.
  • io.moderne.java.spring.framework7.MigrateJmsDestinationResolver
    • Preserve DynamicDestinationResolver behavior for JmsTemplate
    • Spring Framework 7.0 changed the default DestinationResolver for JmsTemplate from DynamicDestinationResolver to SimpleDestinationResolver, which caches Session-resolved Queue and Topic instances. This recipe explicitly configures DynamicDestinationResolver to preserve the pre-7.0 behavior. The caching behavior of SimpleDestinationResolver should be fine for most JMS brokers, so this explicit configuration can be removed once verified.
  • io.moderne.java.spring.framework7.MigrateListenableFuture
    • Migrate ListenableFuture to CompletableFuture
    • Spring Framework 6.0 deprecated ListenableFuture in favor of CompletableFuture. Spring Framework 7.0 removes ListenableFuture entirely. This recipe migrates usages of ListenableFuture and its callbacks to use CompletableFuture and BiConsumer instead.
  • io.moderne.java.spring.framework7.MigrateResponseEntityGetStatusCodeValueMethod
    • Migrate ResponseEntity#getStatusCodeValue() to getStatusCode().value()
    • Replaces calls to ResponseEntity#getStatusCodeValue() which was deprecated in Spring Framework 6.0 and removed in Spring Framework 7.0 with getStatusCode().value().
  • io.moderne.java.spring.framework7.RemoveNullabilityFromSpringHttpEntityTypeArguments
    • Remove Kotlin nullability from Spring HTTP entity type arguments
    • Spring Framework 7 narrowed HttpEntity&lt;T&gt; (and its subtypes ResponseEntity and RequestEntity) to &lt;T : Any&gt;. This recipe removes Kotlin's ? nullable marker from the type argument of these types in declared parameterized types and in explicit method-invocation type arguments, so Kotlin sources continue to compile.
  • io.moderne.java.spring.framework7.RemoveSpringJcl
    • Remove spring-jcl dependency
    • The spring-jcl module has been removed in Spring Framework 7.0 in favor of Apache Commons Logging 1.3.0. This recipe removes any explicit dependency on org.springframework:spring-jcl. The change should be transparent for most applications, as spring-jcl was typically a transitive dependency and the logging API calls (org.apache.commons.logging.*) remain unchanged.
  • io.moderne.java.spring.framework7.RenameMemberCategoryConstants
    • Rename MemberCategory field constants for Spring Framework 7.0
    • Renames deprecated MemberCategory constants to their new names in Spring Framework 7.0. MemberCategory.PUBLIC_FIELDS is renamed to MemberCategory.INVOKE_PUBLIC_FIELDS and MemberCategory.DECLARED_FIELDS is renamed to MemberCategory.INVOKE_DECLARED_FIELDS. These renames clarify the original intent of these categories and align with the rest of the API.
  • io.moderne.java.spring.framework7.RenameRequestContextJstlPresent
    • Rename RequestContext.jstPresent to JSTL_PRESENT
    • Renames the protected static field RequestContext.jstPresent to JSTL_PRESENT in Spring Framework 7.0. This field was renamed as part of a codebase-wide effort to use uppercase for classpath-related static final field names (see https://github.com/spring-projects/spring-framework/issues/35525).
  • io.moderne.java.spring.framework7.ReplaceJUnit4SpringTestBaseClasses
    • Replace JUnit 4 Spring test base classes with JUnit Jupiter annotations
    • Replace AbstractJUnit4SpringContextTests and AbstractTransactionalJUnit4SpringContextTests base classes with @ExtendWith(SpringExtension.class) and @Transactional annotations. These base classes are deprecated in Spring Framework 7.0 in favor of the SpringExtension for JUnit Jupiter.
  • io.moderne.java.spring.framework7.SimplifyReflectionHintRegistration
    • Simplify reflection hint registrations for Spring Framework 7.0
    • Removes deprecated MemberCategory arguments from registerType() calls on ReflectionHints. In Spring Framework 7.0, registering a reflection hint for a type now implies methods, constructors, and fields introspection. All MemberCategory values except INVOKE_* have been deprecated. This recipe removes those deprecated arguments, simplifying code like hints.reflection().registerType(MyType.class, MemberCategory.DECLARED_FIELDS) to hints.reflection().registerType(MyType.class).
  • io.moderne.java.spring.framework7.UpdateGraalVmNativeHints
    • Update GraalVM native reflection hints for Spring Framework 7.0
    • Migrates GraalVM native reflection hints to Spring Framework 7.0 conventions. Spring Framework 7.0 adopts the unified reachability metadata format for GraalVM. This recipe renames deprecated MemberCategory constants and simplifies reflection hint registrations where explicit member categories are no longer needed.
  • io.moderne.java.spring.framework7.UpgradeSpringFramework_7_0
    • Migrate to Spring Framework 7.0
    • Migrates applications to Spring Framework 7.0. This recipe applies all necessary changes including API migrations, removed feature detection, and configuration updates.
  • io.moderne.java.spring.framework7.WrapGenericMessageMapInMessageHeaders
    • Wrap GenericMessage map argument in MessageHeaders
    • Wraps the Map argument in GenericMessage constructors in Kotlin sources with MessageHeaders(map) to explicitly use the MessageHeaders overload. This resolves Kotlin overload resolution ambiguity between the Map and MessageHeaders constructor overloads.
  • io.moderne.java.spring.hibernate.MigrateDaoSupportGetSession
    • Migrate HibernateDaoSupport#getSession() usage
    • Migrate HibernateDaoSupport#getSession() usage to HibernateDaoSupport#getSessionFactory()#getCurrentSession() and annotate the methods with @Transactional.
  • io.moderne.java.spring.hibernate.MigrateSaveOrUpdateAll
    • Migrate HibernateDaoSupport#getHibernateTemplate#saveOrUpdateAll
    • Migrate removed HibernateDaoSupport#getHibernateTemplate#.saveOrUpdateAll to an iterative HibernateDaoSupport#getHibernateTemplate#.saveOrUpdate.
  • io.moderne.java.spring.kafka.consumer.FindKafkaListenerWithoutErrorHandling
    • Find @KafkaListener methods without error handling
    • Flags @KafkaListener methods that lack proper error handling. Methods should have @RetryableTopic, specify an errorHandler in the annotation, or implement try-catch blocks for error handling.
  • io.moderne.java.spring.kafka.consumer.FindMissingDltHandler
    • Find @RetryableTopic without @DltHandler
    • Flags classes that use @RetryableTopic without a corresponding @DltHandler method. A DLT handler should be defined to process messages that have exhausted all retries.
  • io.moderne.java.spring.kafka.consumer.IsKafkaConsumer
    • Is likely a Kafka consumer module
    • Marks the project if it's likely a Kafka consumer module.
  • io.moderne.java.spring.kafka.producer.FindCustomKeyUsage
    • Find KafkaTemplate.send() with custom key
    • Flags KafkaTemplate.send() calls that use a custom key (3+ arguments). Custom keys should be reviewed to ensure they provide appropriate partition distribution.
  • io.moderne.java.spring.kafka.producer.IsKafkaProducer
    • Is likely a Kafka producer module
    • Marks the project if it's likely a Kafka producer module.
  • io.moderne.java.spring.orm.SpringORM5
    • Migrate to Spring ORM to 5
    • Migrate applications using Spring ORM Hibernate Support to Hibernate 5 compatible version. This will enable a further migration by the Spring Framework migration past 5.
  • io.moderne.java.spring.security.MigrateAcegiToSpringSecurity_5_0
    • Migrate from Acegi Security 1.0.x to Spring Security 5.0
    • Migrates Acegi Security 1.0.x directly to Spring Security 5.0. This recipe handles dependency changes, type renames, XML configuration updates, web.xml filter migration, and adds TODO comments for password encoders that require manual migration.
  • io.moderne.java.spring.security6.MigrateAntPathRequestMatcher
    • Migrate antPathRequestMatcher to pathPatternRequestMatcher
    • In Spring Security 6.5, AntPathRequestMatcher is deprecated in favor of PathPatternRequestMatcher. This recipe migrates static method calls and constructor usage to the new pattern in both Java and Kotlin sources.
  • io.moderne.java.spring.security6.UpgradeSpringSecurity_6_5
    • Migrate to Spring Security 6.5 (Moderne Edition)
    • Migrate applications to the latest Spring Security 6.5 release. This recipe will modify an application's build files, make changes to deprecated/preferred APIs, and migrate configuration settings that have changes between versions.
  • io.moderne.java.spring.security7.CommentOnSecurityContextAuthenticationInKotlin
    • Comment on Kotlin usages of SecurityContext.getAuthentication()
    • Spring Security 7 made SecurityContext.getAuthentication() return @Nullable Authentication. In Kotlin this becomes Authentication?, so existing chains like SecurityContextHolder.getContext().authentication.credentials no longer compile. This recipe adds a TODO comment on the line above each Kotlin statement that reads the authentication — both the explicit getAuthentication() form and the Kotlin property form .authentication — so a developer can decide per call site whether to use a safe-call (?.) or a non-null assertion (!!).
  • io.moderne.java.spring.security7.MigrateMvcRequestMatcher
    • Migrate MvcRequestMatcher to PathPatternRequestMatcher
    • In Spring Security 7.0, MvcRequestMatcher which depends on the deprecated HandlerMappingIntrospector is removed in favor of PathPatternRequestMatcher. This recipe migrates constructor and builder usage to the new pattern.
  • io.moderne.java.spring.security7.MigrateOAuth2AccessTokenResponseClient
    • Migrate OAuth2AccessTokenResponseClient from RestOperations to RestClient based implementations
    • A new set of OAuth2AccessTokenResponseClient implementations were introduced based on RestClient. This recipe replaces the RestOperations-based implementations which have been deprecated. The RestClient implementations are drop-in replacements for the deprecated implementations.
  • io.moderne.java.spring.security7.MigrateOAuth2RestOperationsToRestClient
    • Migrate OAuth2 token response client from RestOperations to RestClient
    • Migrates setRestOperations(RestOperations) calls to setRestClient(RestClient) on the new RestClient-based OAuth2 AccessTokenResponseClient implementations. The RestClient-based implementations introduced in Spring Security 7 use RestClient instead of RestOperations.
  • io.moderne.java.spring.security7.MigrateRequiresChannelToRedirectToHttps
    • Migrate requiresChannel() to redirectToHttps()
    • In Spring Security 7.0, HttpSecurity.requiresChannel() is deprecated in favor of HttpSecurity.redirectToHttps(). This recipe renames the method call and simplifies anyRequest().requiresSecure() to Customizer.withDefaults().
  • io.moderne.java.spring.security7.ModularizeSpringSecurity7
    • Spring Security 7 modularization
    • Spring Security Core was modularized in version 7, deprecated classes that are still a crucial part of some applications are moved to spring-security-access.

rewrite-tapestry

  • org.openrewrite.tapestry.ChangeTapestryPackages
    • Change Tapestry 4 packages to Tapestry 5
    • Updates package imports from org.apache.tapestry to org.apache.tapestry5. Only renames packages that have direct equivalents in Tapestry 5.
  • org.openrewrite.tapestry.ChangeTapestryTypes
    • Change Tapestry 4 types to Tapestry 5 equivalents
    • Renames Tapestry 4 types that have direct equivalents in Tapestry 5. This handles types from different packages that were reorganized in T5.
  • org.openrewrite.tapestry.ConvertAnnotatedMethodToField
    • Convert annotated abstract method to field
    • Converts abstract getter methods annotated with sourceAnnotation to private fields annotated with targetAnnotation. Also removes corresponding abstract setter methods.
  • org.openrewrite.tapestry.ConvertBeanAnnotation
    • Convert Tapestry 4 @Bean to @Property
    • Converts Tapestry 4's @Bean annotation to @Property fields. Bean initialization with 'initializer' attribute requires manual migration.
  • org.openrewrite.tapestry.ConvertListenerInterfaces
    • Convert Tapestry 4 listener interfaces to Tapestry 5 annotations
    • Converts Tapestry 4 page lifecycle listener interfaces (PageBeginRenderListener, PageEndRenderListener, etc.) to Tapestry 5 lifecycle annotations (@SetupRender, @CleanupRender, etc.) and removes the interface implementations.
  • org.openrewrite.tapestry.MigrateTapestry4To5
    • Migrate Tapestry 4 to Tapestry 5
    • Migrates Apache Tapestry 4 applications to Tapestry 5. This includes package renames, removing base class inheritance, converting listener interfaces to annotations, and updating dependencies.
  • org.openrewrite.tapestry.RemoveIRequestCycleParameter
    • Remove IRequestCycle parameters
    • Removes IRequestCycle parameters from methods. In Tapestry 5, event handler methods don't receive the request cycle as a parameter.
  • org.openrewrite.tapestry.RemoveObsoleteFormTypes
    • Remove obsolete Tapestry form types
    • Removes field declarations and imports for Tapestry 4 form component types (IPropertySelectionModel, StringPropertySelectionModel, etc.) that don't exist in Tapestry 5. Code using these types will need manual refactoring to use Tapestry 5's SelectModel pattern.
  • org.openrewrite.tapestry.RemoveTapestryBaseClasses
    • Remove Tapestry 4 base classes
    • Removes Tapestry 4 base class inheritance (BasePage, BaseComponent, AbstractComponent) and converts the class to a POJO suitable for Tapestry 5. Abstract getter/setter methods are converted to fields with @Property annotation.
  • org.openrewrite.tapestry.ReplaceReverseComparator
    • Replace ReverseComparator with Collections.reverseOrder()
    • Replaces tapestry-contrib's ReverseComparator with the standard Java Collections.reverseOrder() method.
  • org.openrewrite.tapestry.UpdateTapestryDependencies
    • Update Tapestry dependencies
    • Updates dependencies from Tapestry 4 to Tapestry 5.

rewrite-vulncheck

  • io.moderne.vulncheck.FixVulnCheckVulnerabilities
    • Use VulnCheck Exploit Intelligence to fix vulnerabilities
    • This software composition analysis (SCA) tool detects and upgrades dependencies with publicly disclosed vulnerabilities. This recipe both generates a report of vulnerable dependencies and upgrades to newer versions with fixes. This recipe by default only upgrades to the latest patch version. If a minor or major upgrade is required to reach the fixed version, this can be controlled using the maximumUpgradeDelta option. Vulnerability information comes from VulnCheck Vulnerability Intelligence. The recipe has an option to limit fixes to only those vulnerabilities that have evidence of exploitation at various levels of severity.

org.openrewrite

rewrite-python

  • org.openrewrite.python.AddDependency
    • Add Python dependency
    • Add a dependency to a Python project. Supports pyproject.toml (with scope/group targeting), requirements.txt, and Pipfile. When the matching package manager (uv or pipenv) is available, the corresponding lock file (uv.lock or Pipfile.lock) is regenerated. Not safe to use as a precondition: invokes the package manager and publishes per-project state shared with other dependency recipes.
  • org.openrewrite.python.AddLiteralMethodArgument
    • Add literal method argument
    • Add a literal argument to method invocations matching a pattern.
  • org.openrewrite.python.ChangeDependency
    • Change Python dependency
    • Change a dependency to a different package. Supports pyproject.toml, requirements.txt, and Pipfile. Searches all dependency scopes. When the matching package manager (uv or pipenv) is available, the corresponding lock file (uv.lock or Pipfile.lock) is regenerated. Not safe to use as a precondition: invokes the package manager and publishes per-project state shared with other dependency recipes.
  • org.openrewrite.python.ChangeImport
    • Change import
    • Change a Python import from one module/name to another, updating all type attributions.
  • org.openrewrite.python.ChangeMethodName
    • Change method name
    • Rename method invocations matching a pattern.
  • org.openrewrite.python.ChangePackage
    • Change package
    • Change package/module references from one name to another.
  • org.openrewrite.python.ChangeType
    • Change type
    • Change a type reference from one fully qualified name to another.
  • org.openrewrite.python.DeleteMethodArgument
    • Delete method argument
    • Remove an argument from method invocations matching a pattern.
  • org.openrewrite.python.RemoveDependency
    • Remove Python dependency
    • Remove a dependency from a Python project. Supports pyproject.toml (with scope/group targeting), requirements.txt, and Pipfile. When the matching package manager (uv or pipenv) is available, the corresponding lock file (uv.lock or Pipfile.lock) is regenerated. Not safe to use as a precondition: invokes the package manager and publishes per-project state shared with other dependency recipes.
  • org.openrewrite.python.RemovePass
    • Remove redundant pass statements
    • Remove redundant pass statements from Python code when there are other executable statements in the block.
  • org.openrewrite.python.ReorderMethodArguments
    • Reorder method arguments
    • Reorder arguments in method invocations matching a pattern.
  • org.openrewrite.python.UpgradeDependencyVersion
    • Upgrade Python dependency version
    • Upgrade the version constraint for a dependency. Supports pyproject.toml (with scope/group targeting), requirements.txt, and Pipfile. When the matching package manager (uv or pipenv) is available, the corresponding lock file (uv.lock or Pipfile.lock) is regenerated. Not safe to use as a precondition: invokes the package manager and publishes per-project state shared with other dependency recipes.
  • org.openrewrite.python.UpgradeTransitiveDependencyVersion
    • Upgrade transitive Python dependency version
    • Pin a transitive dependency version using the strategy appropriate for the file type and package manager. For pyproject.toml: uv uses [tool.uv].constraint-dependencies, PDM uses [tool.pdm.overrides], and other managers add a direct dependency. For requirements.txt and Pipfile: appends the dependency. Not safe to use as a precondition: invokes the package manager and publishes per-project state shared with other dependency recipes.
  • org.openrewrite.python.format.PythonSpaces
    • Formats spaces in Python code
    • Standardizes spaces in Python code. Currently limited to formatting method arguments.
  • org.openrewrite.python.search.DependencyInsight
    • Python dependency insight
    • Find direct and transitive Python dependencies matching a package name pattern. Results include dependencies that either directly match or transitively include a matching dependency.

org.openrewrite.recipe

rewrite-android

rewrite-circleci

rewrite-codemods-ng

rewrite-compiled-analysis

rewrite-concourse

rewrite-dotnet

rewrite-java-security

  • org.openrewrite.csharp.dependencies.DependencyInsight
    • Dependency insight for C#
    • Finds dependencies in *.csproj and packages.config.
  • org.openrewrite.csharp.dependencies.DependencyVulnerabilityCheck
    • Find and fix vulnerable Nuget dependencies
    • This software composition analysis (SCA) tool detects and upgrades dependencies with publicly disclosed vulnerabilities. This recipe both generates a report of vulnerable dependencies and upgrades to newer versions with fixes. This recipe by default only upgrades to the latest patch version. If a minor or major upgrade is required to reach the fixed version, this can be controlled using the maximumUpgradeDelta option. Vulnerability information comes from the GitHub Security Advisory Database, which aggregates vulnerability data from several public databases, including the National Vulnerability Database maintained by the United States government. Dependencies following Semantic Versioning will see their patch version updated where applicable. Last updated: 2026-05-11T1202.
  • org.openrewrite.csharp.dependencies.UpgradeDependencyVersion
    • Upgrade C# dependency versions
    • Upgrades dependencies in *.csproj, Directory.Packages.props, and packages.config.
  • org.openrewrite.java.dependencies.AddExplicitTransitiveDependencies
    • Add explicit transitive dependencies
    • Detects when Java source code or configuration files reference types from transitive Maven dependencies and promotes those transitive dependencies to explicit direct dependencies in the pom.xml. This ensures the build is resilient against changes in transitive dependency trees of upstream libraries.
  • org.openrewrite.java.dependencies.DependencyLicenseCheck
    • Find licenses in use in third-party dependencies
    • Locates and reports on all licenses in use.
  • org.openrewrite.java.dependencies.DependencyVulnerabilityCheck
    • Find and fix vulnerable dependencies
    • This software composition analysis (SCA) tool detects and upgrades dependencies with publicly disclosed vulnerabilities. This recipe both generates a report of vulnerable dependencies and upgrades to newer versions with fixes. This recipe by default only upgrades to the latest patch version. If a minor or major upgrade is required to reach the fixed version, this can be controlled using the maximumUpgradeDelta option. Vulnerability information comes from the GitHub Security Advisory Database, which aggregates vulnerability data from several public databases, including the National Vulnerability Database maintained by the United States government. Upgrades dependencies versioned according to Semantic Versioning. ## Customizing Vulnerability Data This recipe can be customized by extending DependencyVulnerabilityCheckBase and overriding the vulnerability data sources: - baselineVulnerabilities(ExecutionContext ctx): Provides the default set of known vulnerabilities. The base implementation loads vulnerability data from the GitHub Security Advisory Database CSV file using ResourceUtils.parseResourceAsCsv(). Override this method to replace the entire vulnerability dataset with your own curated list. - supplementalVulnerabilities(ExecutionContext ctx): Allows adding custom vulnerability data beyond the baseline. The base implementation returns an empty list. Override this method to add organization-specific vulnerabilities, internal security advisories, or vulnerabilities from additional sources while retaining the baseline GitHub Advisory Database. Both methods return List&lt;Vulnerability&gt; objects. Vulnerability data can be loaded from CSV files using ResourceUtils.parseResourceAsCsv(path, Vulnerability.class, consumer) or constructed programmatically. To customize, extend DependencyVulnerabilityCheckBase and override one or both methods depending on your needs. For example, override supplementalVulnerabilities() to add custom CVEs while keeping the standard vulnerability database, or override baselineVulnerabilities() to use an entirely different vulnerability data source. Last updated: 2026-05-11T1202.
  • org.openrewrite.java.dependencies.RemoveUnusedDependencies
    • Remove unused dependencies
    • Scans through source code collecting references to types and methods, removing any dependencies that are not used from Maven or Gradle build files. This is best effort and not guaranteed to work well in all cases; false positives are still possible. This recipe takes reflective access into account: - When reflective access to a class is made unambiguously via a string literal, such as: Class.forName(&quot;java.util.List&quot;) that is counted correctly. - When reflective access to a class is made ambiguously via anything other than a string literal no dependencies will be removed. This recipe takes transitive dependencies into account: - When a direct dependency is not used but a transitive dependency it brings in is in use the direct dependency is not removed.
  • org.openrewrite.java.dependencies.SoftwareBillOfMaterials
    • Software bill of materials
    • Produces a software bill of materials (SBOM) for a project. An SBOM is a complete list of all dependencies used in a project, including transitive dependencies. The produced SBOM is in the CycloneDX XML format. Supports Gradle and Maven. Places a file named sbom.xml adjacent to the Gradle or Maven build file.
  • org.openrewrite.java.security.FindTextDirectionChanges
    • Find text-direction changes
    • Finds unicode control characters which can change the direction text is displayed in. These control characters can alter how source code is presented to a human reader without affecting its interpretation by tools like compilers. So a malicious patch could pass code review while introducing vulnerabilities. Note that text direction-changing unicode control characters aren't inherently malicious. These characters can appear for legitimate reasons in code written in or dealing with right-to-left languages. See: https://trojansource.codes/ for more information.
  • org.openrewrite.java.security.FixCwe338
    • Fix CWE-338 with SecureRandom
    • Use a cryptographically strong pseudo-random number generator (PRNG).
  • org.openrewrite.java.security.FixCwe918
    • Remediate server-side request forgery (SSRF)
    • Inserts a guard that validates URLs constructed from user-controlled input do not target internal network addresses, blocking server-side request forgery (SSRF) attacks.
  • org.openrewrite.java.security.ImproperPrivilegeManagement
    • Improper privilege management
    • Marking code as privileged enables a piece of trusted code to temporarily enable access to more resources than are available directly to the code that called it.
  • org.openrewrite.java.security.JavaSecurityBestPractices
    • Java security best practices
    • Applies security best practices to Java code.
  • org.openrewrite.java.security.Owasp2025A01
    • Remediate OWASP A01:2025 Broken access control
    • OWASP A01:2025 describes failures related to broken access control.
  • org.openrewrite.java.security.Owasp2025A02
    • Remediate OWASP A02:2025 Security misconfiguration
    • OWASP A02:2025 describes failures related to security misconfiguration. Previously A05:2021, this category moved up to #2 in 2025.
  • org.openrewrite.java.security.Owasp2025A03
    • Remediate OWASP A03:2025 Software supply chain failures
    • OWASP A03:2025 describes failures related to the software supply chain, including vulnerable and outdated components. Expanded from A06:2021 Vulnerable and Outdated Components.
  • org.openrewrite.java.security.Owasp2025A04
    • Remediate OWASP A04:2025 Cryptographic failures
    • OWASP A04:2025 describes failures related to cryptography (or lack thereof), which often lead to exposure of sensitive data. Previously A02:2021.
  • org.openrewrite.java.security.Owasp2025A05
    • Remediate OWASP A05:2025 Injection
    • OWASP A05:2025 describes failures related to user-supplied data being used to influence program state to operate outside of its intended bounds. Previously A03:2021.
  • org.openrewrite.java.security.Owasp2025A07
    • Remediate OWASP A07:2025 Identification and authentication failures
    • OWASP A07:2025 describes failures related to identification and authentication, including weak credential management, missing brute force protections, session fixation, hardcoded credentials, insecure "remember me", and missing multi-factor authentication. Same position as A07:2021 (no prior aggregator existed).
  • org.openrewrite.java.security.OwaspA01
    • Remediate OWASP A01:2021 Broken access control
    • OWASP A01:2021 describes failures related to broken access control.
  • org.openrewrite.java.security.OwaspA02
    • Remediate OWASP A02:2021 Cryptographic failures
    • OWASP A02:2021 describes failures related to cryptography (or lack thereof), which often lead to exposure of sensitive data. This recipe seeks to remediate these vulnerabilities.
  • org.openrewrite.java.security.OwaspA03
    • Remediate OWASP A03:2021 Injection
    • OWASP A03:2021 describes failures related to user-supplied data being used to influence program state to operate outside of its intended bounds. This recipe seeks to remediate these vulnerabilities.
  • org.openrewrite.java.security.OwaspA05
    • Remediate OWASP A05:2021 Security misconfiguration
    • OWASP A05:2021 describes failures related to security misconfiguration.
  • org.openrewrite.java.security.OwaspA06
    • Remediate OWASP A06:2021 Vulnerable and outdated components
    • OWASP A06:2021 describes failures related to vulnerable and outdated components.
  • org.openrewrite.java.security.OwaspA08
    • Remediate OWASP A08:2021 Software and data integrity failures
    • OWASP A08:2021 software and data integrity failures.
  • org.openrewrite.java.security.OwaspTopTen
    • Remediate vulnerabilities from the OWASP Top Ten
    • OWASP publishes a list of the most impactful common security vulnerabilities. These recipes identify and remediate vulnerabilities from the OWASP Top Ten.
  • org.openrewrite.java.security.PartialPathTraversalVulnerability
    • Partial path traversal vulnerability
    • Replaces dir.getCanonicalPath().startsWith(parent.getCanonicalPath(), which is vulnerable to partial path traversal attacks, with the more secure dir.getCanonicalFile().toPath().startsWith(parent.getCanonicalFile().toPath()). To demonstrate this vulnerability, consider &quot;/usr/outnot&quot;.startsWith(&quot;/usr/out&quot;). The check is bypassed although /outnot is not under the /out directory. It's important to understand that the terminating slash may be removed when using various String representations of the File object. For example, on Linux, println(new File(&quot;/var&quot;)) will print /var, but println(new File(&quot;/var&quot;, &quot;/&quot;) will print /var/; however, println(new File(&quot;/var&quot;, &quot;/&quot;).getCanonicalPath()) will print /var.
  • org.openrewrite.java.security.RegularExpressionDenialOfService
    • Regular Expression Denial of Service (ReDOS)
    • ReDoS is a Denial of Service attack that exploits the fact that most Regular Expression implementations may reach extreme situations that cause them to work very slowly (exponentially related to input size). See the OWASP description of this attack here for more details.
  • org.openrewrite.java.security.SecureRandom
    • Secure random
    • Use cryptographically secure Pseudo Random Number Generation in the "main" source set. Replaces instantiation of java.util.Random with java.security.SecureRandom.
  • org.openrewrite.java.security.SecureRandomPrefersDefaultSeed
    • SecureRandom seeds are not constant or predictable
    • Remove SecureRandom#setSeed(*) method invocations having constant or predictable arguments.
  • org.openrewrite.java.security.SecureTempFileCreation
    • Use secure temporary file creation
    • java.io.File.createTempFile() has exploitable default file permissions. This recipe migrates to the more secure java.nio.file.Files.createTempFile().
  • org.openrewrite.java.security.UseFilesCreateTempDirectory
    • Use Files#createTempDirectory
    • Use Files#createTempDirectory when the sequence File#createTempFile(..)->File#delete()->File#mkdir() is used for creating a temp directory.
  • org.openrewrite.java.security.XmlParserXXEVulnerability
    • XML parser XXE vulnerability
    • Avoid exposing dangerous features of the XML parser by updating certain factory settings.
  • org.openrewrite.java.security.ZipSlip
    • Zip slip
    • Zip slip is an arbitrary file overwrite critical vulnerability, which typically results in remote command execution. A fuller description of this vulnerability is available in the Snyk documentation on it.
  • org.openrewrite.java.security.marshalling.InsecureJmsDeserialization
    • Insecure JMS deserialization
    • JMS Object messages depend on Java Serialization for marshalling/unmarshalling of the message payload when ObjectMessage#getObject is called. Deserialization of untrusted data can lead to security flaws.
  • org.openrewrite.java.security.marshalling.SecureJacksonDefaultTyping
    • Secure the use of Jackson default typing
    • See the blog post on this subject.
  • org.openrewrite.java.security.marshalling.SecureSnakeYamlConstructor
    • Secure the use of SnakeYAML's constructor
    • See the paper on this subject.
  • org.openrewrite.java.security.search.FindCommandInjection
    • Find OS command injection vectors
    • Finds calls to Runtime.exec(String) which passes the command through a shell interpreter, enabling command injection via metacharacters like ;, |, and &amp;&amp;. Use the String[] overload instead to avoid shell interpretation.
  • org.openrewrite.java.security.search.FindExpressionLanguageInjection
    • Find Expression Language injection vectors
    • Finds calls to Expression Language (EL) evaluation methods which, when the expression is built from user input, can allow arbitrary code execution. Use parameterized expressions or input validation instead.
  • org.openrewrite.java.security.search.FindHardcodedAuthenticationCredentials
    • Find hardcoded authentication credentials
    • Finds hardcoded passwords flowing into Spring Security user builders: InMemoryUserDetailsManagerConfigurer (inMemoryAuthentication().withUser(...).password(...)) and the User.UserBuilder.password(...) API. Uses taint analysis so credentials assigned to a variable, field, or constant before being passed to .password(...) are also detected.
  • org.openrewrite.java.security.search.FindHardcodedIv
    • Find hardcoded initialization vectors
    • Finds IvParameterSpec constructed with hardcoded byte arrays or string literals. A static IV makes CBC and other modes deterministic, enabling chosen-plaintext attacks. IVs should be generated randomly using SecureRandom for each encryption operation.
  • org.openrewrite.java.security.search.FindHttpResponseSplitting
    • Find HTTP response splitting vectors
    • Finds calls to HttpServletResponse.addHeader(), setHeader(), and addCookie() which, when header values are derived from user input without CRLF sanitization, can allow HTTP response splitting attacks. Full taint-based detection requires rewrite-program-analysis; this recipe identifies the sink call sites for manual review.
  • org.openrewrite.java.security.search.FindInadequateKeySize
    • Find inadequate cryptographic key sizes
    • Finds cryptographic key generation with inadequate key sizes. RSA keys should be at least 2048 bits, DSA keys at least 2048 bits, EC keys at least 256 bits, and symmetric keys (AES) at least 128 bits. NIST recommends RSA-2048+ and AES-128+ as minimum for all new applications.
  • org.openrewrite.java.security.search.FindInsecureRememberMeConfig
    • Find insecure Spring Security RememberMe configuration
    • Finds Spring Security RememberMe configurations with insecure settings: useSecureCookie(false) (allows cookie transmission over HTTP), alwaysRemember(true) (bypasses user opt-in), or tokenValiditySeconds(...) set longer than 30 days (extends the window in which a stolen remember-me cookie can be replayed).
  • org.openrewrite.java.security.search.FindInsecureSessionFixationConfig
    • Find Spring Security configurations that disable session fixation protection
    • Finds Spring Security configurations that disable session fixation protection by calling sessionFixation().none(). Without session fixation protection, an attacker who obtains a victim's session identifier before authentication can reuse it to hijack the authenticated session. Spring Security defaults to changeSessionId(); applications should keep that default or use migrateSession().
  • org.openrewrite.java.security.search.FindJacksonDefaultTypeMapping
    • Find Jackson default type mapping enablement
    • ObjectMapper#enableTypeMapping(..) can lead to vulnerable deserialization.
  • org.openrewrite.java.security.search.FindLongSessionTimeout
    • Find long or disabled HTTP session timeout
    • Finds calls to HttpSession.setMaxInactiveInterval(int) whose integer-literal argument exceeds 30 minutes or is zero/negative (which disables session expiration). Long-lived or non-expiring sessions increase the window for session hijacking and replay (CWE-613).
  • org.openrewrite.java.security.search.FindPermissiveCorsConfiguration
    • Find permissive CORS configuration
    • Finds overly permissive CORS configurations that allow all origins, which can expose the application to cross-domain attacks.
  • org.openrewrite.java.security.search.FindPredictableSalt
    • Find predictable cryptographic salts
    • Finds PBEParameterSpec and PBEKeySpec constructed with hardcoded salt byte arrays. A predictable salt undermines the purpose of salting, making rainbow table and precomputation attacks feasible. Salts should be generated randomly using SecureRandom.
  • org.openrewrite.java.security.search.FindProcessControl
    • Find process control vectors
    • Finds calls to System.loadLibrary(), System.load(), and Runtime.load() which, when the library path or name is derived from user input, can allow an attacker to load arbitrary native code. Ensure library names are not externally controlled.
  • org.openrewrite.java.security.search.FindResourceInjection
    • Find resource injection vectors
    • Detects resource injection vulnerabilities where user-controlled input flows to resource access operations — file paths, JNDI lookups, class loading, and native library loading. Uses taint analysis from rewrite-program-analysis for source-to-sink tracking with sanitizer support, plus structural detection as fallback.
  • org.openrewrite.java.security.search.FindRsaWithoutOaep
    • Find RSA encryption without OAEP padding
    • Finds uses of RSA encryption with PKCS#1 v1.5 padding or no padding specification. RSA without OAEP padding is vulnerable to padding oracle attacks. Use RSA/ECB/OAEPWithSHA-256AndMGF1Padding or equivalent OAEP mode instead.
  • org.openrewrite.java.security.search.FindScriptEngineInjection
    • Find script engine code injection vectors
    • Finds calls to ScriptEngine.eval() which can execute arbitrary code if the script string is influenced by user input. Consider sandboxing or removing dynamic script evaluation.
  • org.openrewrite.java.security.search.FindSensitiveApiEndpoints
    • Find sensitive API endpoints
    • Find data models exposed by REST APIs that contain sensitive information like PII and secrets.
  • org.openrewrite.java.security.search.FindSqlInjection
    • Find potential SQL injection
    • Finds SQL query methods where the query string is constructed via string concatenation, which may indicate SQL injection vulnerabilities. Use parameterized queries or prepared statements instead.
  • org.openrewrite.java.security.search.FindUnsafeReflection
    • Find unsafe reflection vectors
    • Finds calls to Class.forName() which, when the class name is derived from user input, can allow an attacker to instantiate arbitrary classes. Review these call sites to ensure the class name is not externally controlled.
  • org.openrewrite.java.security.search.FindUnsignedJwt
    • Find unsigned JWT usage
    • Finds construction or parsing of Nimbus PlainJWT — an unsecured JWT that has no signature or MAC. Unsecured JWTs allow an attacker to forge tokens because their payloads are not integrity-protected. Use a signed (SignedJWT) or encrypted (EncryptedJWT) JWT instead.
  • org.openrewrite.java.security.search.FindUserWithDefaultPasswordEncoder
    • Find User.withDefaultPasswordEncoder() usage
    • Flags any call to User.withDefaultPasswordEncoder() from Spring Security. The factory is documented as for non-production demos only: it stores credentials in memory with a fixed encoder and is unsafe for real workloads.
  • org.openrewrite.java.security.search.FindVulnerableJacksonJsonTypeInfo
    • Find vulnerable uses of Jackson @JsonTypeInfo
    • Identify where attackers can deserialize gadgets into a target field.
  • org.openrewrite.java.security.search.FindWeakCryptoAlgorithm
    • Find weak cryptographic algorithms
    • Finds uses of broken or risky cryptographic algorithms such as MD5, SHA-1, DES, DESede (3DES), RC2, RC4, and Blowfish in calls to Cipher.getInstance(), MessageDigest.getInstance(), Mac.getInstance(), KeyGenerator.getInstance(), and SecretKeyFactory.getInstance().
  • org.openrewrite.java.security.search.FindWeakDigestInPasswordEncoder
    • Find weak message digests used inside custom PasswordEncoder implementations
    • Finds calls to MessageDigest.getInstance(&quot;...&quot;) whose algorithm is unsuitable for password storage, scoped to classes implementing org.springframework.security.crypto.password.PasswordEncoder. Unsalted and non-iterated digests (MD2, MD4, MD5, SHA-1, SHA-224/256/384/512) are unsuitable for password hashing regardless of how they are wrapped. Delegate to BCryptPasswordEncoder, Argon2PasswordEncoder, Pbkdf2PasswordEncoder, or SCryptPasswordEncoder instead of implementing PasswordEncoder yourself.
  • org.openrewrite.java.security.search.FindWeakPasswordEncoderStrength
    • Find weak password encoder strength
    • Finds Spring Security BCryptPasswordEncoder instantiations with a strength (work factor) below 10. The default and OWASP-recommended minimum is 10; lower values make stolen password hashes substantially cheaper to brute-force.
  • org.openrewrite.java.security.search.FindWeakPasswordHashing
    • Find weak password hashing
    • Finds uses of MessageDigest.getInstance() with algorithms unsuitable for password hashing (MD5, SHA-1, SHA-256, SHA-384, SHA-512). Passwords should be hashed with a purpose-built password hashing function such as bcrypt, scrypt, Argon2, or PBKDF2 that includes a salt and a tunable work factor.
  • org.openrewrite.java.security.search.FindWeakSpringPasswordEncoder
    • Find weak Spring Security password encoders
    • Finds uses of Spring Security password encoders that are unsuitable for production password storage: NoOpPasswordEncoder (plaintext), StandardPasswordEncoder (deprecated SHA-256), MessageDigestPasswordEncoder (raw message digest), Md4PasswordEncoder (MD4, broken), and LdapShaPasswordEncoder (deprecated). Use an adaptive function such as BCryptPasswordEncoder, Argon2PasswordEncoder, Pbkdf2PasswordEncoder, or SCryptPasswordEncoder instead.
  • org.openrewrite.java.security.search.FindXPathInjection
    • Find XPath injection vectors
    • Finds calls to XPath.evaluate() and XPath.compile() which, when the expression is built from user input, can allow XPath injection attacks. Use parameterized XPath expressions or input validation instead.
  • org.openrewrite.java.security.secrets.FindArtifactorySecrets
    • Find Artifactory secrets
    • Locates Artifactory secrets stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindAwsSecrets
    • Find AWS secrets
    • Locates AWS secrets stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindAzureSecrets
    • Find Azure secrets
    • Locates Azure secrets stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindBasicAuthSecrets
    • Find HTTP Basic authentication secrets
    • Locates HTTP Basic authentication credentials stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindBearerTokenSecrets
    • Find Bearer token secrets
    • Locates HTTP Bearer tokens (RFC 6750) stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindDiscordSecrets
    • Find Discord secrets
    • Locates Discord secrets stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindFacebookSecrets
    • Find Facebook secrets
    • Locates Facebook secrets stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindGenericSecrets
    • Find generic secrets
    • Locates generic secrets stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindGitHubSecrets
    • Find GitHub secrets
    • Locates GitHub secrets stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindGoogleSecrets
    • Find Google secrets
    • Locates Google secrets stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindHerokuSecrets
    • Find Heroku secrets
    • Locates Heroku secrets stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindJwtSecrets
    • Find JWT secrets
    • Locates JWTs stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindMailChimpSecrets
    • Find MailChimp secrets
    • Locates MailChimp secrets stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindMailgunSecrets
    • Find Mailgun secrets
    • Locates Mailgun secrets stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindNpmSecrets
    • Find NPM secrets
    • Locates NPM secrets stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindPasswordInUrlSecrets
    • Find passwords used in URLs
    • Locates URLs that contain passwords in plain text.
  • org.openrewrite.java.security.secrets.FindPayPalSecrets
    • Find PayPal secrets
    • Locates PayPal secrets stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindPgpSecrets
    • Find PGP secrets
    • Locates PGP secrets stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindPicaticSecrets
    • Find Picatic secrets
    • Locates Picatic secrets stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindRsaSecrets
    • Find RSA private keys
    • Locates RSA private keys stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindSecrets
    • Find secrets
    • Locates secrets stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindSecretsByPattern
    • Find secrets with regular expressions
    • A secret is a literal that matches any one of the provided patterns.
  • org.openrewrite.java.security.secrets.FindSendGridSecrets
    • Find SendGrid secrets
    • Locates SendGrid secrets stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindSlackSecrets
    • Find Slack secrets
    • Locates Slack secrets stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindSquareSecrets
    • Find Square secrets
    • Locates Square secrets stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindSshSecrets
    • Find SSH secrets
    • Locates SSH secrets stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindStripeSecrets
    • Find Stripe secrets
    • Locates Stripe secrets stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindTelegramSecrets
    • Find Telegram secrets
    • Locates Telegram secrets stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindTwilioSecrets
    • Find Twilio secrets
    • Locates Twilio secrets stored in plain text in code.
  • org.openrewrite.java.security.secrets.FindTwitterSecrets
    • Find Twitter secrets
    • Locates Twitter secrets stored in plain text in code.
  • org.openrewrite.java.security.servlet.CookieSetHttpOnly
    • Cookies missing HttpOnly flag
    • Check for use of cookies without the HttpOnly flag. Cookies should be marked as HttpOnly to prevent client-side scripts from accessing them, reducing the risk of cross-site scripting (XSS) attacks.
  • org.openrewrite.java.security.servlet.CookieSetSecure
    • Insecure cookies
    • Check for use of insecure cookies. Cookies should be marked as secure. This ensures that the cookie is sent only over HTTPS to prevent cross-site scripting attacks.
  • org.openrewrite.java.security.spring.CsrfProtection
    • Enable CSRF attack prevention
    • Cross-Site Request Forgery (CSRF) is a type of attack that occurs when a malicious web site, email, blog, instant message, or program causes a user's web browser to perform an unwanted action on a trusted site when the user is authenticated. See the full OWASP cheatsheet.
  • org.openrewrite.java.security.spring.InsecureSpringServiceExporter
    • Secure Spring service exporters
    • The default Java deserialization mechanism is available via ObjectInputStream class. This mechanism is known to be vulnerable. If an attacker can make an application deserialize malicious data, it may result in arbitrary code execution. Spring’s RemoteInvocationSerializingExporter uses the default Java deserialization mechanism to parse data. As a result, all classes that extend it are vulnerable to deserialization attacks. The Spring Framework contains at least HttpInvokerServiceExporter and SimpleHttpInvokerServiceExporter that extend RemoteInvocationSerializingExporter. These exporters parse data from the HTTP body using the unsafe Java deserialization mechanism. See the full blog post by Artem Smotrakov on CVE-2016-1000027 from which the above description is excerpted.
  • org.openrewrite.java.security.spring.PreventClickjacking
    • Prevent clickjacking
    • The frame-ancestors directive can be used in a Content-Security-Policy HTTP response header to indicate whether or not a browser should be allowed to render a page in a &lt;frame&gt; or &lt;iframe&gt;. Sites can use this to avoid Clickjacking attacks by ensuring that their content is not embedded into other sites.
  • org.openrewrite.java.security.spring.RemoveEnableWebSecurityDebug
    • Remove debug mode from Spring Security
    • Removes the debug attribute from @EnableWebSecurity annotations to prevent sensitive security information from being logged in production.
  • org.openrewrite.python.dependencies.DependencyVulnerabilityCheck
    • Find and fix vulnerable PyPI dependencies
    • This software composition analysis (SCA) tool detects and upgrades dependencies with publicly disclosed vulnerabilities. This recipe both generates a report of vulnerable dependencies and upgrades to newer versions with fixes. This recipe by default only upgrades to the latest patch version. If a minor or major upgrade is required to reach the fixed version, this can be controlled using the maximumUpgradeDelta option. Vulnerability information comes from the GitHub Security Advisory Database, which aggregates vulnerability data from several public databases, including the National Vulnerability Database maintained by the United States government. Dependencies following Semantic Versioning will see their patch version updated where applicable. ## Customizing Vulnerability Data This recipe can be customized by extending DependencyVulnerabilityCheckBase and overriding the vulnerability data sources: - baselineVulnerabilities(ExecutionContext ctx): Provides the default set of known vulnerabilities. The base implementation loads vulnerability data from the GitHub Security Advisory Database CSV file using ResourceUtils.parseResourceAsCsv(). Override this method to replace the entire vulnerability dataset with your own curated list. - supplementalVulnerabilities(ExecutionContext ctx): Allows adding custom vulnerability data beyond the baseline. The base implementation returns an empty list. Override this method to add organization-specific vulnerabilities, internal security advisories, or vulnerabilities from additional sources while retaining the baseline GitHub Advisory Database. Both methods return List&lt;Vulnerability&gt; objects. Vulnerability data can be loaded from CSV files using ResourceUtils.parseResourceAsCsv(path, Vulnerability.class, consumer) or constructed programmatically. To customize, extend DependencyVulnerabilityCheckBase and override one or both methods depending on your needs. For example, override supplementalVulnerabilities() to add custom CVEs while keeping the standard vulnerability database, or override baselineVulnerabilities() to use an entirely different vulnerability data source.
  • org.openrewrite.recipe.rewrite-java-security.InlineDeprecatedMethods
    • Inline deprecated delegating methods
    • Automatically generated recipes to inline deprecated method calls that delegate to other methods in the same class.
  • org.openrewrite.text.FindHardcodedLoopbackAddresses
    • Find hard-coded loopback IPv4 addresses
    • Locates mentions of hard-coded IPv4 addresses from the loopback IP range. The loopback IP range includes 127.0.0.0 to 127.255.255.255. This detects the entire localhost/loopback subnet range, not just the commonly used 127.0.0.1.
  • org.openrewrite.text.FindHardcodedPrivateIPAddresses
    • Find hard-coded private IPv4 addresses
    • Locates mentions of hard-coded IPv4 addresses from private IP ranges. Private IP ranges include: * 192.168.0.0 to 192.168.255.255 * 10.0.0.0 to 10.255.255.255 * 172.16.0.0 to 172.31.255.255 It is not detecting the localhost subnet 127.0.0.0 to 127.255.255.255.
  • org.openrewrite.text.RemoveHardcodedIPAddressesFromComments
    • Remove hard-coded IP addresses from comments
    • Removes hard-coded IPv4 addresses from comments when they match private IP ranges or loopback addresses. This targets IP addresses that are commented out in various comment formats: Private IP ranges: * 192.168.0.0 to 192.168.255.255 * 10.0.0.0 to 10.255.255.255 * 172.16.0.0 to 172.31.255.255 Loopback IP range: * 127.0.0.0 to 127.255.255.255 Supported comment formats: * C-style line comments (//) * C-style block comments (/* */) * Shell/Python style comments (#) * XML comments (&lt;!-- --&gt;) * YAML comments (#) * Properties file comments (# or !) For line comments, the entire line is removed. For block comments, only the IP address is removed.

rewrite-kubernetes

rewrite-migrate-kotlin

rewrite-migrate-python

rewrite-nodejs

rewrite-reactive-streams

rewrite-sql

rewrite-struts

rewrite-terraform