Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
c60fdf6
Initial plan
Copilot Jan 24, 2026
c02fa4e
Add JSpecify annotations to 10 classes in graphql.language package
Copilot Jan 24, 2026
3db2dde
Update JSpecify annotation prompt with effectiveness improvements
Copilot Jan 24, 2026
b0ef95a
Initial plan
Copilot Jan 25, 2026
7235cf1
Add JSpecify annotations to 10 language classes
Copilot Jan 25, 2026
53e8423
Fix FragmentDefinition selectionSet to be nullable for validation tests
Copilot Jan 25, 2026
256724a
Make builder methods lenient to allow incomplete test instances
Copilot Jan 25, 2026
46c212c
Merge branch 'master' into copilot/add-jspecify-annotations-again
dondonz Feb 15, 2026
c0cab87
A selection set is not-nullable
dondonz Feb 15, 2026
75a9170
Add directive location
dondonz Feb 15, 2026
baa9a0e
Make name not nullable
dondonz Feb 15, 2026
1055f0c
Add more suggestions
dondonz Feb 15, 2026
2cd70a7
More improvements not nullable items according to spec
dondonz Feb 15, 2026
ea86f88
Add assert for compile check
dondonz Feb 15, 2026
6237b2d
Add nullunmarked on builders
dondonz Feb 15, 2026
fced94a
Fix more builders
dondonz Feb 15, 2026
97abdd1
Commit the fixing plan
dondonz Feb 15, 2026
d88fa6b
Fixing tests with new stricter non-nullable requirements
dondonz Feb 17, 2026
72ea92a
More test fixes
dondonz Feb 18, 2026
84f59fb
Merge branch 'master' into copilot/add-jspecify-annotations-again
dondonz Mar 21, 2026
346b85e
Remove already-annotated classes from JSpecify exemption list
dondonz Mar 21, 2026
dec1764
Remove no longer needed file
dondonz Mar 21, 2026
53a4a8a
Update test baseline for EnumValueDefinition coverage
dondonz Mar 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 0 additions & 68 deletions .claude/commands/jspecify-annotate.md

This file was deleted.

9 changes: 6 additions & 3 deletions src/main/java/graphql/language/Comment.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
package graphql.language;

import graphql.PublicApi;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

import java.io.Serializable;

