Skip to content

Commit 5e92d19

Browse files
authored
Add new diagnostic for providing intrinsic wrapped types (#2190)
1 parent e48699e commit 5e92d19

16 files changed

Lines changed: 317 additions & 12 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Changelog
77
### New
88

99
- Add a new `desugaredProviderSeverity` option (default: `WARN`) that reports a diagnostic when `Provider<T>` is used instead of the preferred `() -> T` form. Set this to `NONE` to disable the warning during migration, or `ERROR` to enforce the new style. Automatically treated as `NONE` when `enableFunctionProviders` is disabled.
10+
- **[FIR]** Add diagnostic checks against providing intrinsic types (`Provider`, `Lazy`, etc.) from `@Provides` declarations.
1011
- **[Gradle]** Introduce a new `compilerOptions {}` DSL for free Metro compiler options and flags.
1112
- **[Gradle]** Add `IDE_WARN` and `IDE_ERROR` members to `DiagnosticSeverity` to allow configuring some diagnostics to _only_ run in IDE sessions. Useful for diagnostics you only want to surface to readers in the IDE without emitting compiler warnings in real (CLI) compilations.
1213

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/DependencyGraphFactory_ProvidesParamCannotBeIntrinsic.kt:(579,595): error: `@Provides` graph factory parameters may not be intrinsic types, but `string` is `Provider<Double>`. Remove the wrapper and let Metro handle the underlying type directly.
2+
3+
/DependencyGraphFactory_ProvidesParamCannotBeIntrinsic.kt:(625,634): error: `@Provides` graph factory parameters may not be intrinsic types, but `lazyTarget` is `Lazy<Int>`. Remove the wrapper and let Metro handle the underlying type directly.
4+
5+
/DependencyGraphFactory_ProvidesParamCannotBeIntrinsic.kt:(661,673): error: `@Provides` graph factory parameters may not be intrinsic types, but `factory` is `() -> String`. Remove the wrapper and let Metro handle the underlying type directly. Note: `enableFunctionProviders` is enabled, so parameter-less Kotlin function literal types are treated as provider types by Metro and cannot be unique bindings on the graph.
6+
7+
/DependencyGraphFactory_ProvidesParamCannotBeIntrinsic.kt:(725,735): error: `@BindsInstance` graph factory parameters may not be intrinsic types, but `bindsInstanceFactory` is `() -> Long`. Remove the wrapper and let Metro handle the underlying type directly. Note: `enableFunctionProviders` is enabled, so parameter-less Kotlin function literal types are treated as provider types by Metro and cannot be unique bindings on the graph.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RENDER_DIAGNOSTICS_FULL_TEXT
2+
// ENABLE_DAGGER_INTEROP
3+
4+
// `@Provides` on a @DependencyGraph.Factory parameter is Metro's analog to Dagger's
5+
// `@BindsInstance`. Passing a pre-wrapped intrinsic type silently double-wraps the binding,
6+
// so intrinsic parameter types are rejected the same way as intrinsic return types on
7+
// `@Provides`/`@Binds` declarations.
8+
9+
class Target
10+
11+
@DependencyGraph
12+
interface ExampleGraph {
13+
val string: String
14+
val target: Target
15+
16+
@DependencyGraph.Factory
17+
interface Factory {
18+
fun create(
19+
@Provides string: <!INTRINSIC_BINDING_ERROR!>Provider<Double><!>,
20+
@Provides lazyTarget: <!INTRINSIC_BINDING_ERROR!>Lazy<Int><!>,
21+
@Provides factory: <!INTRINSIC_BINDING_ERROR!>() -> String<!>,
22+
@dagger.BindsInstance bindsInstanceFactory: <!INTRINSIC_BINDING_ERROR!>() -> Long<!>,
23+
): ExampleGraph
24+
}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/GraphExtensionFactory_ProvidesParamCannotBeIntrinsic.kt:(519,535): error: `@Provides` graph factory parameters may not be intrinsic types, but `string` is `Provider<Double>`. Remove the wrapper and let Metro handle the underlying type directly.
2+
3+
/GraphExtensionFactory_ProvidesParamCannotBeIntrinsic.kt:(565,574): error: `@Provides` graph factory parameters may not be intrinsic types, but `lazyTarget` is `Lazy<Int>`. Remove the wrapper and let Metro handle the underlying type directly.
4+
5+
/GraphExtensionFactory_ProvidesParamCannotBeIntrinsic.kt:(601,613): error: `@Provides` graph factory parameters may not be intrinsic types, but `factory` is `() -> String`. Remove the wrapper and let Metro handle the underlying type directly. Note: `enableFunctionProviders` is enabled, so parameter-less Kotlin function literal types are treated as provider types by Metro and cannot be unique bindings on the graph.
6+
7+
/GraphExtensionFactory_ProvidesParamCannotBeIntrinsic.kt:(665,675): error: `@BindsInstance` graph factory parameters may not be intrinsic types, but `bindsInstanceFactory` is `() -> Long`. Remove the wrapper and let Metro handle the underlying type directly. Note: `enableFunctionProviders` is enabled, so parameter-less Kotlin function literal types are treated as provider types by Metro and cannot be unique bindings on the graph.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// RENDER_DIAGNOSTICS_FULL_TEXT
2+
// ENABLE_DAGGER_INTEROP
3+
4+
// Same rule applies to @GraphExtension.Factory `@Provides` parameters as to
5+
// @DependencyGraph.Factory parameters — intrinsic parameter types silently double-wrap
6+
// and are rejected.
7+
8+
class Target
9+
10+
@DependencyGraph
11+
interface ParentGraph : ChildGraph.Factory
12+
13+
@GraphExtension
14+
interface ChildGraph {
15+
val string: String
16+
val target: Target
17+
18+
@GraphExtension.Factory
19+
interface Factory {
20+
fun create(
21+
@Provides string: <!INTRINSIC_BINDING_ERROR!>Provider<Double><!>,
22+
@Provides lazyTarget: <!INTRINSIC_BINDING_ERROR!>Lazy<Int><!>,
23+
@Provides factory: <!INTRINSIC_BINDING_ERROR!>() -> String<!>,
24+
@dagger.BindsInstance bindsInstanceFactory: <!INTRINSIC_BINDING_ERROR!>() -> Long<!>,
25+
): ChildGraph
26+
}
27+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// RENDER_DIAGNOSTICS_FULL_TEXT
2+
3+
// @ElementsIntoSet declarations may return Set<Provider<T>> — the return type itself is
4+
// Set<...>, not an intrinsic, so the check naturally passes. Also negative coverage for
5+
// user-defined subtypes of Provider, which should not be caught by the identity-based check.
6+
7+
class MyProvider : Provider<String> { override fun invoke(): String = "" }
8+
9+
@DependencyGraph
10+
interface ExampleGraph {
11+
val strings: Set<Provider<String>>
12+
val myProvider: MyProvider
13+
14+
@Provides @ElementsIntoSet fun provideStrings(): Set<Provider<String>> =
15+
setOf(provider { "a" }, provider { "b" })
16+
17+
// User-defined subclass of Provider is allowed (identity check, no supertype walk).
18+
@Provides fun provideMyProvider(): MyProvider = MyProvider()
19+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/Provides_ReturnTypeCannotBeIntrinsic.kt:(391,407): error: `@Provides` declarations may not return intrinsic types, but `provideProvider` returns `Provider<String>`. Remove the wrapper and let Metro provide the underlying type directly.
2+
3+
/Provides_ReturnTypeCannotBeIntrinsic.kt:(515,536): error: `@Provides` declarations may not return intrinsic types, but `provideJavaxProvider` returns `Provider<String>`. Remove the wrapper and let Metro provide the underlying type directly.
4+
5+
/Provides_ReturnTypeCannotBeIntrinsic.kt:(653,676): error: `@Provides` declarations may not return intrinsic types, but `provideJakartaProvider` returns `Provider<String>`. Remove the wrapper and let Metro provide the underlying type directly.
6+
7+
/Provides_ReturnTypeCannotBeIntrinsic.kt:(772,784): error: `@Provides` declarations may not return intrinsic types, but `provideLazy` returns `Lazy<String>`. Remove the wrapper and let Metro provide the underlying type directly.
8+
9+
/Provides_ReturnTypeCannotBeIntrinsic.kt:(876,893): error: `@Provides` declarations may not return intrinsic types, but `provideNullable` returns `Provider<String>?`. Remove the wrapper and let Metro provide the underlying type directly.
10+
11+
/Provides_ReturnTypeCannotBeIntrinsic.kt:(996,1011): error: `@Provides` declarations may not return intrinsic types, but `provideAliased` returns `Provider<String>`. Remove the wrapper and let Metro provide the underlying type directly.
12+
13+
/Provides_ReturnTypeCannotBeIntrinsic.kt:(1156,1168): error: `@Provides` declarations may not return intrinsic types, but `provideFunction` returns `() -> String`. Remove the wrapper and let Metro provide the underlying type directly. Note: `enableFunctionProviders` is enabled, so parameter-less Kotlin function types are treated as provider types by Metro and cannot be unique bindings on the graph.
14+
15+
/Provides_ReturnTypeCannotBeIntrinsic.kt:(1353,1369): error: `@Provides` declarations may not return intrinsic types, but `provideString` returns `Provider<String>`. Remove the wrapper and let Metro provide the underlying type directly.
16+
17+
/Provides_ReturnTypeCannotBeIntrinsic.kt:(1580,1596): error: `@Provides` declarations may not return intrinsic types, but `provideString` returns `Provider<String>`. Remove the wrapper and let Metro provide the underlying type directly.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// RENDER_DIAGNOSTICS_FULL_TEXT
2+
// ENABLE_DAGGER_INTEROP
3+
4+
import javax.inject.Provider as JavaxProvider
5+
import jakarta.inject.Provider as JakartaProvider
6+
7+
typealias AliasedProvider = Provider<String>
8+
9+
class MyClass
10+
11+
interface ExampleGraph {
12+
val string: String
13+
val myClass: MyClass
14+
15+
// Metro Provider is an intrinsic type
16+
@Provides fun provideProvider(): <!INTRINSIC_BINDING_ERROR!>Provider<String><!> = provider { "" }
17+
18+
// javax.inject.Provider is an intrinsic type
19+
@Provides fun provideJavaxProvider(): <!INTRINSIC_BINDING_ERROR!>JavaxProvider<String><!> = JavaxProvider { "" }
20+
21+
// jakarta.inject.Provider is an intrinsic type
22+
@Provides fun provideJakartaProvider(): <!INTRINSIC_BINDING_ERROR!>JakartaProvider<String><!> = JakartaProvider { "" }
23+
24+
// kotlin.Lazy is an intrinsic type
25+
@Provides fun provideLazy(): <!INTRINSIC_BINDING_ERROR!>Lazy<String><!> = lazy { "" }
26+
27+
// Nullable wrappers are also rejected
28+
@Provides fun provideNullable(): <!INTRINSIC_BINDING_ERROR!>Provider<String>?<!> = null
29+
30+
// Type aliases that resolve to an intrinsic are rejected
31+
@Provides fun provideAliased(): <!INTRINSIC_BINDING_ERROR!>AliasedProvider<!> = provider { "" }
32+
33+
// With `enableFunctionProviders` enabled (the default), Function0 is an intrinsic type
34+
@Provides fun provideFunction(): <!INTRINSIC_BINDING_ERROR!>() -> String<!> = { "" }
35+
}
36+
37+
// Companion object @Provides is also checked
38+
@DependencyGraph
39+
interface GraphWithCompanion {
40+
val string: String
41+
42+
companion object {
43+
@Provides fun provideString(): <!INTRINSIC_BINDING_ERROR!>Provider<String><!> = provider { "" }
44+
}
45+
}
46+
47+
// @IntoSet / @IntoMap contributions returning an intrinsic are rejected — there's no
48+
// multibinding carve-out.
49+
interface MultibindingGraph {
50+
@Provides @IntoSet fun provideString(): <!INTRINSIC_BINDING_ERROR!>Provider<String><!> = provider { "" }
51+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/Provides_ReturnTypeCannotBeIntrinsic_DaggerInterop.kt:(296,314): error: `@Provides` declarations may not return intrinsic types, but `provideLazy` returns `Lazy<String>`. Remove the wrapper and let Metro provide the underlying type directly.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// RENDER_DIAGNOSTICS_FULL_TEXT
2+
// ENABLE_DAGGER_INTEROP
3+
4+
import dagger.Lazy as DaggerLazy
5+
6+
interface ExampleGraph {
7+
val string: String
8+
9+
// dagger.Lazy is an intrinsic type (under Dagger runtime interop it's treated as kotlin.Lazy)
10+
@Provides fun provideLazy(): <!INTRINSIC_BINDING_ERROR!>DaggerLazy<String><!> = DaggerLazy { "" }
11+
}

0 commit comments

Comments
 (0)