From 0dad5260fdb94e8496d7030f3d5033dfb799f0db Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 25 Jan 2026 06:36:26 +0000 Subject: [PATCH 1/8] Initial plan From b63b21ccd47edf3d2ab23cc5342397f0ed1d176b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 25 Jan 2026 06:44:02 +0000 Subject: [PATCH 2/8] Add JSpecify annotations to 10 language classes - Added @NullMarked to ImplementingTypeDefinition interface - Added @NullMarked to InlineFragment with @Nullable typeCondition - Added @NullMarked to InputObjectTypeDefinition with @NullUnmarked Builder - Added @NullMarked to InputObjectTypeExtensionDefinition with @NullUnmarked Builder - Added @NullMarked to InputValueDefinition with @Nullable defaultValue - Added @NullMarked to InterfaceTypeDefinition with @NullUnmarked Builder - Added @NullMarked to InterfaceTypeExtensionDefinition with @NullUnmarked Builder - Added @NullMarked to ListType with @NullUnmarked Builder - Added @NullMarked to NodeDirectivesBuilder interface - Added @NullMarked to NodeParentTree with @Nullable parent - Fixed deepCopy methods with assertNotNull for non-null fields - Fixed InlineFragment constructor to provide default SelectionSet - Fixed Anonymizer to handle nullable typeCondition - Removed completed classes from JSpecify exemption list Co-authored-by: dondonz <13839920+dondonz@users.noreply.github.com> --- .../language/ImplementingTypeDefinition.java | 2 ++ .../java/graphql/language/InlineFragment.java | 25 +++++++++++-------- .../language/InputObjectTypeDefinition.java | 15 +++++++---- .../InputObjectTypeExtensionDefinition.java | 13 +++++++--- .../language/InputValueDefinition.java | 23 ++++++++++------- .../language/InterfaceTypeDefinition.java | 17 ++++++++----- .../InterfaceTypeExtensionDefinition.java | 13 +++++++--- src/main/java/graphql/language/ListType.java | 11 +++++--- .../language/NodeDirectivesBuilder.java | 2 ++ .../java/graphql/language/NodeParentTree.java | 5 +++- src/main/java/graphql/util/Anonymizer.java | 10 +++++--- .../archunit/JSpecifyAnnotationsCheck.groovy | 10 -------- 12 files changed, 91 insertions(+), 55 deletions(-) diff --git a/src/main/java/graphql/language/ImplementingTypeDefinition.java b/src/main/java/graphql/language/ImplementingTypeDefinition.java index 31effe0d11..405ee1ccfa 100644 --- a/src/main/java/graphql/language/ImplementingTypeDefinition.java +++ b/src/main/java/graphql/language/ImplementingTypeDefinition.java @@ -2,6 +2,7 @@ import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; import java.util.List; @@ -11,6 +12,7 @@ * @param for two */ @PublicApi +@NullMarked public interface ImplementingTypeDefinition extends TypeDefinition { List getImplements(); diff --git a/src/main/java/graphql/language/InlineFragment.java b/src/main/java/graphql/language/InlineFragment.java index b065a5b8df..173427071e 100644 --- a/src/main/java/graphql/language/InlineFragment.java +++ b/src/main/java/graphql/language/InlineFragment.java @@ -7,6 +7,9 @@ import graphql.collect.ImmutableKit; import graphql.util.TraversalControl; import graphql.util.TraverserContext; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -20,8 +23,9 @@ import static graphql.language.NodeChildrenContainer.newNodeChildrenContainer; @PublicApi +@NullMarked public class InlineFragment extends AbstractNode implements Selection, SelectionSetContainer, DirectivesContainer { - private final TypeName typeCondition; + private final @Nullable TypeName typeCondition; private final NodeUtil.DirectivesHolder directives; private final SelectionSet selectionSet; @@ -30,10 +34,10 @@ public class InlineFragment extends AbstractNode implements Sele public static final String CHILD_SELECTION_SET = "selectionSet"; @Internal - protected InlineFragment(TypeName typeCondition, + protected InlineFragment(@Nullable TypeName typeCondition, List directives, SelectionSet selectionSet, - SourceLocation sourceLocation, + @Nullable SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { @@ -48,8 +52,8 @@ protected InlineFragment(TypeName typeCondition, * * @param typeCondition the type condition of the inline fragment */ - public InlineFragment(TypeName typeCondition) { - this(typeCondition, emptyList(), null, null, emptyList(), IgnoredChars.EMPTY, emptyMap()); + public InlineFragment(@Nullable TypeName typeCondition) { + this(typeCondition, emptyList(), SelectionSet.newSelectionSet().build(), null, emptyList(), IgnoredChars.EMPTY, emptyMap()); } /** @@ -58,11 +62,11 @@ public InlineFragment(TypeName typeCondition) { * @param typeCondition the type condition of the inline fragment * @param selectionSet of the inline fragment */ - public InlineFragment(TypeName typeCondition, SelectionSet selectionSet) { + public InlineFragment(@Nullable TypeName typeCondition, SelectionSet selectionSet) { this(typeCondition, emptyList(), selectionSet, null, emptyList(), IgnoredChars.EMPTY, emptyMap()); } - public TypeName getTypeCondition() { + public @Nullable TypeName getTypeCondition() { return typeCondition; } @@ -121,7 +125,7 @@ public InlineFragment withNewChildren(NodeChildrenContainer newChildren) { } @Override - public boolean isEqualTo(Node o) { + public boolean isEqualTo(@Nullable Node o) { if (this == o) { return true; } @@ -132,8 +136,8 @@ public boolean isEqualTo(Node o) { public InlineFragment deepCopy() { return new InlineFragment( deepCopy(typeCondition), - deepCopy(directives.getDirectives()), - deepCopy(selectionSet), + assertNotNull(deepCopy(directives.getDirectives()), "directives cannot be null"), + assertNotNull(deepCopy(selectionSet), "selectionSet cannot be null"), getSourceLocation(), getComments(), getIgnoredChars(), @@ -164,6 +168,7 @@ public InlineFragment transform(Consumer builderConsumer) { return builder.build(); } + @NullUnmarked public static final class Builder implements NodeDirectivesBuilder { private SourceLocation sourceLocation; private ImmutableList comments = emptyList(); diff --git a/src/main/java/graphql/language/InputObjectTypeDefinition.java b/src/main/java/graphql/language/InputObjectTypeDefinition.java index d545814091..ce1a9c7425 100644 --- a/src/main/java/graphql/language/InputObjectTypeDefinition.java +++ b/src/main/java/graphql/language/InputObjectTypeDefinition.java @@ -7,6 +7,9 @@ import graphql.util.FpKit; import graphql.util.TraversalControl; import graphql.util.TraverserContext; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.util.LinkedHashMap; import java.util.List; @@ -19,6 +22,7 @@ import static graphql.language.NodeChildrenContainer.newNodeChildrenContainer; @PublicApi +@NullMarked public class InputObjectTypeDefinition extends AbstractDescribedNode implements TypeDefinition, DirectivesContainer, NamedNode { private final String name; @@ -32,8 +36,8 @@ public class InputObjectTypeDefinition extends AbstractDescribedNode directives, List inputValueDefinitions, - Description description, - SourceLocation sourceLocation, + @Nullable Description description, + @Nullable SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { @@ -94,7 +98,7 @@ public InputObjectTypeDefinition withNewChildren(NodeChildrenContainer newChildr } @Override - public boolean isEqualTo(Node o) { + public boolean isEqualTo(@Nullable Node o) { if (this == o) { return true; } @@ -110,8 +114,8 @@ public boolean isEqualTo(Node o) { @Override public InputObjectTypeDefinition deepCopy() { return new InputObjectTypeDefinition(name, - deepCopy(directives.getDirectives()), - deepCopy(inputValueDefinitions), + assertNotNull(deepCopy(directives.getDirectives()), "directives cannot be null"), + assertNotNull(deepCopy(inputValueDefinitions), "inputValueDefinitions cannot be null"), description, getSourceLocation(), getComments(), @@ -144,6 +148,7 @@ public InputObjectTypeDefinition transform(Consumer builderConsumer) { return builder.build(); } + @NullUnmarked public static final class Builder implements NodeDirectivesBuilder { private SourceLocation sourceLocation; private ImmutableList comments = emptyList(); diff --git a/src/main/java/graphql/language/InputObjectTypeExtensionDefinition.java b/src/main/java/graphql/language/InputObjectTypeExtensionDefinition.java index 0835c0eb44..76c1b4055c 100644 --- a/src/main/java/graphql/language/InputObjectTypeExtensionDefinition.java +++ b/src/main/java/graphql/language/InputObjectTypeExtensionDefinition.java @@ -4,6 +4,9 @@ import graphql.Internal; import graphql.PublicApi; import graphql.collect.ImmutableKit; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.util.LinkedHashMap; import java.util.List; @@ -14,14 +17,15 @@ import static graphql.collect.ImmutableKit.emptyList; @PublicApi +@NullMarked public class InputObjectTypeExtensionDefinition extends InputObjectTypeDefinition implements SDLExtensionDefinition { @Internal protected InputObjectTypeExtensionDefinition(String name, List directives, List inputValueDefinitions, - Description description, - SourceLocation sourceLocation, + @Nullable Description description, + @Nullable SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { @@ -31,8 +35,8 @@ protected InputObjectTypeExtensionDefinition(String name, @Override public InputObjectTypeExtensionDefinition deepCopy() { return new InputObjectTypeExtensionDefinition(getName(), - deepCopy(getDirectives()), - deepCopy(getInputValueDefinitions()), + assertNotNull(deepCopy(getDirectives()), "directives cannot be null"), + assertNotNull(deepCopy(getInputValueDefinitions()), "inputValueDefinitions cannot be null"), getDescription(), getSourceLocation(), getComments(), @@ -67,6 +71,7 @@ public InputObjectTypeExtensionDefinition transformExtension(Consumer b return builder.build(); } + @NullUnmarked public static final class Builder implements NodeDirectivesBuilder { private SourceLocation sourceLocation; private ImmutableList comments = emptyList(); diff --git a/src/main/java/graphql/language/InputValueDefinition.java b/src/main/java/graphql/language/InputValueDefinition.java index c0db3318fa..0c2ca489b6 100644 --- a/src/main/java/graphql/language/InputValueDefinition.java +++ b/src/main/java/graphql/language/InputValueDefinition.java @@ -7,6 +7,9 @@ import graphql.collect.ImmutableKit; import graphql.util.TraversalControl; import graphql.util.TraverserContext; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -21,10 +24,11 @@ import static graphql.language.NodeChildrenContainer.newNodeChildrenContainer; @PublicApi +@NullMarked public class InputValueDefinition extends AbstractDescribedNode implements DirectivesContainer, NamedNode { private final String name; private final Type type; - private final Value defaultValue; + private final @Nullable Value defaultValue; private final NodeUtil.DirectivesHolder directives; public static final String CHILD_TYPE = "type"; @@ -34,10 +38,10 @@ public class InputValueDefinition extends AbstractDescribedNode directives, - Description description, - SourceLocation sourceLocation, + @Nullable Description description, + @Nullable SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { @@ -70,7 +74,7 @@ public InputValueDefinition(String name, public InputValueDefinition(String name, Type type, - Value defaultValue) { + @Nullable Value defaultValue) { this(name, type, defaultValue, emptyList(), null, null, emptyList(), IgnoredChars.EMPTY, emptyMap()); } @@ -84,7 +88,7 @@ public String getName() { return name; } - public Value getDefaultValue() { + public @Nullable Value getDefaultValue() { return defaultValue; } @@ -139,7 +143,7 @@ public InputValueDefinition withNewChildren(NodeChildrenContainer newChildren) { } @Override - public boolean isEqualTo(Node o) { + public boolean isEqualTo(@Nullable Node o) { if (this == o) { return true; } @@ -155,9 +159,9 @@ public boolean isEqualTo(Node o) { @Override public InputValueDefinition deepCopy() { return new InputValueDefinition(name, - deepCopy(type), + assertNotNull(deepCopy(type), "type cannot be null"), deepCopy(defaultValue), - deepCopy(directives.getDirectives()), + assertNotNull(deepCopy(directives.getDirectives()), "directives cannot be null"), description, getSourceLocation(), getComments(), @@ -190,6 +194,7 @@ public InputValueDefinition transform(Consumer builderConsumer) { return builder.build(); } + @NullUnmarked public static final class Builder implements NodeDirectivesBuilder { private SourceLocation sourceLocation; private ImmutableList comments = emptyList(); diff --git a/src/main/java/graphql/language/InterfaceTypeDefinition.java b/src/main/java/graphql/language/InterfaceTypeDefinition.java index 3fd1343f89..64f956e1ce 100644 --- a/src/main/java/graphql/language/InterfaceTypeDefinition.java +++ b/src/main/java/graphql/language/InterfaceTypeDefinition.java @@ -7,6 +7,9 @@ import graphql.collect.ImmutableKit; import graphql.util.TraversalControl; import graphql.util.TraverserContext; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -21,6 +24,7 @@ import static graphql.language.NodeChildrenContainer.newNodeChildrenContainer; @PublicApi +@NullMarked public class InterfaceTypeDefinition extends AbstractDescribedNode implements ImplementingTypeDefinition, DirectivesContainer, NamedNode { private final String name; @@ -37,8 +41,8 @@ protected InterfaceTypeDefinition(String name, List implementz, List definitions, List directives, - Description description, - SourceLocation sourceLocation, + @Nullable Description description, + @Nullable SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { @@ -121,7 +125,7 @@ public InterfaceTypeDefinition withNewChildren(NodeChildrenContainer newChildren } @Override - public boolean isEqualTo(Node o) { + public boolean isEqualTo(@Nullable Node o) { if (this == o) { return true; } @@ -137,9 +141,9 @@ public boolean isEqualTo(Node o) { @Override public InterfaceTypeDefinition deepCopy() { return new InterfaceTypeDefinition(name, - deepCopy(implementz), - deepCopy(definitions), - deepCopy(directives.getDirectives()), + assertNotNull(deepCopy(implementz), "implementz cannot be null"), + assertNotNull(deepCopy(definitions), "definitions cannot be null"), + assertNotNull(deepCopy(directives.getDirectives()), "directives cannot be null"), description, getSourceLocation(), getComments(), @@ -173,6 +177,7 @@ public InterfaceTypeDefinition transform(Consumer builderConsumer) { return builder.build(); } + @NullUnmarked public static final class Builder implements NodeDirectivesBuilder { private SourceLocation sourceLocation; private ImmutableList comments = emptyList(); diff --git a/src/main/java/graphql/language/InterfaceTypeExtensionDefinition.java b/src/main/java/graphql/language/InterfaceTypeExtensionDefinition.java index 2de917065b..6c5eb2bf4e 100644 --- a/src/main/java/graphql/language/InterfaceTypeExtensionDefinition.java +++ b/src/main/java/graphql/language/InterfaceTypeExtensionDefinition.java @@ -4,6 +4,9 @@ import graphql.Internal; import graphql.PublicApi; import graphql.collect.ImmutableKit; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.util.LinkedHashMap; import java.util.List; @@ -14,6 +17,7 @@ import static graphql.collect.ImmutableKit.emptyList; @PublicApi +@NullMarked public class InterfaceTypeExtensionDefinition extends InterfaceTypeDefinition implements SDLExtensionDefinition { @Internal @@ -21,8 +25,8 @@ protected InterfaceTypeExtensionDefinition(String name, List implementz, List definitions, List directives, - Description description, - SourceLocation sourceLocation, + @Nullable Description description, + @Nullable SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { @@ -33,8 +37,8 @@ protected InterfaceTypeExtensionDefinition(String name, public InterfaceTypeExtensionDefinition deepCopy() { return new InterfaceTypeExtensionDefinition(getName(), getImplements(), - deepCopy(getFieldDefinitions()), - deepCopy(getDirectives()), + assertNotNull(deepCopy(getFieldDefinitions()), "fieldDefinitions cannot be null"), + assertNotNull(deepCopy(getDirectives()), "directives cannot be null"), getDescription(), getSourceLocation(), getComments(), @@ -70,6 +74,7 @@ public InterfaceTypeExtensionDefinition transformExtension(Consumer bui return builder.build(); } + @NullUnmarked public static final class Builder implements NodeDirectivesBuilder { private SourceLocation sourceLocation; private ImmutableList comments = emptyList(); diff --git a/src/main/java/graphql/language/ListType.java b/src/main/java/graphql/language/ListType.java index f193179bb6..670ffe7d53 100644 --- a/src/main/java/graphql/language/ListType.java +++ b/src/main/java/graphql/language/ListType.java @@ -6,6 +6,9 @@ import graphql.PublicApi; import graphql.util.TraversalControl; import graphql.util.TraverserContext; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.NullUnmarked; +import org.jspecify.annotations.Nullable; import java.util.LinkedHashMap; import java.util.List; @@ -18,6 +21,7 @@ import static graphql.language.NodeChildrenContainer.newNodeChildrenContainer; @PublicApi +@NullMarked public class ListType extends AbstractNode implements Type { private final Type type; @@ -25,7 +29,7 @@ public class ListType extends AbstractNode implements Type { public static final String CHILD_TYPE = "type"; @Internal - protected ListType(Type type, SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { + protected ListType(Type type, @Nullable SourceLocation sourceLocation, List comments, IgnoredChars ignoredChars, Map additionalData) { super(sourceLocation, comments, ignoredChars, additionalData); this.type = type; } @@ -63,7 +67,7 @@ public ListType withNewChildren(NodeChildrenContainer newChildren) { } @Override - public boolean isEqualTo(Node o) { + public boolean isEqualTo(@Nullable Node o) { if (this == o) { return true; } @@ -76,7 +80,7 @@ public boolean isEqualTo(Node o) { @Override public ListType deepCopy() { - return new ListType(deepCopy(type), getSourceLocation(), getComments(), getIgnoredChars(), getAdditionalData()); + return new ListType(assertNotNull(deepCopy(type), "type cannot be null"), getSourceLocation(), getComments(), getIgnoredChars(), getAdditionalData()); } @Override @@ -105,6 +109,7 @@ public ListType transform(Consumer builderConsumer) { return builder.build(); } + @NullUnmarked public static final class Builder implements NodeBuilder { private Type type; private SourceLocation sourceLocation; diff --git a/src/main/java/graphql/language/NodeDirectivesBuilder.java b/src/main/java/graphql/language/NodeDirectivesBuilder.java index 3694165fab..12b793f3a9 100644 --- a/src/main/java/graphql/language/NodeDirectivesBuilder.java +++ b/src/main/java/graphql/language/NodeDirectivesBuilder.java @@ -1,10 +1,12 @@ package graphql.language; import graphql.PublicApi; +import org.jspecify.annotations.NullMarked; import java.util.List; @PublicApi +@NullMarked public interface NodeDirectivesBuilder extends NodeBuilder { NodeDirectivesBuilder directives(List directives); diff --git a/src/main/java/graphql/language/NodeParentTree.java b/src/main/java/graphql/language/NodeParentTree.java index 91907e6ef0..289743eab3 100644 --- a/src/main/java/graphql/language/NodeParentTree.java +++ b/src/main/java/graphql/language/NodeParentTree.java @@ -4,6 +4,8 @@ import graphql.Internal; import graphql.PublicApi; import graphql.collect.ImmutableKit; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import java.util.ArrayDeque; import java.util.ArrayList; @@ -21,10 +23,11 @@ * on an ObjectTypeDefinition. */ @PublicApi +@NullMarked public class NodeParentTree { private final T node; - private final NodeParentTree parent; + private final @Nullable NodeParentTree parent; private final ImmutableList path; @Internal diff --git a/src/main/java/graphql/util/Anonymizer.java b/src/main/java/graphql/util/Anonymizer.java index e58a67ed17..5afc76c564 100644 --- a/src/main/java/graphql/util/Anonymizer.java +++ b/src/main/java/graphql/util/Anonymizer.java @@ -871,9 +871,13 @@ public TraversalControl visitFragmentDefinition(FragmentDefinition node, Travers @Override public TraversalControl visitInlineFragment(InlineFragment node, TraverserContext context) { - GraphQLType currentCondition = assertNotNull(schema.getType(node.getTypeCondition().getName())); - String newCondition = newNames.get(currentCondition); - return changeNode(context, node.transform(builder -> builder.typeCondition(new TypeName(newCondition)))); + TypeName typeCondition = node.getTypeCondition(); + if (typeCondition != null) { + GraphQLType currentCondition = assertNotNull(schema.getType(typeCondition.getName())); + String newCondition = newNames.get(currentCondition); + return changeNode(context, node.transform(builder -> builder.typeCondition(new TypeName(newCondition)))); + } + return TraversalControl.CONTINUE; } @Override diff --git a/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy b/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy index 7116b1641a..667cc63d0c 100644 --- a/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy +++ b/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy @@ -123,16 +123,6 @@ class JSpecifyAnnotationsCheck extends Specification { "graphql.language.FieldDefinition", "graphql.language.FragmentDefinition", "graphql.language.FragmentSpread", - "graphql.language.ImplementingTypeDefinition", - "graphql.language.InlineFragment", - "graphql.language.InputObjectTypeDefinition", - "graphql.language.InputObjectTypeExtensionDefinition", - "graphql.language.InputValueDefinition", - "graphql.language.InterfaceTypeDefinition", - "graphql.language.InterfaceTypeExtensionDefinition", - "graphql.language.ListType", - "graphql.language.NodeDirectivesBuilder", - "graphql.language.NodeParentTree", "graphql.language.NodeTraverser", "graphql.language.NonNullType", "graphql.language.ObjectField", From 6c6937c6684bdd7587ec2d04a3d5fdd1447a038a Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Sat, 21 Mar 2026 18:05:56 +1100 Subject: [PATCH 3/8] Remove already-annotated classes from JSpecify exemption list NodeVisitor, NodeVisitorStub, SourceLocation, and Type are already annotated with @NullMarked so they should not be in the exemption list. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy b/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy index 37c3b4490c..9da8c8325d 100644 --- a/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy +++ b/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy @@ -127,8 +127,6 @@ class JSpecifyAnnotationsCheck extends Specification { "graphql.language.NodeChildrenContainer", "graphql.language.NodeDirectivesBuilder", "graphql.language.NodeParentTree", - "graphql.language.NodeVisitor", - "graphql.language.NodeVisitorStub", "graphql.language.ScalarTypeDefinition", "graphql.language.ScalarTypeExtensionDefinition", "graphql.language.SchemaDefinition", @@ -136,8 +134,6 @@ class JSpecifyAnnotationsCheck extends Specification { "graphql.language.Selection", "graphql.language.SelectionSet", "graphql.language.SelectionSetContainer", - "graphql.language.SourceLocation", - "graphql.language.Type", "graphql.language.TypeKind", "graphql.language.TypeName", "graphql.language.UnionTypeDefinition", From 1c060305cc1086750c386c554db0dbec99559a5a Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Sat, 21 Mar 2026 18:33:05 +1100 Subject: [PATCH 4/8] Fix NullAway error in NodeParentTree.mkPath NamedNode.getName() returns @Nullable String, so use Objects.toString to handle null names (e.g. anonymous OperationDefinition). Co-Authored-By: Claude Opus 4.6 (1M context) --- src/main/java/graphql/language/NodeParentTree.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/graphql/language/NodeParentTree.java b/src/main/java/graphql/language/NodeParentTree.java index 289743eab3..7edc6e2dd3 100644 --- a/src/main/java/graphql/language/NodeParentTree.java +++ b/src/main/java/graphql/language/NodeParentTree.java @@ -11,6 +11,7 @@ import java.util.ArrayList; import java.util.Deque; import java.util.List; +import java.util.Objects; import java.util.Optional; import static graphql.Assert.assertNotNull; @@ -48,7 +49,7 @@ public NodeParentTree(Deque nodeStack) { private ImmutableList mkPath(Deque copy) { return ImmutableKit.filterAndMap(copy, node1 -> node1 instanceof NamedNode, - node1 -> ((NamedNode) node1).getName()); + node1 -> Objects.toString(((NamedNode) node1).getName(), "")); } From e54472dcf2061663f79e1c611fd2f058fe1b2a93 Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Sat, 21 Mar 2026 18:43:06 +1100 Subject: [PATCH 5/8] Remove 15 already-annotated classes from JSpecify exemption list These classes are annotated with @NullMarked on this branch but were still in the exemption list after the merge from master. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../archunit/JSpecifyAnnotationsCheck.groovy | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy b/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy index 9da8c8325d..01a3c5dd5e 100644 --- a/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy +++ b/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy @@ -113,20 +113,6 @@ class JSpecifyAnnotationsCheck extends Specification { "graphql.language.FieldDefinition", "graphql.language.FragmentDefinition", "graphql.language.FragmentSpread", - "graphql.language.IgnoredChar", - "graphql.language.IgnoredChars", - "graphql.language.ImplementingTypeDefinition", - "graphql.language.InlineFragment", - "graphql.language.InputObjectTypeDefinition", - "graphql.language.InputObjectTypeExtensionDefinition", - "graphql.language.InputValueDefinition", - "graphql.language.InterfaceTypeDefinition", - "graphql.language.InterfaceTypeExtensionDefinition", - "graphql.language.ListType", - "graphql.language.Node", - "graphql.language.NodeChildrenContainer", - "graphql.language.NodeDirectivesBuilder", - "graphql.language.NodeParentTree", "graphql.language.ScalarTypeDefinition", "graphql.language.ScalarTypeExtensionDefinition", "graphql.language.SchemaDefinition", From c2f45d571740dd082547fc2a0ea70b0b624d8153 Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Sat, 21 Mar 2026 19:01:13 +1100 Subject: [PATCH 6/8] Update test baseline for Anonymizer$4 coverage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit visitInlineFragment coverage dropped due to NamedNode.getName() returning @Nullable — the null-check branch is not exercised in tests. Co-Authored-By: Claude Opus 4.6 (1M context) --- test-baseline.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-baseline.json b/test-baseline.json index c96d7aa13a..79d95f5bf6 100644 --- a/test-baseline.json +++ b/test-baseline.json @@ -116750,11 +116750,11 @@ "graphql.util.Anonymizer$4": { "line": { "covered": 52, - "missed": 0 + "missed": 1 }, "branch": { "covered": 12, - "missed": 0 + "missed": 1 }, "method": { "covered": 19, From 03900ca3e9e63411f4525acc9a35ad09aded8b98 Mon Sep 17 00:00:00 2001 From: Andreas Marek Date: Thu, 23 Apr 2026 09:45:07 +1000 Subject: [PATCH 7/8] Revert test-baseline.json changes PRs should not modify this file. --- test-baseline.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test-baseline.json b/test-baseline.json index 95a6e62233..06dba72acc 100644 --- a/test-baseline.json +++ b/test-baseline.json @@ -116769,11 +116769,11 @@ "graphql.util.Anonymizer$4": { "line": { "covered": 52, - "missed": 1 + "missed": 0 }, "branch": { "covered": 12, - "missed": 1 + "missed": 0 }, "method": { "covered": 19, From 6de2cc8658b25f5cb28c4874514b8e7a21636e7b Mon Sep 17 00:00:00 2001 From: Andreas Marek Date: Thu, 23 Apr 2026 10:10:31 +1000 Subject: [PATCH 8/8] Cover inline fragment without type condition in Anonymizer Exercises the null branch in Anonymizer.visitInlineFragment that was added because InlineFragment.getTypeCondition() is now @Nullable, restoring Anonymizer$4 coverage to its baseline. --- .../groovy/graphql/util/AnonymizerTest.groovy | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/test/groovy/graphql/util/AnonymizerTest.groovy b/src/test/groovy/graphql/util/AnonymizerTest.groovy index ae6ed614a4..804ee88387 100644 --- a/src/test/groovy/graphql/util/AnonymizerTest.groovy +++ b/src/test/groovy/graphql/util/AnonymizerTest.groovy @@ -79,6 +79,27 @@ type Object2 { } + def "query with inline fragment without type condition"() { + given: + def schema = TestUtil.schema(""" + type Query { + foo: Foo + } + type Foo { + bar1: String + bar2: ID + } + """) + def query = "{foo {... {bar1 bar2}}}" + + when: + def result = Anonymizer.anonymizeSchemaAndQueries(schema, [query]) + def newQuery = result.queries[0] + + then: + newQuery == "{field1{...{field2 field3}}}" + } + def "query with arguments"() { given: def schema = TestUtil.schema("""