/**
* A single-line comment. These are comments that start with a {@code #} in source documents.
*/
@PublicApi
@NullMarked
public class Comment implements Serializable {
public final String content;
public final SourceLocation sourceLocation;
public final @Nullable SourceLocation sourceLocation;

public Comment(String content, SourceLocation sourceLocation) {
public Comment(String content, @Nullable SourceLocation sourceLocation) {
this.content = content;
this.sourceLocation = sourceLocation;
}
Expand All @@ -21,7 +24,7 @@ public String getContent() {
return content;
}

public SourceLocation getSourceLocation() {
public @Nullable SourceLocation getSourceLocation() {
return sourceLocation;
}
}
2 changes: 2 additions & 0 deletions src/main/java/graphql/language/Definition.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@


import graphql.PublicApi;
import org.jspecify.annotations.NullMarked;

@PublicApi
@NullMarked
public interface Definition<T extends Definition> extends Node<T> {

}
13 changes: 9 additions & 4 deletions src/main/java/graphql/language/DirectiveLocation.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package graphql.language;


import com.google.common.collect.ImmutableList;
import graphql.Internal;
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;
Expand All @@ -20,11 +22,13 @@
import static graphql.language.NodeUtil.assertNewChildrenAreEmpty;

@PublicApi
@NullMarked
public class DirectiveLocation extends AbstractNode<DirectiveLocation> implements NamedNode<DirectiveLocation> {
private final String name;

@Internal
protected DirectiveLocation(String name, SourceLocation sourceLocation, List<Comment> comments, IgnoredChars ignoredChars, Map<String, String> additionalData) {
protected DirectiveLocation(String name, @Nullable SourceLocation sourceLocation, List<Comment> comments,
IgnoredChars ignoredChars, Map<String, String> additionalData) {
super(sourceLocation, comments, ignoredChars, additionalData);
this.name = name;
}
Expand Down Expand Up @@ -60,7 +64,7 @@ public DirectiveLocation withNewChildren(NodeChildrenContainer newChildren) {
}

@Override
public boolean isEqualTo(Node o) {
public boolean isEqualTo(@Nullable Node o) {
if (this == o) {
return true;
}
Expand Down Expand Up @@ -100,6 +104,7 @@ public DirectiveLocation transform(Consumer<Builder> builderConsumer) {
return builder.build();
}

@NullUnmarked
public static final class Builder implements NodeBuilder {
private SourceLocation sourceLocation;
private ImmutableList<Comment> comments = emptyList();
Expand Down Expand Up @@ -148,7 +153,7 @@ public Builder additionalData(String key, String value) {
}

public DirectiveLocation build() {
return new DirectiveLocation(name, sourceLocation, comments, ignoredChars, additionalData);
return new DirectiveLocation(assertNotNull(name), sourceLocation, comments, ignoredChars, additionalData);
}
}
}
2 changes: 2 additions & 0 deletions src/main/java/graphql/language/DirectivesContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@


import graphql.PublicApi;
import org.jspecify.annotations.NullMarked;

import java.util.List;
import java.util.Map;
Expand All @@ -15,6 +16,7 @@
* @see DirectiveDefinition#isRepeatable()
*/
@PublicApi
@NullMarked
public interface DirectivesContainer<T extends DirectivesContainer> extends Node<T> {

/**
Expand Down
30 changes: 19 additions & 11 deletions src/main/java/graphql/language/Document.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package graphql.language;


import com.google.common.collect.ImmutableList;
import graphql.Assert;
import graphql.Internal;
import graphql.PublicApi;
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.LinkedHashMap;
import java.util.List;
Expand All @@ -21,14 +23,16 @@
import static graphql.language.NodeChildrenContainer.newNodeChildrenContainer;

@PublicApi
@NullMarked
public class Document extends AbstractNode<Document> {

private final ImmutableList<Definition> definitions;

public static final String CHILD_DEFINITIONS = "definitions";

@Internal
protected Document(List<Definition> definitions, SourceLocation sourceLocation, List<Comment> comments, IgnoredChars ignoredChars, Map<String, String> additionalData) {
protected Document(List<Definition> definitions, @Nullable SourceLocation sourceLocation, List<Comment> comments,
IgnoredChars ignoredChars, Map<String, String> additionalData) {
super(sourceLocation, comments, ignoredChars, additionalData);
this.definitions = ImmutableList.copyOf(definitions);
}
Expand All @@ -47,7 +51,8 @@ public List<Definition> getDefinitions() {
}

/**
* Returns a list of definitions of the specific type. It uses {@link java.lang.Class#isAssignableFrom(Class)} for the test
* Returns a list of definitions of the specific type. It uses
* {@link java.lang.Class#isAssignableFrom(Class)} for the test
*
* @param definitionClass the definition class
* @param <T> the type of definition
Expand All @@ -61,9 +66,11 @@ public <T extends Definition> List<T> getDefinitionsOfType(Class<T> definitionCl
}

/**
* Returns the first of the specific type. It uses {@link java.lang.Class#isAssignableFrom(Class)} for the test.
* Returns the first of the specific type. It uses
* {@link java.lang.Class#isAssignableFrom(Class)} for the test.
*
* This is useful when you have generated a document in code and KNOW there is only one definition in it
* This is useful when you have generated a document in code and KNOW there is
* only one definition in it
*
* @param definitionClass the definition class
* @param <T> the type of definition
Expand All @@ -78,7 +85,8 @@ public <T extends Definition> Optional<T> getFirstDefinitionOfType(Class<T> defi
}

/**
* This will allow you to find a {@link OperationDefinition} with the specified name
* This will allow you to find a {@link OperationDefinition} with the specified
* name
* in the document
*
* @param name the name of the operation to find
Expand Down Expand Up @@ -109,12 +117,11 @@ public NodeChildrenContainer getNamedChildren() {
@Override
public Document withNewChildren(NodeChildrenContainer newChildren) {
return transform(builder -> builder
.definitions(newChildren.getChildren(CHILD_DEFINITIONS))
);
.definitions(newChildren.getChildren(CHILD_DEFINITIONS)));
}

@Override
public boolean isEqualTo(Node o) {
public boolean isEqualTo(@Nullable Node o) {
if (this == o) {
return true;
}
Expand All @@ -127,7 +134,8 @@ public boolean isEqualTo(Node o) {

@Override
public Document deepCopy() {
return new Document(deepCopy(definitions), getSourceLocation(), getComments(), getIgnoredChars(), getAdditionalData());
return new Document(assertNotNull(deepCopy(definitions)), getSourceLocation(), getComments(), getIgnoredChars(),
getAdditionalData());
}

@Override
Expand All @@ -152,6 +160,7 @@ public Document transform(Consumer<Builder> builderConsumer) {
return builder.build();
}

@NullUnmarked
public static final class Builder implements NodeBuilder {
private ImmutableList<Definition> definitions = emptyList();
private SourceLocation sourceLocation;
Expand Down Expand Up @@ -205,7 +214,6 @@ public Builder additionalData(String key, String value) {
return this;
}


public Document build() {
return new Document(definitions, sourceLocation, comments, ignoredChars, additionalData);
}
Expand Down
33 changes: 19 additions & 14 deletions src/main/java/graphql/language/EnumTypeDefinition.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,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;
Expand All @@ -20,7 +23,9 @@
import static graphql.language.NodeChildrenContainer.newNodeChildrenContainer;

@PublicApi
public class EnumTypeDefinition extends AbstractDescribedNode<EnumTypeDefinition> implements TypeDefinition<EnumTypeDefinition>, DirectivesContainer<EnumTypeDefinition>, NamedNode<EnumTypeDefinition> {
@NullMarked
public class EnumTypeDefinition extends AbstractDescribedNode<EnumTypeDefinition> implements
TypeDefinition<EnumTypeDefinition>, DirectivesContainer<EnumTypeDefinition>, NamedNode<EnumTypeDefinition> {
private final String name;
private final ImmutableList<EnumValueDefinition> enumValueDefinitions;
private final NodeUtil.DirectivesHolder directives;
Expand All @@ -30,12 +35,12 @@ public class EnumTypeDefinition extends AbstractDescribedNode<EnumTypeDefinition

@Internal
protected EnumTypeDefinition(String name,
List<EnumValueDefinition> enumValueDefinitions,
List<Directive> directives,
Description description,
SourceLocation sourceLocation,
List<Comment> comments,
IgnoredChars ignoredChars, Map<String, String> additionalData) {
List<EnumValueDefinition> enumValueDefinitions,
List<Directive> directives,
@Nullable Description description,
@Nullable SourceLocation sourceLocation,
List<Comment> comments,
IgnoredChars ignoredChars, Map<String, String> additionalData) {
super(sourceLocation, comments, ignoredChars, additionalData, description);
this.name = name;
this.directives = NodeUtil.DirectivesHolder.of(directives);
Expand Down Expand Up @@ -100,12 +105,11 @@ public NodeChildrenContainer getNamedChildren() {
public EnumTypeDefinition withNewChildren(NodeChildrenContainer newChildren) {
return transform(builder -> builder
.enumValueDefinitions(newChildren.getChildren(CHILD_ENUM_VALUE_DEFINITIONS))
.directives(newChildren.getChildren(CHILD_DIRECTIVES))
);
.directives(newChildren.getChildren(CHILD_DIRECTIVES)));
}

@Override
public boolean isEqualTo(Node o) {
public boolean isEqualTo(@Nullable Node o) {
if (this == o) {
return true;
}
Expand All @@ -121,8 +125,8 @@ public boolean isEqualTo(Node o) {
@Override
public EnumTypeDefinition deepCopy() {
return new EnumTypeDefinition(name,
deepCopy(enumValueDefinitions),
deepCopy(directives.getDirectives()),
assertNotNull(deepCopy(enumValueDefinitions)),
assertNotNull(deepCopy(directives.getDirectives())),
description,
getSourceLocation(),
getComments(),
Expand Down Expand Up @@ -154,6 +158,7 @@ public EnumTypeDefinition transform(Consumer<Builder> builderConsumer) {
return builder.build();
}

@NullUnmarked
public static final class Builder implements NodeDirectivesBuilder {
private SourceLocation sourceLocation;
private ImmutableList<Comment> comments = emptyList();
Expand Down Expand Up @@ -234,9 +239,9 @@ public Builder additionalData(String key, String value) {
return this;
}


public EnumTypeDefinition build() {
return new EnumTypeDefinition(name, enumValueDefinitions, directives, description, sourceLocation, comments, ignoredChars, additionalData);
return new EnumTypeDefinition(assertNotNull(name), enumValueDefinitions, directives, description,
sourceLocation, comments, ignoredChars, additionalData);
}
}
}
Loading