diff --git a/libraries/apollo-ast/api/apollo-ast.api b/libraries/apollo-ast/api/apollo-ast.api index f851273d87d..eefe3bc586c 100644 --- a/libraries/apollo-ast/api/apollo-ast.api +++ b/libraries/apollo-ast/api/apollo-ast.api @@ -132,6 +132,12 @@ public final class com/apollographql/apollo/ast/ForeignSchema { public final fun getVersion ()Ljava/lang/String; } +public final class com/apollographql/apollo/ast/FragmentCycle : com/apollographql/apollo/ast/GraphQLValidationIssue { + public fun (Ljava/lang/String;Lcom/apollographql/apollo/ast/SourceLocation;)V + public fun getMessage ()Ljava/lang/String; + public fun getSourceLocation ()Lcom/apollographql/apollo/ast/SourceLocation; +} + public final class com/apollographql/apollo/ast/GQLArgument : com/apollographql/apollo/ast/GQLNamed, com/apollographql/apollo/ast/GQLNode { public fun (Lcom/apollographql/apollo/ast/SourceLocation;Ljava/lang/String;Lcom/apollographql/apollo/ast/GQLValue;)V public synthetic fun (Lcom/apollographql/apollo/ast/SourceLocation;Ljava/lang/String;Lcom/apollographql/apollo/ast/GQLValue;ILkotlin/jvm/internal/DefaultConstructorMarker;)V diff --git a/libraries/apollo-ast/api/apollo-ast.klib.api b/libraries/apollo-ast/api/apollo-ast.klib.api index 76e8c437fbe..7ca166edc20 100644 --- a/libraries/apollo-ast/api/apollo-ast.klib.api +++ b/libraries/apollo-ast/api/apollo-ast.klib.api @@ -238,6 +238,15 @@ final class com.apollographql.apollo.ast/ForeignSchema { // com.apollographql.ap final fun (): kotlin/String // com.apollographql.apollo.ast/ForeignSchema.version.|(){}[0] } +final class com.apollographql.apollo.ast/FragmentCycle : com.apollographql.apollo.ast/GraphQLValidationIssue { // com.apollographql.apollo.ast/FragmentCycle|null[0] + constructor (kotlin/String, com.apollographql.apollo.ast/SourceLocation?) // com.apollographql.apollo.ast/FragmentCycle.|(kotlin.String;com.apollographql.apollo.ast.SourceLocation?){}[0] + + final val message // com.apollographql.apollo.ast/FragmentCycle.message|{}message[0] + final fun (): kotlin/String // com.apollographql.apollo.ast/FragmentCycle.message.|(){}[0] + final val sourceLocation // com.apollographql.apollo.ast/FragmentCycle.sourceLocation|{}sourceLocation[0] + final fun (): com.apollographql.apollo.ast/SourceLocation? // com.apollographql.apollo.ast/FragmentCycle.sourceLocation.|(){}[0] +} + final class com.apollographql.apollo.ast/GQLArgument : com.apollographql.apollo.ast/GQLNamed, com.apollographql.apollo.ast/GQLNode { // com.apollographql.apollo.ast/GQLArgument|null[0] constructor (com.apollographql.apollo.ast/SourceLocation? = ..., kotlin/String, com.apollographql.apollo.ast/GQLValue) // com.apollographql.apollo.ast/GQLArgument.|(com.apollographql.apollo.ast.SourceLocation?;kotlin.String;com.apollographql.apollo.ast.GQLValue){}[0] diff --git a/libraries/apollo-ast/src/commonMain/kotlin/com/apollographql/apollo/ast/Issue.kt b/libraries/apollo-ast/src/commonMain/kotlin/com/apollographql/apollo/ast/Issue.kt index cdbbb416ce2..22ce805b580 100644 --- a/libraries/apollo-ast/src/commonMain/kotlin/com/apollographql/apollo/ast/Issue.kt +++ b/libraries/apollo-ast/src/commonMain/kotlin/com/apollographql/apollo/ast/Issue.kt @@ -70,6 +70,13 @@ class DifferentShape(override val message: String, override val sourceLocation: class UnusedFragment(override val message: String, override val sourceLocation: SourceLocation?) : GraphQLValidationIssue +/** + * There is a fragment cycle. + * + * See https://spec.graphql.org/September2025/#sec-Fragment-Spreads-Must-Not-Form-Cycles + */ +class FragmentCycle(override val message: String, override val sourceLocation: SourceLocation?) : GraphQLValidationIssue + /** * Two type definitions have the same name */ diff --git a/libraries/apollo-ast/src/commonMain/kotlin/com/apollographql/apollo/ast/internal/ExecutableValidationScope.kt b/libraries/apollo-ast/src/commonMain/kotlin/com/apollographql/apollo/ast/internal/ExecutableValidationScope.kt index e2a38019dba..c663dd2d80c 100644 --- a/libraries/apollo-ast/src/commonMain/kotlin/com/apollographql/apollo/ast/internal/ExecutableValidationScope.kt +++ b/libraries/apollo-ast/src/commonMain/kotlin/com/apollographql/apollo/ast/internal/ExecutableValidationScope.kt @@ -6,6 +6,7 @@ import com.apollographql.apollo.ast.Catch import com.apollographql.apollo.ast.DeprecatedUsage import com.apollographql.apollo.ast.DifferentShape import com.apollographql.apollo.ast.ExecutableValidationResult +import com.apollographql.apollo.ast.FragmentCycle import com.apollographql.apollo.ast.GQLArgument import com.apollographql.apollo.ast.GQLBooleanValue import com.apollographql.apollo.ast.GQLDirective @@ -117,7 +118,7 @@ internal class ExecutableValidationScope( val index = path.indexOf("__${name}") val nextPath = path + "__${fragment.name}" if (index != -1) { - registerIssue("Fragment '$name' spreads itself, creating a cycle at '${nextPath.subList(index, nextPath.size).joinToString(".")}'", it.sourceLocation) + issues.add(FragmentCycle("Fragment '$name' spreads itself, creating a cycle at '${nextPath.subList(index, nextPath.size).joinToString(".")}'", it.sourceLocation)) cyclicFragments.add(name) return@forEach } diff --git a/libraries/apollo-compiler/src/test/validation/operation/CircularFragmentReference.expected b/libraries/apollo-compiler/src/test/validation/operation/CircularFragmentReference.expected index 67ca67a926e..585b90d968d 100644 --- a/libraries/apollo-compiler/src/test/validation/operation/CircularFragmentReference.expected +++ b/libraries/apollo-compiler/src/test/validation/operation/CircularFragmentReference.expected @@ -1,2 +1,2 @@ -OtherValidationIssue (14:3) +FragmentCycle (14:3) Fragment 'characterFragment' spreads itself, creating a cycle at '__characterFragment.friend.__friendFragment.__characterFragment' \ No newline at end of file