Netflix / dgs-framework

GraphQL for Java with Spring Boot made easy.
https://netflix.github.io/dgs
Apache License 2.0
3.09k stars 297 forks source link

bug: Suppression unsupported in Kotlin 2.0+ generated by the Kotlin codegen for some classes #2010

Open dmoidl opened 2 months ago

dmoidl commented 2 months ago

Expected behavior

No compilation issues are produced when compiling classes generated by the Kotlin codegen using Kotlin 2.0+

Actual behavior

This warning:

w: file:///Users/david.moidl/Productboard/pb-universe/board-module/board/build/generated/sources/dgs-codegen/com/productboard/board/graphql/generated/types/User.kt:20:13 This code uses error suppression for 'INAPPLICABLE_JVM_NAME'. While it might compile and work, the compiler behavior is UNSPECIFIED and WON'T BE PRESERVED. Please report your use case to the Kotlin issue tracker instead: https://kotl.in/issue

is issued for some of the classes generated via the DGS's Kotlin codegen.

Normally, I wouldn't worry too much about warnings being issued during compilation of a generated code. However, this warning seems to be intentionally scary as it communicates a possible compilation errors in the future.

Steps to reproduce

Generate any class representing a GQL type that implements a GQL interface via the Kotlin codegen. Any fields of the interface implemented by the class will get annotated with @Suppress("INAPPLICABLE_JVM_NAME"), triggering this error.

An excerpt from our GQL schema:

directive @key(fields: _FieldSet!) repeatable on OBJECT | INTERFACE
directive @external on FIELD_DEFINITION
directive @extends repeatable on OBJECT | INTERFACE

interface Node {
    id: ID!
}

interface Error {
    message: String!
}

type Team implements Node @key(fields: "id") @extends {
    id: ID! @external
}

type LocatedEntityNotFoundError implements Error {
    message: String!
}

And the corresponding generated classes:

Node.kt

@JsonTypeInfo(
  use = JsonTypeInfo.Id.NAME,
  include = JsonTypeInfo.As.PROPERTY,
  property = "__typename",
)
@JsonSubTypes(value = [
  // removed for brevity
])
public sealed interface Node {
  @Suppress("INAPPLICABLE_JVM_NAME")  // <-- this is the problem
  @get:JvmName("getId")
  public val id: String
}

Team.kt

@JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
@JsonDeserialize(builder = Team.Builder::class)
public class Team(
  id: () -> String = idDefault,
) : Node,
    RoadmapGroupingValue {
  private val __id: () -> String = id

  @Suppress("INAPPLICABLE_JVM_NAME")  // <-- this is the problem
  @get:JvmName("getId")
  override val id: String
    get() = __id.invoke()

  public companion object {
    private val idDefault: () -> String = 
        { throw IllegalStateException("Field `id` was not requested") }
  }

  @JsonPOJOBuilder
  @JsonIgnoreProperties("__typename")
  public class Builder {
    private var id: () -> String = idDefault

    @JsonProperty("id")
    public fun withId(id: String): Builder = this.apply {
      this.id = { id }
    }

    public fun build(): Team = Team(
      id = id,
    )
  }
}

Error.kt

@JsonTypeInfo(
  use = JsonTypeInfo.Id.NAME,
  include = JsonTypeInfo.As.PROPERTY,
  property = "__typename",
)
@JsonSubTypes(value = [
  // removed for brevity
])
public sealed interface Error {
  @Suppress("INAPPLICABLE_JVM_NAME")  // <-- this is the problem
  @get:JvmName("getMessage")
  public val message: String
}

LocatedEntityNotFoundError

@JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
@JsonDeserialize(builder = LocatedEntityNotFoundError.Builder::class)
public class LocatedEntityNotFoundError(
  message: () -> String = messageDefault,
) : Error,
    GridRowConnectionError {
  private val __message: () -> String = message

  @Suppress("INAPPLICABLE_JVM_NAME")  // <-- this is the problem
  @get:JvmName("getMessage")
  override val message: String
    get() = __message.invoke()

  public companion object {
    private val messageDefault: () -> String = 
        { throw IllegalStateException("Field `message` was not requested") }
  }

  @JsonPOJOBuilder
  @JsonIgnoreProperties("__typename")
  public class Builder {
    private var message: () -> String = messageDefault

    @JsonProperty("message")
    public fun withMessage(message: String): Builder = this.apply {
      this.message = { message }
    }

    public fun build(): LocatedEntityNotFoundError = LocatedEntityNotFoundError(
      message = message,
    )
  }
}

Notes

Please note that I understand the Kotlin codegen is still marked as experimental. However, with the recent release of Kotlin 2.0, this looks like a possible incompatibility that I believe should be addressed to keep the project up-to-date with the latest language version.

paulbakker commented 4 hours ago

@mbossenbroek Could you have a look at this?