From 19c85a545e5732d5f5b9741fb1f04494f2ebbe05 Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Sat, 5 Apr 2025 13:30:59 +1100 Subject: [PATCH 01/15] Add allow listed exemptions --- .../graphql/JSpecifyAnnotationsCheck.groovy | 706 ++++++++++++++++++ 1 file changed, 706 insertions(+) create mode 100644 src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy diff --git a/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy b/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy new file mode 100644 index 0000000000..0d6f0f0fbd --- /dev/null +++ b/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy @@ -0,0 +1,706 @@ +package graphql + +import com.tngtech.archunit.core.domain.JavaClass +import com.tngtech.archunit.core.domain.JavaClasses +import com.tngtech.archunit.core.importer.ClassFileImporter +import com.tngtech.archunit.core.importer.ImportOption +import spock.lang.Specification + +/** + * This test ensures that all NEW classes in the graphql package are properly annotated with JSpecify annotations. + * It checks for the presence of @NullMarked annotation on classes. + * + * Classes can be exempted from this check by adding them to the ALLOWLIST. + */ +class JSpecifyAnnotationsCheck extends Specification { + + // Allowlist of existing classes that are exempt from JSpecify annotation checks + private static final Set ALLOWLIST = [ + "graphql.Assert", + "graphql.AssertException", + "graphql.Directives", + "graphql.DirectivesUtil", + "graphql.DuckTyped", + "graphql.ErrorClassification", + "graphql.ErrorType", + "graphql.ExceptionWhileDataFetching", + "graphql.ExecutionInput", + "graphql.ExecutionResult", + "graphql.ExecutionResultImpl", + "graphql.ExperimentalApi", + "graphql.GraphQL", + "graphql.GraphQLContext", + "graphql.GraphQLError", + "graphql.GraphQLException", + "graphql.GraphqlErrorBuilder", + "graphql.GraphqlErrorException", + "graphql.GraphqlErrorHelper", + "graphql.Internal", + "graphql.InvalidSyntaxError", + "graphql.Mutable", + "graphql.ParseAndValidate", + "graphql.ParseAndValidateResult", + "graphql.PublicApi", + "graphql.PublicSpi", + "graphql.Scalars", + "graphql.SerializationError", + "graphql.ThreadSafe", + "graphql.TrivialDataFetcher", + "graphql.TypeMismatchError", + "graphql.TypeResolutionEnvironment", + "graphql.UnresolvedTypeError", + "graphql.VisibleForTesting", + "graphql.agent.result.ExecutionTrackingResult", + "graphql.analysis.FieldComplexityCalculator", + "graphql.analysis.FieldComplexityEnvironment", + "graphql.analysis.MaxQueryComplexityInstrumentation", + "graphql.analysis.MaxQueryDepthInstrumentation", + "graphql.analysis.NodeVisitorWithTypeTracking", + "graphql.analysis.QueryComplexityCalculator", + "graphql.analysis.QueryComplexityInfo", + "graphql.analysis.QueryDepthInfo", + "graphql.analysis.QueryReducer", + "graphql.analysis.QueryTransformer", + "graphql.analysis.QueryTraversalContext", + "graphql.analysis.QueryTraversalOptions", + "graphql.analysis.QueryTraverser", + "graphql.analysis.QueryVisitor", + "graphql.analysis.QueryVisitorFieldArgumentEnvironment", + "graphql.analysis.QueryVisitorFieldArgumentEnvironmentImpl", + "graphql.analysis.QueryVisitorFieldArgumentInputValue", + "graphql.analysis.QueryVisitorFieldArgumentInputValueImpl", + "graphql.analysis.QueryVisitorFieldArgumentValueEnvironment", + "graphql.analysis.QueryVisitorFieldArgumentValueEnvironmentImpl", + "graphql.analysis.QueryVisitorFieldEnvironment", + "graphql.analysis.QueryVisitorFieldEnvironmentImpl", + "graphql.analysis.QueryVisitorFragmentDefinitionEnvironment", + "graphql.analysis.QueryVisitorFragmentDefinitionEnvironmentImpl", + "graphql.analysis.QueryVisitorFragmentSpreadEnvironment", + "graphql.analysis.QueryVisitorFragmentSpreadEnvironmentImpl", + "graphql.analysis.QueryVisitorInlineFragmentEnvironment", + "graphql.analysis.QueryVisitorInlineFragmentEnvironmentImpl", + "graphql.analysis.QueryVisitorStub", + "graphql.analysis.values.ValueTraverser", + "graphql.analysis.values.ValueVisitor", + "graphql.collect.ImmutableKit", + "graphql.collect.ImmutableMapWithNullValues", + "graphql.execution.AbortExecutionException", + "graphql.execution.AbstractAsyncExecutionStrategy", + "graphql.execution.Async", + "graphql.execution.AsyncExecutionStrategy", + "graphql.execution.AsyncSerialExecutionStrategy", + "graphql.execution.CoercedVariables", + "graphql.execution.DataFetcherExceptionHandler", + "graphql.execution.DataFetcherExceptionHandlerParameters", + "graphql.execution.DataFetcherExceptionHandlerResult", + "graphql.execution.DataFetcherResult", + "graphql.execution.DataLoaderDispatchStrategy", + "graphql.execution.DefaultValueUnboxer", + "graphql.execution.EngineRunningObserver", + "graphql.execution.Execution", + "graphql.execution.ExecutionContext", + "graphql.execution.ExecutionContextBuilder", + "graphql.execution.ExecutionId", + "graphql.execution.ExecutionIdProvider", + "graphql.execution.ExecutionStepInfo", + "graphql.execution.ExecutionStepInfoFactory", + "graphql.execution.ExecutionStrategy", + "graphql.execution.ExecutionStrategyParameters", + "graphql.execution.FetchedValue", + "graphql.execution.FieldCollector", + "graphql.execution.FieldCollectorParameters", + "graphql.execution.FieldValueInfo", + "graphql.execution.InputMapDefinesTooManyFieldsException", + "graphql.execution.MergedField", + "graphql.execution.MergedSelectionSet", + "graphql.execution.MissingRootTypeException", + "graphql.execution.NonNullableFieldValidator", + "graphql.execution.NonNullableFieldWasNullError", + "graphql.execution.NonNullableFieldWasNullException", + "graphql.execution.NonNullableValueCoercedAsNullException", + "graphql.execution.NormalizedVariables", + "graphql.execution.OneOfNullValueException", + "graphql.execution.OneOfTooManyKeysException", + "graphql.execution.RawVariables", + "graphql.execution.ResolveType", + "graphql.execution.ResultNodesInfo", + "graphql.execution.ResultPath", + "graphql.execution.SimpleDataFetcherExceptionHandler", + "graphql.execution.SubscriptionExecutionStrategy", + "graphql.execution.TypeFromAST", + "graphql.execution.TypeResolutionParameters", + "graphql.execution.UnknownOperationException", + "graphql.execution.UnresolvedTypeException", + "graphql.execution.ValueUnboxer", + "graphql.execution.ValuesResolver", + "graphql.execution.ValuesResolverConversion", + "graphql.execution.ValuesResolverLegacy", + "graphql.execution.ValuesResolverOneOfValidation", + "graphql.execution.conditional.ConditionalNodeDecision", + "graphql.execution.conditional.ConditionalNodeDecisionEnvironment", + "graphql.execution.conditional.ConditionalNodes", + "graphql.execution.directives.DirectivesResolver", + "graphql.execution.directives.QueryAppliedDirective", + "graphql.execution.directives.QueryAppliedDirectiveArgument", + "graphql.execution.directives.QueryDirectives", + "graphql.execution.directives.QueryDirectivesBuilder", + "graphql.execution.directives.QueryDirectivesImpl", + "graphql.execution.incremental.DeferredCallContext", + "graphql.execution.incremental.DeferredExecution", + "graphql.execution.incremental.DeferredExecutionSupport", + "graphql.execution.incremental.DeferredFragmentCall", + "graphql.execution.incremental.IncrementalCall", + "graphql.execution.incremental.IncrementalCallState", + "graphql.execution.incremental.IncrementalUtils", + "graphql.execution.incremental.StreamedCall", + "graphql.execution.instrumentation.ChainedInstrumentation", + "graphql.execution.instrumentation.DocumentAndVariables", + "graphql.execution.instrumentation.ExecuteObjectInstrumentationContext", + "graphql.execution.instrumentation.ExecutionStrategyInstrumentationContext", + "graphql.execution.instrumentation.FieldFetchingInstrumentationContext", + "graphql.execution.instrumentation.Instrumentation", + "graphql.execution.instrumentation.InstrumentationContext", + "graphql.execution.instrumentation.InstrumentationState", + "graphql.execution.instrumentation.NoContextChainedInstrumentation", + "graphql.execution.instrumentation.SimpleInstrumentation", + "graphql.execution.instrumentation.SimpleInstrumentationContext", + "graphql.execution.instrumentation.SimplePerformantInstrumentation", + "graphql.execution.instrumentation.dataloader.EmptyDataLoaderRegistryInstance", + "graphql.execution.instrumentation.dataloader.FallbackDataLoaderDispatchStrategy", + "graphql.execution.instrumentation.dataloader.LevelMap", + "graphql.execution.instrumentation.dataloader.PerLevelDataLoaderDispatchStrategy", + "graphql.execution.instrumentation.dataloader.PerLevelDataLoaderDispatchStrategyWithDeferAlwaysDispatch", + "graphql.execution.instrumentation.fieldvalidation.FieldAndArguments", + "graphql.execution.instrumentation.fieldvalidation.FieldValidation", + "graphql.execution.instrumentation.fieldvalidation.FieldValidationEnvironment", + "graphql.execution.instrumentation.fieldvalidation.FieldValidationInstrumentation", + "graphql.execution.instrumentation.fieldvalidation.FieldValidationSupport", + "graphql.execution.instrumentation.fieldvalidation.SimpleFieldValidation", + "graphql.execution.instrumentation.parameters.InstrumentationCreateStateParameters", + "graphql.execution.instrumentation.parameters.InstrumentationExecuteOperationParameters", + "graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters", + "graphql.execution.instrumentation.parameters.InstrumentationExecutionStrategyParameters", + "graphql.execution.instrumentation.parameters.InstrumentationFieldCompleteParameters", + "graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters", + "graphql.execution.instrumentation.parameters.InstrumentationFieldParameters", + "graphql.execution.instrumentation.parameters.InstrumentationValidationParameters", + "graphql.execution.instrumentation.tracing.TracingInstrumentation", + "graphql.execution.instrumentation.tracing.TracingSupport", + "graphql.execution.preparsed.NoOpPreparsedDocumentProvider", + "graphql.execution.preparsed.PreparsedDocumentEntry", + "graphql.execution.preparsed.PreparsedDocumentProvider", + "graphql.execution.preparsed.persisted.ApolloPersistedQuerySupport", + "graphql.execution.preparsed.persisted.InMemoryPersistedQueryCache", + "graphql.execution.preparsed.persisted.PersistedQueryCache", + "graphql.execution.preparsed.persisted.PersistedQueryCacheMiss", + "graphql.execution.preparsed.persisted.PersistedQueryError", + "graphql.execution.preparsed.persisted.PersistedQueryIdInvalid", + "graphql.execution.preparsed.persisted.PersistedQueryNotFound", + "graphql.execution.preparsed.persisted.PersistedQuerySupport", + "graphql.execution.reactive.CompletionStageMappingOrderedPublisher", + "graphql.execution.reactive.CompletionStageMappingPublisher", + "graphql.execution.reactive.CompletionStageOrderedSubscriber", + "graphql.execution.reactive.CompletionStageSubscriber", + "graphql.execution.reactive.DelegatingSubscription", + "graphql.execution.reactive.NonBlockingMutexExecutor", + "graphql.execution.reactive.ReactiveSupport", + "graphql.execution.reactive.SingleSubscriberPublisher", + "graphql.execution.reactive.SubscriptionPublisher", + "graphql.execution.values.InputInterceptor", + "graphql.execution.values.legacycoercing.LegacyCoercingInputInterceptor", + "graphql.extensions.DefaultExtensionsMerger", + "graphql.extensions.ExtensionsBuilder", + "graphql.extensions.ExtensionsMerger", + "graphql.i18n.I18n", + "graphql.i18n.I18nMsg", + "graphql.incremental.DeferPayload", + "graphql.incremental.DelayedIncrementalPartialResult", + "graphql.incremental.DelayedIncrementalPartialResultImpl", + "graphql.incremental.IncrementalExecutionResult", + "graphql.incremental.IncrementalExecutionResultImpl", + "graphql.incremental.IncrementalPayload", + "graphql.incremental.StreamPayload", + "graphql.introspection.GoodFaithIntrospection", + "graphql.introspection.Introspection", + "graphql.introspection.IntrospectionDataFetcher", + "graphql.introspection.IntrospectionDataFetchingEnvironment", + "graphql.introspection.IntrospectionDisabledError", + "graphql.introspection.IntrospectionQuery", + "graphql.introspection.IntrospectionQueryBuilder", + "graphql.introspection.IntrospectionResultToSchema", + "graphql.introspection.IntrospectionWithDirectivesSupport", + "graphql.language.AbstractDescribedNode", + "graphql.language.AbstractNode", + "graphql.language.Argument", + "graphql.language.ArrayValue", + "graphql.language.AstComparator", + "graphql.language.AstNodeAdapter", + "graphql.language.AstPrinter", + "graphql.language.AstSignature", + "graphql.language.AstSorter", + "graphql.language.AstTransformer", + "graphql.language.BooleanValue", + "graphql.language.Comment", + "graphql.language.Definition", + "graphql.language.DescribedNode", + "graphql.language.Description", + "graphql.language.Directive", + "graphql.language.DirectiveDefinition", + "graphql.language.DirectiveLocation", + "graphql.language.DirectivesContainer", + "graphql.language.Document", + "graphql.language.EnumTypeDefinition", + "graphql.language.EnumTypeExtensionDefinition", + "graphql.language.EnumValue", + "graphql.language.EnumValueDefinition", + "graphql.language.Field", + "graphql.language.FieldDefinition", + "graphql.language.FloatValue", + "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.IntValue", + "graphql.language.InterfaceTypeDefinition", + "graphql.language.InterfaceTypeExtensionDefinition", + "graphql.language.ListType", + "graphql.language.NamedNode", + "graphql.language.Node", + "graphql.language.NodeBuilder", + "graphql.language.NodeChildrenContainer", + "graphql.language.NodeDirectivesBuilder", + "graphql.language.NodeParentTree", + "graphql.language.NodeTraverser", + "graphql.language.NodeUtil", + "graphql.language.NodeVisitor", + "graphql.language.NodeVisitorStub", + "graphql.language.NonNullType", + "graphql.language.NullValue", + "graphql.language.ObjectField", + "graphql.language.ObjectTypeDefinition", + "graphql.language.ObjectTypeExtensionDefinition", + "graphql.language.ObjectValue", + "graphql.language.OperationDefinition", + "graphql.language.OperationTypeDefinition", + "graphql.language.PrettyAstPrinter", + "graphql.language.SDLDefinition", + "graphql.language.SDLExtensionDefinition", + "graphql.language.SDLNamedDefinition", + "graphql.language.ScalarTypeDefinition", + "graphql.language.ScalarTypeExtensionDefinition", + "graphql.language.ScalarValue", + "graphql.language.SchemaDefinition", + "graphql.language.SchemaExtensionDefinition", + "graphql.language.Selection", + "graphql.language.SelectionSet", + "graphql.language.SelectionSetContainer", + "graphql.language.SourceLocation", + "graphql.language.StringValue", + "graphql.language.Type", + "graphql.language.TypeDefinition", + "graphql.language.TypeKind", + "graphql.language.TypeName", + "graphql.language.UnionTypeDefinition", + "graphql.language.UnionTypeExtensionDefinition", + "graphql.language.Value", + "graphql.language.VariableDefinition", + "graphql.language.VariableReference", + "graphql.normalized.ArgumentMaker", + "graphql.normalized.ENFMerger", + "graphql.normalized.ExecutableNormalizedField", + "graphql.normalized.ExecutableNormalizedOperation", + "graphql.normalized.ExecutableNormalizedOperationFactory", + "graphql.normalized.ExecutableNormalizedOperationToAstCompiler", + "graphql.normalized.NormalizedInputValue", + "graphql.normalized.ValueToVariableValueCompiler", + "graphql.normalized.VariableAccumulator", + "graphql.normalized.VariablePredicate", + "graphql.normalized.VariableValueWithDefinition", + "graphql.normalized.incremental.NormalizedDeferredExecution", + "graphql.normalized.nf.NormalizedDocument", + "graphql.normalized.nf.NormalizedDocumentFactory", + "graphql.normalized.nf.NormalizedField", + "graphql.normalized.nf.NormalizedFieldsMerger", + "graphql.normalized.nf.NormalizedOperation", + "graphql.normalized.nf.NormalizedOperationToAstCompiler", + "graphql.parser.AntlrHelper", + "graphql.parser.CommentParser", + "graphql.parser.ExtendedBailStrategy", + "graphql.parser.GraphqlAntlrToLanguage", + "graphql.parser.InvalidSyntaxException", + "graphql.parser.MultiSourceReader", + "graphql.parser.NodeToRuleCapturingParser", + "graphql.parser.Parser", + "graphql.parser.ParserEnvironment", + "graphql.parser.ParserOptions", + "graphql.parser.ParsingListener", + "graphql.parser.SafeTokenReader", + "graphql.parser.SafeTokenSource", + "graphql.parser.StringValueParsing", + "graphql.parser.UnicodeUtil", + "graphql.parser.exceptions.InvalidUnicodeSyntaxException", + "graphql.parser.exceptions.MoreTokensSyntaxException", + "graphql.parser.exceptions.ParseCancelledException", + "graphql.parser.exceptions.ParseCancelledTooDeepException", + "graphql.parser.exceptions.ParseCancelledTooManyCharsException", + "graphql.relay.Connection", + "graphql.relay.ConnectionCursor", + "graphql.relay.DefaultConnection", + "graphql.relay.DefaultConnectionCursor", + "graphql.relay.DefaultEdge", + "graphql.relay.DefaultPageInfo", + "graphql.relay.Edge", + "graphql.relay.InvalidCursorException", + "graphql.relay.InvalidPageSizeException", + "graphql.relay.PageInfo", + "graphql.relay.Relay", + "graphql.relay.SimpleListConnection", + "graphql.scalar.CoercingUtil", + "graphql.scalar.GraphqlBooleanCoercing", + "graphql.scalar.GraphqlFloatCoercing", + "graphql.scalar.GraphqlIDCoercing", + "graphql.scalar.GraphqlIntCoercing", + "graphql.scalar.GraphqlStringCoercing", + "graphql.schema.AsyncDataFetcher", + "graphql.schema.CodeRegistryVisitor", + "graphql.schema.Coercing", + "graphql.schema.CoercingParseLiteralException", + "graphql.schema.CoercingParseValueException", + "graphql.schema.CoercingSerializeException", + "graphql.schema.DataFetcher", + "graphql.schema.DataFetcherFactories", + "graphql.schema.DataFetcherFactory", + "graphql.schema.DataFetcherFactoryEnvironment", + "graphql.schema.DataFetchingEnvironment", + "graphql.schema.DataFetchingEnvironmentImpl", + "graphql.schema.DataFetchingFieldSelectionSet", + "graphql.schema.DataFetchingFieldSelectionSetImpl", + "graphql.schema.DefaultGraphqlTypeComparatorRegistry", + "graphql.schema.DelegatingDataFetchingEnvironment", + "graphql.schema.FieldCoordinates", + "graphql.schema.GraphQLAppliedDirective", + "graphql.schema.GraphQLAppliedDirectiveArgument", + "graphql.schema.GraphQLArgument", + "graphql.schema.GraphQLCodeRegistry", + "graphql.schema.GraphQLCompositeType", + "graphql.schema.GraphQLDirective", + "graphql.schema.GraphQLDirectiveContainer", + "graphql.schema.GraphQLEnumType", + "graphql.schema.GraphQLEnumValueDefinition", + "graphql.schema.GraphQLFieldDefinition", + "graphql.schema.GraphQLFieldsContainer", + "graphql.schema.GraphQLImplementingType", + "graphql.schema.GraphQLInputFieldsContainer", + "graphql.schema.GraphQLInputObjectField", + "graphql.schema.GraphQLInputObjectType", + "graphql.schema.GraphQLInputSchemaElement", + "graphql.schema.GraphQLInputType", + "graphql.schema.GraphQLInputValueDefinition", + "graphql.schema.GraphQLInterfaceType", + "graphql.schema.GraphQLList", + "graphql.schema.GraphQLModifiedType", + "graphql.schema.GraphQLNamedInputType", + "graphql.schema.GraphQLNamedOutputType", + "graphql.schema.GraphQLNamedSchemaElement", + "graphql.schema.GraphQLNamedType", + "graphql.schema.GraphQLNonNull", + "graphql.schema.GraphQLNullableType", + "graphql.schema.GraphQLObjectType", + "graphql.schema.GraphQLOutputType", + "graphql.schema.GraphQLScalarType", + "graphql.schema.GraphQLSchema", + "graphql.schema.GraphQLSchemaElement", + "graphql.schema.GraphQLSchemaElementAdapter", + "graphql.schema.GraphQLType", + "graphql.schema.GraphQLTypeReference", + "graphql.schema.GraphQLTypeResolvingVisitor", + "graphql.schema.GraphQLTypeUtil", + "graphql.schema.GraphQLTypeVisitor", + "graphql.schema.GraphQLTypeVisitorStub", + "graphql.schema.GraphQLUnionType", + "graphql.schema.GraphQLUnmodifiedType", + "graphql.schema.GraphqlDirectivesContainerTypeBuilder", + "graphql.schema.GraphqlElementParentTree", + "graphql.schema.GraphqlTypeBuilder", + "graphql.schema.GraphqlTypeComparatorEnvironment", + "graphql.schema.GraphqlTypeComparatorRegistry", + "graphql.schema.GraphqlTypeComparators", + "graphql.schema.InputValueWithState", + "graphql.schema.LightDataFetcher", + "graphql.schema.PropertyDataFetcher", + "graphql.schema.PropertyDataFetcherHelper", + "graphql.schema.PropertyFetchingImpl", + "graphql.schema.SchemaElementChildrenContainer", + "graphql.schema.SchemaTransformer", + "graphql.schema.SchemaTraverser", + "graphql.schema.SelectedField", + "graphql.schema.SingletonPropertyDataFetcher", + "graphql.schema.StaticDataFetcher", + "graphql.schema.TypeResolver", + "graphql.schema.TypeResolverProxy", + "graphql.schema.diff.DiffCategory", + "graphql.schema.diff.DiffCtx", + "graphql.schema.diff.DiffEvent", + "graphql.schema.diff.DiffLevel", + "graphql.schema.diff.DiffSet", + "graphql.schema.diff.SchemaDiff", + "graphql.schema.diff.SchemaDiffSet", + "graphql.schema.diff.reporting.CapturingReporter", + "graphql.schema.diff.reporting.ChainedReporter", + "graphql.schema.diff.reporting.DifferenceReporter", + "graphql.schema.diff.reporting.PrintStreamReporter", + "graphql.schema.diffing.DiffImpl", + "graphql.schema.diffing.Edge", + "graphql.schema.diffing.EditOperation", + "graphql.schema.diffing.EditorialCostForMapping", + "graphql.schema.diffing.HungarianAlgorithm", + "graphql.schema.diffing.Mapping", + "graphql.schema.diffing.PossibleMappingsCalculator", + "graphql.schema.diffing.SchemaDiffing", + "graphql.schema.diffing.SchemaDiffingCancelledException", + "graphql.schema.diffing.SchemaDiffingRunningCheck", + "graphql.schema.diffing.SchemaGraph", + "graphql.schema.diffing.SchemaGraphFactory", + "graphql.schema.diffing.SortSourceGraph", + "graphql.schema.diffing.Util", + "graphql.schema.diffing.Vertex", + "graphql.schema.diffing.ana.EditOperationAnalysisResult", + "graphql.schema.diffing.ana.EditOperationAnalyzer", + "graphql.schema.diffing.ana.SchemaDifference", + "graphql.schema.fetching.LambdaFetchingSupport", + "graphql.schema.idl.ArgValueOfAllowedTypeChecker", + "graphql.schema.idl.CombinedWiringFactory", + "graphql.schema.idl.DirectiveInfo", + "graphql.schema.idl.EchoingWiringFactory", + "graphql.schema.idl.EnumValuesProvider", + "graphql.schema.idl.FieldWiringEnvironment", + "graphql.schema.idl.ImplementingTypesChecker", + "graphql.schema.idl.InterfaceWiringEnvironment", + "graphql.schema.idl.MapEnumValuesProvider", + "graphql.schema.idl.MockedWiringFactory", + "graphql.schema.idl.NaturalEnumValuesProvider", + "graphql.schema.idl.NoopWiringFactory", + "graphql.schema.idl.RuntimeWiring", + "graphql.schema.idl.ScalarInfo", + "graphql.schema.idl.ScalarWiringEnvironment", + "graphql.schema.idl.SchemaDirectiveWiring", + "graphql.schema.idl.SchemaDirectiveWiringEnvironment", + "graphql.schema.idl.SchemaDirectiveWiringEnvironmentImpl", + "graphql.schema.idl.SchemaDirectiveWiringSchemaGeneratorPostProcessing", + "graphql.schema.idl.SchemaExtensionsChecker", + "graphql.schema.idl.SchemaGenerator", + "graphql.schema.idl.SchemaGeneratorAppliedDirectiveHelper", + "graphql.schema.idl.SchemaGeneratorDirectiveHelper", + "graphql.schema.idl.SchemaGeneratorHelper", + "graphql.schema.idl.SchemaParseOrder", + "graphql.schema.idl.SchemaParser", + "graphql.schema.idl.SchemaPrinter", + "graphql.schema.idl.SchemaTypeChecker", + "graphql.schema.idl.SchemaTypeDirectivesChecker", + "graphql.schema.idl.SchemaTypeExtensionsChecker", + "graphql.schema.idl.TypeDefinitionRegistry", + "graphql.schema.idl.TypeInfo", + "graphql.schema.idl.TypeRuntimeWiring", + "graphql.schema.idl.TypeUtil", + "graphql.schema.idl.UnExecutableSchemaGenerator", + "graphql.schema.idl.UnionTypesChecker", + "graphql.schema.idl.UnionWiringEnvironment", + "graphql.schema.idl.WiringEnvironment", + "graphql.schema.idl.WiringFactory", + "graphql.schema.idl.errors.BaseError", + "graphql.schema.idl.errors.DirectiveIllegalArgumentTypeError", + "graphql.schema.idl.errors.DirectiveIllegalLocationError", + "graphql.schema.idl.errors.DirectiveIllegalReferenceError", + "graphql.schema.idl.errors.DirectiveMissingNonNullArgumentError", + "graphql.schema.idl.errors.DirectiveRedefinitionError", + "graphql.schema.idl.errors.DirectiveUndeclaredError", + "graphql.schema.idl.errors.DirectiveUnknownArgumentError", + "graphql.schema.idl.errors.IllegalNameError", + "graphql.schema.idl.errors.InterfaceFieldArgumentNotOptionalError", + "graphql.schema.idl.errors.InterfaceFieldArgumentRedefinitionError", + "graphql.schema.idl.errors.InterfaceFieldRedefinitionError", + "graphql.schema.idl.errors.InterfaceImplementedMoreThanOnceError", + "graphql.schema.idl.errors.InterfaceImplementingItselfError", + "graphql.schema.idl.errors.InterfaceWithCircularImplementationHierarchyError", + "graphql.schema.idl.errors.MissingInterfaceFieldArgumentsError", + "graphql.schema.idl.errors.MissingInterfaceFieldError", + "graphql.schema.idl.errors.MissingInterfaceTypeError", + "graphql.schema.idl.errors.MissingScalarImplementationError", + "graphql.schema.idl.errors.MissingTransitiveInterfaceError", + "graphql.schema.idl.errors.MissingTypeError", + "graphql.schema.idl.errors.MissingTypeResolverError", + "graphql.schema.idl.errors.NonSDLDefinitionError", + "graphql.schema.idl.errors.NonUniqueArgumentError", + "graphql.schema.idl.errors.NonUniqueDirectiveError", + "graphql.schema.idl.errors.NonUniqueNameError", + "graphql.schema.idl.errors.NotAnInputTypeError", + "graphql.schema.idl.errors.NotAnOutputTypeError", + "graphql.schema.idl.errors.OperationRedefinitionError", + "graphql.schema.idl.errors.OperationTypesMustBeObjects", + "graphql.schema.idl.errors.QueryOperationMissingError", + "graphql.schema.idl.errors.SchemaMissingError", + "graphql.schema.idl.errors.SchemaProblem", + "graphql.schema.idl.errors.SchemaRedefinitionError", + "graphql.schema.idl.errors.StrictModeWiringException", + "graphql.schema.idl.errors.TypeExtensionDirectiveRedefinitionError", + "graphql.schema.idl.errors.TypeExtensionEnumValueRedefinitionError", + "graphql.schema.idl.errors.TypeExtensionFieldRedefinitionError", + "graphql.schema.idl.errors.TypeExtensionMissingBaseTypeError", + "graphql.schema.idl.errors.TypeRedefinitionError", + "graphql.schema.idl.errors.UnionTypeError", + "graphql.schema.impl.GraphQLTypeCollectingVisitor", + "graphql.schema.impl.MultiReadOnlyGraphQLTypeVisitor", + "graphql.schema.impl.SchemaUtil", + "graphql.schema.impl.StronglyConnectedComponentsTopologicallySorted", + "graphql.schema.transform.FieldVisibilitySchemaTransformation", + "graphql.schema.transform.VisibleFieldPredicate", + "graphql.schema.transform.VisibleFieldPredicateEnvironment", + "graphql.schema.usage.SchemaUsage", + "graphql.schema.usage.SchemaUsageSupport", + "graphql.schema.validation.AppliedDirectiveArgumentsAreValid", + "graphql.schema.validation.AppliedDirectivesAreValid", + "graphql.schema.validation.DefaultValuesAreValid", + "graphql.schema.validation.DeprecatedInputObjectAndArgumentsAreValid", + "graphql.schema.validation.InputAndOutputTypesUsedAppropriately", + "graphql.schema.validation.InvalidSchemaException", + "graphql.schema.validation.NoUnbrokenInputCycles", + "graphql.schema.validation.OneOfInputObjectRules", + "graphql.schema.validation.SchemaValidationError", + "graphql.schema.validation.SchemaValidationErrorClassification", + "graphql.schema.validation.SchemaValidationErrorCollector", + "graphql.schema.validation.SchemaValidationErrorType", + "graphql.schema.validation.SchemaValidator", + "graphql.schema.validation.TypeAndFieldRule", + "graphql.schema.validation.TypesImplementInterfaces", + "graphql.schema.visibility.BlockedFields", + "graphql.schema.visibility.DefaultGraphqlFieldVisibility", + "graphql.schema.visibility.GraphqlFieldVisibility", + "graphql.schema.visibility.NoIntrospectionGraphqlFieldVisibility", + "graphql.schema.visitor.GraphQLSchemaTraversalControl", + "graphql.schema.visitor.GraphQLSchemaVisitor", + "graphql.schema.visitor.GraphQLSchemaVisitorAdapter", + "graphql.schema.visitor.GraphQLSchemaVisitorEnvironment", + "graphql.schema.visitor.GraphQLSchemaVisitorEnvironmentImpl", + "graphql.util.Anonymizer", + "graphql.util.Breadcrumb", + "graphql.util.CyclicSchemaAnalyzer", + "graphql.util.DefaultTraverserContext", + "graphql.util.EscapeUtil", + "graphql.util.FpKit", + "graphql.util.IdGenerator", + "graphql.util.InterThreadMemoizedSupplier", + "graphql.util.Interning", + "graphql.util.IntraThreadMemoizedSupplier", + "graphql.util.LockKit", + "graphql.util.MutableRef", + "graphql.util.NodeAdapter", + "graphql.util.NodeLocation", + "graphql.util.NodeMultiZipper", + "graphql.util.NodeZipper", + "graphql.util.Pair", + "graphql.util.ReplaceNode", + "graphql.util.StringKit", + "graphql.util.TraversalControl", + "graphql.util.Traverser", + "graphql.util.TraverserContext", + "graphql.util.TraverserResult", + "graphql.util.TraverserState", + "graphql.util.TraverserVisitor", + "graphql.util.TraverserVisitorStub", + "graphql.util.TreeParallelTransformer", + "graphql.util.TreeParallelTraverser", + "graphql.util.TreeTransformer", + "graphql.util.TreeTransformerUtil", + "graphql.validation.AbstractRule", + "graphql.validation.ArgumentValidationUtil", + "graphql.validation.DocumentVisitor", + "graphql.validation.LanguageTraversal", + "graphql.validation.RulesVisitor", + "graphql.validation.TraversalContext", + "graphql.validation.ValidationContext", + "graphql.validation.ValidationError", + "graphql.validation.ValidationErrorClassification", + "graphql.validation.ValidationErrorCollector", + "graphql.validation.ValidationErrorType", + "graphql.validation.ValidationUtil", + "graphql.validation.Validator", + "graphql.validation.rules.ArgumentsOfCorrectType", + "graphql.validation.rules.DeferDirectiveLabel", + "graphql.validation.rules.DeferDirectiveOnRootLevel", + "graphql.validation.rules.DeferDirectiveOnValidOperation", + "graphql.validation.rules.ExecutableDefinitions", + "graphql.validation.rules.FieldsOnCorrectType", + "graphql.validation.rules.FragmentsOnCompositeType", + "graphql.validation.rules.KnownArgumentNames", + "graphql.validation.rules.KnownDirectives", + "graphql.validation.rules.KnownFragmentNames", + "graphql.validation.rules.KnownOperationTypes", + "graphql.validation.rules.KnownTypeNames", + "graphql.validation.rules.LoneAnonymousOperation", + "graphql.validation.rules.NoFragmentCycles", + "graphql.validation.rules.NoUndefinedVariables", + "graphql.validation.rules.NoUnusedFragments", + "graphql.validation.rules.NoUnusedVariables", + "graphql.validation.rules.OverlappingFieldsCanBeMerged", + "graphql.validation.rules.PossibleFragmentSpreads", + "graphql.validation.rules.ProvidedNonNullArguments", + "graphql.validation.rules.ScalarLeaves", + "graphql.validation.rules.SubscriptionRootField", + "graphql.validation.rules.UniqueArgumentNames", + "graphql.validation.rules.UniqueDirectiveNamesPerLocation", + "graphql.validation.rules.UniqueFragmentNames", + "graphql.validation.rules.UniqueObjectFieldName", + "graphql.validation.rules.UniqueOperationNames", + "graphql.validation.rules.UniqueVariableNames", + "graphql.validation.rules.VariableDefaultValuesOfCorrectType", + "graphql.validation.rules.VariableTypesMatch", + "graphql.validation.rules.VariablesAreInputTypes", + "graphql.validation.rules.VariablesTypesMatcher", + ] + + def "should ensure all new classes have @NullMarked annotation"() { + given: + // Get all classes in the graphql package + JavaClasses allClasses = new ClassFileImporter() + .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) + .importPackages("graphql") + + // Filter out allowlisted classes + def newClasses = allClasses.stream() + .filter { javaClass -> !ALLOWLIST.contains(javaClass.name) } + .collect() + + when: + List classesMissingAnnotation = [] + + newClasses.each { JavaClass javaClass -> + // Check if class has @NullMarked annotation + if (!javaClass.isAnnotatedWith("org.jspecify.annotations.NullMarked")) { + classesMissingAnnotation.add(javaClass.name) + } + } + + then: + classesMissingAnnotation.isEmpty() + + cleanup: "if the test fails, provide detailed information about which classes have violations" + if (!classesMissingAnnotation.isEmpty()) { + def errorMessage = new StringBuilder("Classes missing @NullMarked annotation:\n") + + classesMissingAnnotation.each { className -> + errorMessage.append(" - ${className}\n") + } + + errorMessage.append("\nTo fix these issues:\n") + errorMessage.append("1. Add @NullMarked to classes\n") + errorMessage.append("2. Add @Nullable annotations for nullable elements within that class\n") + + println errorMessage.toString() + } + } +} \ No newline at end of file From a1c31fadf3900f8e882127a77eb8b18086a79751 Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Sat, 5 Apr 2025 17:14:24 +1100 Subject: [PATCH 02/15] Only check Public API and Experimental API classes --- .../graphql/JSpecifyAnnotationsCheck.groovy | 701 +----------------- 1 file changed, 18 insertions(+), 683 deletions(-) diff --git a/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy b/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy index 0d6f0f0fbd..22fb8ed253 100644 --- a/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy +++ b/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy @@ -1,706 +1,41 @@ package graphql -import com.tngtech.archunit.core.domain.JavaClass -import com.tngtech.archunit.core.domain.JavaClasses import com.tngtech.archunit.core.importer.ClassFileImporter import com.tngtech.archunit.core.importer.ImportOption import spock.lang.Specification /** - * This test ensures that all NEW classes in the graphql package are properly annotated with JSpecify annotations. - * It checks for the presence of @NullMarked annotation on classes. - * - * Classes can be exempted from this check by adding them to the ALLOWLIST. + * This test ensures that all public API and experimental API classes in the graphql package + * are properly annotated with JSpecify annotations. */ class JSpecifyAnnotationsCheck extends Specification { - // Allowlist of existing classes that are exempt from JSpecify annotation checks - private static final Set ALLOWLIST = [ - "graphql.Assert", - "graphql.AssertException", - "graphql.Directives", - "graphql.DirectivesUtil", - "graphql.DuckTyped", - "graphql.ErrorClassification", - "graphql.ErrorType", - "graphql.ExceptionWhileDataFetching", - "graphql.ExecutionInput", - "graphql.ExecutionResult", - "graphql.ExecutionResultImpl", - "graphql.ExperimentalApi", - "graphql.GraphQL", - "graphql.GraphQLContext", - "graphql.GraphQLError", - "graphql.GraphQLException", - "graphql.GraphqlErrorBuilder", - "graphql.GraphqlErrorException", - "graphql.GraphqlErrorHelper", - "graphql.Internal", - "graphql.InvalidSyntaxError", - "graphql.Mutable", - "graphql.ParseAndValidate", - "graphql.ParseAndValidateResult", - "graphql.PublicApi", - "graphql.PublicSpi", - "graphql.Scalars", - "graphql.SerializationError", - "graphql.ThreadSafe", - "graphql.TrivialDataFetcher", - "graphql.TypeMismatchError", - "graphql.TypeResolutionEnvironment", - "graphql.UnresolvedTypeError", - "graphql.VisibleForTesting", - "graphql.agent.result.ExecutionTrackingResult", - "graphql.analysis.FieldComplexityCalculator", - "graphql.analysis.FieldComplexityEnvironment", - "graphql.analysis.MaxQueryComplexityInstrumentation", - "graphql.analysis.MaxQueryDepthInstrumentation", - "graphql.analysis.NodeVisitorWithTypeTracking", - "graphql.analysis.QueryComplexityCalculator", - "graphql.analysis.QueryComplexityInfo", - "graphql.analysis.QueryDepthInfo", - "graphql.analysis.QueryReducer", - "graphql.analysis.QueryTransformer", - "graphql.analysis.QueryTraversalContext", - "graphql.analysis.QueryTraversalOptions", - "graphql.analysis.QueryTraverser", - "graphql.analysis.QueryVisitor", - "graphql.analysis.QueryVisitorFieldArgumentEnvironment", - "graphql.analysis.QueryVisitorFieldArgumentEnvironmentImpl", - "graphql.analysis.QueryVisitorFieldArgumentInputValue", - "graphql.analysis.QueryVisitorFieldArgumentInputValueImpl", - "graphql.analysis.QueryVisitorFieldArgumentValueEnvironment", - "graphql.analysis.QueryVisitorFieldArgumentValueEnvironmentImpl", - "graphql.analysis.QueryVisitorFieldEnvironment", - "graphql.analysis.QueryVisitorFieldEnvironmentImpl", - "graphql.analysis.QueryVisitorFragmentDefinitionEnvironment", - "graphql.analysis.QueryVisitorFragmentDefinitionEnvironmentImpl", - "graphql.analysis.QueryVisitorFragmentSpreadEnvironment", - "graphql.analysis.QueryVisitorFragmentSpreadEnvironmentImpl", - "graphql.analysis.QueryVisitorInlineFragmentEnvironment", - "graphql.analysis.QueryVisitorInlineFragmentEnvironmentImpl", - "graphql.analysis.QueryVisitorStub", - "graphql.analysis.values.ValueTraverser", - "graphql.analysis.values.ValueVisitor", - "graphql.collect.ImmutableKit", - "graphql.collect.ImmutableMapWithNullValues", - "graphql.execution.AbortExecutionException", - "graphql.execution.AbstractAsyncExecutionStrategy", - "graphql.execution.Async", - "graphql.execution.AsyncExecutionStrategy", - "graphql.execution.AsyncSerialExecutionStrategy", - "graphql.execution.CoercedVariables", - "graphql.execution.DataFetcherExceptionHandler", - "graphql.execution.DataFetcherExceptionHandlerParameters", - "graphql.execution.DataFetcherExceptionHandlerResult", - "graphql.execution.DataFetcherResult", - "graphql.execution.DataLoaderDispatchStrategy", - "graphql.execution.DefaultValueUnboxer", - "graphql.execution.EngineRunningObserver", - "graphql.execution.Execution", - "graphql.execution.ExecutionContext", - "graphql.execution.ExecutionContextBuilder", - "graphql.execution.ExecutionId", - "graphql.execution.ExecutionIdProvider", - "graphql.execution.ExecutionStepInfo", - "graphql.execution.ExecutionStepInfoFactory", - "graphql.execution.ExecutionStrategy", - "graphql.execution.ExecutionStrategyParameters", - "graphql.execution.FetchedValue", - "graphql.execution.FieldCollector", - "graphql.execution.FieldCollectorParameters", - "graphql.execution.FieldValueInfo", - "graphql.execution.InputMapDefinesTooManyFieldsException", - "graphql.execution.MergedField", - "graphql.execution.MergedSelectionSet", - "graphql.execution.MissingRootTypeException", - "graphql.execution.NonNullableFieldValidator", - "graphql.execution.NonNullableFieldWasNullError", - "graphql.execution.NonNullableFieldWasNullException", - "graphql.execution.NonNullableValueCoercedAsNullException", - "graphql.execution.NormalizedVariables", - "graphql.execution.OneOfNullValueException", - "graphql.execution.OneOfTooManyKeysException", - "graphql.execution.RawVariables", - "graphql.execution.ResolveType", - "graphql.execution.ResultNodesInfo", - "graphql.execution.ResultPath", - "graphql.execution.SimpleDataFetcherExceptionHandler", - "graphql.execution.SubscriptionExecutionStrategy", - "graphql.execution.TypeFromAST", - "graphql.execution.TypeResolutionParameters", - "graphql.execution.UnknownOperationException", - "graphql.execution.UnresolvedTypeException", - "graphql.execution.ValueUnboxer", - "graphql.execution.ValuesResolver", - "graphql.execution.ValuesResolverConversion", - "graphql.execution.ValuesResolverLegacy", - "graphql.execution.ValuesResolverOneOfValidation", - "graphql.execution.conditional.ConditionalNodeDecision", - "graphql.execution.conditional.ConditionalNodeDecisionEnvironment", - "graphql.execution.conditional.ConditionalNodes", - "graphql.execution.directives.DirectivesResolver", - "graphql.execution.directives.QueryAppliedDirective", - "graphql.execution.directives.QueryAppliedDirectiveArgument", - "graphql.execution.directives.QueryDirectives", - "graphql.execution.directives.QueryDirectivesBuilder", - "graphql.execution.directives.QueryDirectivesImpl", - "graphql.execution.incremental.DeferredCallContext", - "graphql.execution.incremental.DeferredExecution", - "graphql.execution.incremental.DeferredExecutionSupport", - "graphql.execution.incremental.DeferredFragmentCall", - "graphql.execution.incremental.IncrementalCall", - "graphql.execution.incremental.IncrementalCallState", - "graphql.execution.incremental.IncrementalUtils", - "graphql.execution.incremental.StreamedCall", - "graphql.execution.instrumentation.ChainedInstrumentation", - "graphql.execution.instrumentation.DocumentAndVariables", - "graphql.execution.instrumentation.ExecuteObjectInstrumentationContext", - "graphql.execution.instrumentation.ExecutionStrategyInstrumentationContext", - "graphql.execution.instrumentation.FieldFetchingInstrumentationContext", - "graphql.execution.instrumentation.Instrumentation", - "graphql.execution.instrumentation.InstrumentationContext", - "graphql.execution.instrumentation.InstrumentationState", - "graphql.execution.instrumentation.NoContextChainedInstrumentation", - "graphql.execution.instrumentation.SimpleInstrumentation", - "graphql.execution.instrumentation.SimpleInstrumentationContext", - "graphql.execution.instrumentation.SimplePerformantInstrumentation", - "graphql.execution.instrumentation.dataloader.EmptyDataLoaderRegistryInstance", - "graphql.execution.instrumentation.dataloader.FallbackDataLoaderDispatchStrategy", - "graphql.execution.instrumentation.dataloader.LevelMap", - "graphql.execution.instrumentation.dataloader.PerLevelDataLoaderDispatchStrategy", - "graphql.execution.instrumentation.dataloader.PerLevelDataLoaderDispatchStrategyWithDeferAlwaysDispatch", - "graphql.execution.instrumentation.fieldvalidation.FieldAndArguments", - "graphql.execution.instrumentation.fieldvalidation.FieldValidation", - "graphql.execution.instrumentation.fieldvalidation.FieldValidationEnvironment", - "graphql.execution.instrumentation.fieldvalidation.FieldValidationInstrumentation", - "graphql.execution.instrumentation.fieldvalidation.FieldValidationSupport", - "graphql.execution.instrumentation.fieldvalidation.SimpleFieldValidation", - "graphql.execution.instrumentation.parameters.InstrumentationCreateStateParameters", - "graphql.execution.instrumentation.parameters.InstrumentationExecuteOperationParameters", - "graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters", - "graphql.execution.instrumentation.parameters.InstrumentationExecutionStrategyParameters", - "graphql.execution.instrumentation.parameters.InstrumentationFieldCompleteParameters", - "graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters", - "graphql.execution.instrumentation.parameters.InstrumentationFieldParameters", - "graphql.execution.instrumentation.parameters.InstrumentationValidationParameters", - "graphql.execution.instrumentation.tracing.TracingInstrumentation", - "graphql.execution.instrumentation.tracing.TracingSupport", - "graphql.execution.preparsed.NoOpPreparsedDocumentProvider", - "graphql.execution.preparsed.PreparsedDocumentEntry", - "graphql.execution.preparsed.PreparsedDocumentProvider", - "graphql.execution.preparsed.persisted.ApolloPersistedQuerySupport", - "graphql.execution.preparsed.persisted.InMemoryPersistedQueryCache", - "graphql.execution.preparsed.persisted.PersistedQueryCache", - "graphql.execution.preparsed.persisted.PersistedQueryCacheMiss", - "graphql.execution.preparsed.persisted.PersistedQueryError", - "graphql.execution.preparsed.persisted.PersistedQueryIdInvalid", - "graphql.execution.preparsed.persisted.PersistedQueryNotFound", - "graphql.execution.preparsed.persisted.PersistedQuerySupport", - "graphql.execution.reactive.CompletionStageMappingOrderedPublisher", - "graphql.execution.reactive.CompletionStageMappingPublisher", - "graphql.execution.reactive.CompletionStageOrderedSubscriber", - "graphql.execution.reactive.CompletionStageSubscriber", - "graphql.execution.reactive.DelegatingSubscription", - "graphql.execution.reactive.NonBlockingMutexExecutor", - "graphql.execution.reactive.ReactiveSupport", - "graphql.execution.reactive.SingleSubscriberPublisher", - "graphql.execution.reactive.SubscriptionPublisher", - "graphql.execution.values.InputInterceptor", - "graphql.execution.values.legacycoercing.LegacyCoercingInputInterceptor", - "graphql.extensions.DefaultExtensionsMerger", - "graphql.extensions.ExtensionsBuilder", - "graphql.extensions.ExtensionsMerger", - "graphql.i18n.I18n", - "graphql.i18n.I18nMsg", - "graphql.incremental.DeferPayload", - "graphql.incremental.DelayedIncrementalPartialResult", - "graphql.incremental.DelayedIncrementalPartialResultImpl", - "graphql.incremental.IncrementalExecutionResult", - "graphql.incremental.IncrementalExecutionResultImpl", - "graphql.incremental.IncrementalPayload", - "graphql.incremental.StreamPayload", - "graphql.introspection.GoodFaithIntrospection", - "graphql.introspection.Introspection", - "graphql.introspection.IntrospectionDataFetcher", - "graphql.introspection.IntrospectionDataFetchingEnvironment", - "graphql.introspection.IntrospectionDisabledError", - "graphql.introspection.IntrospectionQuery", - "graphql.introspection.IntrospectionQueryBuilder", - "graphql.introspection.IntrospectionResultToSchema", - "graphql.introspection.IntrospectionWithDirectivesSupport", - "graphql.language.AbstractDescribedNode", - "graphql.language.AbstractNode", - "graphql.language.Argument", - "graphql.language.ArrayValue", - "graphql.language.AstComparator", - "graphql.language.AstNodeAdapter", - "graphql.language.AstPrinter", - "graphql.language.AstSignature", - "graphql.language.AstSorter", - "graphql.language.AstTransformer", - "graphql.language.BooleanValue", - "graphql.language.Comment", - "graphql.language.Definition", - "graphql.language.DescribedNode", - "graphql.language.Description", - "graphql.language.Directive", - "graphql.language.DirectiveDefinition", - "graphql.language.DirectiveLocation", - "graphql.language.DirectivesContainer", - "graphql.language.Document", - "graphql.language.EnumTypeDefinition", - "graphql.language.EnumTypeExtensionDefinition", - "graphql.language.EnumValue", - "graphql.language.EnumValueDefinition", - "graphql.language.Field", - "graphql.language.FieldDefinition", - "graphql.language.FloatValue", - "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.IntValue", - "graphql.language.InterfaceTypeDefinition", - "graphql.language.InterfaceTypeExtensionDefinition", - "graphql.language.ListType", - "graphql.language.NamedNode", - "graphql.language.Node", - "graphql.language.NodeBuilder", - "graphql.language.NodeChildrenContainer", - "graphql.language.NodeDirectivesBuilder", - "graphql.language.NodeParentTree", - "graphql.language.NodeTraverser", - "graphql.language.NodeUtil", - "graphql.language.NodeVisitor", - "graphql.language.NodeVisitorStub", - "graphql.language.NonNullType", - "graphql.language.NullValue", - "graphql.language.ObjectField", - "graphql.language.ObjectTypeDefinition", - "graphql.language.ObjectTypeExtensionDefinition", - "graphql.language.ObjectValue", - "graphql.language.OperationDefinition", - "graphql.language.OperationTypeDefinition", - "graphql.language.PrettyAstPrinter", - "graphql.language.SDLDefinition", - "graphql.language.SDLExtensionDefinition", - "graphql.language.SDLNamedDefinition", - "graphql.language.ScalarTypeDefinition", - "graphql.language.ScalarTypeExtensionDefinition", - "graphql.language.ScalarValue", - "graphql.language.SchemaDefinition", - "graphql.language.SchemaExtensionDefinition", - "graphql.language.Selection", - "graphql.language.SelectionSet", - "graphql.language.SelectionSetContainer", - "graphql.language.SourceLocation", - "graphql.language.StringValue", - "graphql.language.Type", - "graphql.language.TypeDefinition", - "graphql.language.TypeKind", - "graphql.language.TypeName", - "graphql.language.UnionTypeDefinition", - "graphql.language.UnionTypeExtensionDefinition", - "graphql.language.Value", - "graphql.language.VariableDefinition", - "graphql.language.VariableReference", - "graphql.normalized.ArgumentMaker", - "graphql.normalized.ENFMerger", - "graphql.normalized.ExecutableNormalizedField", - "graphql.normalized.ExecutableNormalizedOperation", - "graphql.normalized.ExecutableNormalizedOperationFactory", - "graphql.normalized.ExecutableNormalizedOperationToAstCompiler", - "graphql.normalized.NormalizedInputValue", - "graphql.normalized.ValueToVariableValueCompiler", - "graphql.normalized.VariableAccumulator", - "graphql.normalized.VariablePredicate", - "graphql.normalized.VariableValueWithDefinition", - "graphql.normalized.incremental.NormalizedDeferredExecution", - "graphql.normalized.nf.NormalizedDocument", - "graphql.normalized.nf.NormalizedDocumentFactory", - "graphql.normalized.nf.NormalizedField", - "graphql.normalized.nf.NormalizedFieldsMerger", - "graphql.normalized.nf.NormalizedOperation", - "graphql.normalized.nf.NormalizedOperationToAstCompiler", - "graphql.parser.AntlrHelper", - "graphql.parser.CommentParser", - "graphql.parser.ExtendedBailStrategy", - "graphql.parser.GraphqlAntlrToLanguage", - "graphql.parser.InvalidSyntaxException", - "graphql.parser.MultiSourceReader", - "graphql.parser.NodeToRuleCapturingParser", - "graphql.parser.Parser", - "graphql.parser.ParserEnvironment", - "graphql.parser.ParserOptions", - "graphql.parser.ParsingListener", - "graphql.parser.SafeTokenReader", - "graphql.parser.SafeTokenSource", - "graphql.parser.StringValueParsing", - "graphql.parser.UnicodeUtil", - "graphql.parser.exceptions.InvalidUnicodeSyntaxException", - "graphql.parser.exceptions.MoreTokensSyntaxException", - "graphql.parser.exceptions.ParseCancelledException", - "graphql.parser.exceptions.ParseCancelledTooDeepException", - "graphql.parser.exceptions.ParseCancelledTooManyCharsException", - "graphql.relay.Connection", - "graphql.relay.ConnectionCursor", - "graphql.relay.DefaultConnection", - "graphql.relay.DefaultConnectionCursor", - "graphql.relay.DefaultEdge", - "graphql.relay.DefaultPageInfo", - "graphql.relay.Edge", - "graphql.relay.InvalidCursorException", - "graphql.relay.InvalidPageSizeException", - "graphql.relay.PageInfo", - "graphql.relay.Relay", - "graphql.relay.SimpleListConnection", - "graphql.scalar.CoercingUtil", - "graphql.scalar.GraphqlBooleanCoercing", - "graphql.scalar.GraphqlFloatCoercing", - "graphql.scalar.GraphqlIDCoercing", - "graphql.scalar.GraphqlIntCoercing", - "graphql.scalar.GraphqlStringCoercing", - "graphql.schema.AsyncDataFetcher", - "graphql.schema.CodeRegistryVisitor", - "graphql.schema.Coercing", - "graphql.schema.CoercingParseLiteralException", - "graphql.schema.CoercingParseValueException", - "graphql.schema.CoercingSerializeException", - "graphql.schema.DataFetcher", - "graphql.schema.DataFetcherFactories", - "graphql.schema.DataFetcherFactory", - "graphql.schema.DataFetcherFactoryEnvironment", - "graphql.schema.DataFetchingEnvironment", - "graphql.schema.DataFetchingEnvironmentImpl", - "graphql.schema.DataFetchingFieldSelectionSet", - "graphql.schema.DataFetchingFieldSelectionSetImpl", - "graphql.schema.DefaultGraphqlTypeComparatorRegistry", - "graphql.schema.DelegatingDataFetchingEnvironment", - "graphql.schema.FieldCoordinates", - "graphql.schema.GraphQLAppliedDirective", - "graphql.schema.GraphQLAppliedDirectiveArgument", - "graphql.schema.GraphQLArgument", - "graphql.schema.GraphQLCodeRegistry", - "graphql.schema.GraphQLCompositeType", - "graphql.schema.GraphQLDirective", - "graphql.schema.GraphQLDirectiveContainer", - "graphql.schema.GraphQLEnumType", - "graphql.schema.GraphQLEnumValueDefinition", - "graphql.schema.GraphQLFieldDefinition", - "graphql.schema.GraphQLFieldsContainer", - "graphql.schema.GraphQLImplementingType", - "graphql.schema.GraphQLInputFieldsContainer", - "graphql.schema.GraphQLInputObjectField", - "graphql.schema.GraphQLInputObjectType", - "graphql.schema.GraphQLInputSchemaElement", - "graphql.schema.GraphQLInputType", - "graphql.schema.GraphQLInputValueDefinition", - "graphql.schema.GraphQLInterfaceType", - "graphql.schema.GraphQLList", - "graphql.schema.GraphQLModifiedType", - "graphql.schema.GraphQLNamedInputType", - "graphql.schema.GraphQLNamedOutputType", - "graphql.schema.GraphQLNamedSchemaElement", - "graphql.schema.GraphQLNamedType", - "graphql.schema.GraphQLNonNull", - "graphql.schema.GraphQLNullableType", - "graphql.schema.GraphQLObjectType", - "graphql.schema.GraphQLOutputType", - "graphql.schema.GraphQLScalarType", - "graphql.schema.GraphQLSchema", - "graphql.schema.GraphQLSchemaElement", - "graphql.schema.GraphQLSchemaElementAdapter", - "graphql.schema.GraphQLType", - "graphql.schema.GraphQLTypeReference", - "graphql.schema.GraphQLTypeResolvingVisitor", - "graphql.schema.GraphQLTypeUtil", - "graphql.schema.GraphQLTypeVisitor", - "graphql.schema.GraphQLTypeVisitorStub", - "graphql.schema.GraphQLUnionType", - "graphql.schema.GraphQLUnmodifiedType", - "graphql.schema.GraphqlDirectivesContainerTypeBuilder", - "graphql.schema.GraphqlElementParentTree", - "graphql.schema.GraphqlTypeBuilder", - "graphql.schema.GraphqlTypeComparatorEnvironment", - "graphql.schema.GraphqlTypeComparatorRegistry", - "graphql.schema.GraphqlTypeComparators", - "graphql.schema.InputValueWithState", - "graphql.schema.LightDataFetcher", - "graphql.schema.PropertyDataFetcher", - "graphql.schema.PropertyDataFetcherHelper", - "graphql.schema.PropertyFetchingImpl", - "graphql.schema.SchemaElementChildrenContainer", - "graphql.schema.SchemaTransformer", - "graphql.schema.SchemaTraverser", - "graphql.schema.SelectedField", - "graphql.schema.SingletonPropertyDataFetcher", - "graphql.schema.StaticDataFetcher", - "graphql.schema.TypeResolver", - "graphql.schema.TypeResolverProxy", - "graphql.schema.diff.DiffCategory", - "graphql.schema.diff.DiffCtx", - "graphql.schema.diff.DiffEvent", - "graphql.schema.diff.DiffLevel", - "graphql.schema.diff.DiffSet", - "graphql.schema.diff.SchemaDiff", - "graphql.schema.diff.SchemaDiffSet", - "graphql.schema.diff.reporting.CapturingReporter", - "graphql.schema.diff.reporting.ChainedReporter", - "graphql.schema.diff.reporting.DifferenceReporter", - "graphql.schema.diff.reporting.PrintStreamReporter", - "graphql.schema.diffing.DiffImpl", - "graphql.schema.diffing.Edge", - "graphql.schema.diffing.EditOperation", - "graphql.schema.diffing.EditorialCostForMapping", - "graphql.schema.diffing.HungarianAlgorithm", - "graphql.schema.diffing.Mapping", - "graphql.schema.diffing.PossibleMappingsCalculator", - "graphql.schema.diffing.SchemaDiffing", - "graphql.schema.diffing.SchemaDiffingCancelledException", - "graphql.schema.diffing.SchemaDiffingRunningCheck", - "graphql.schema.diffing.SchemaGraph", - "graphql.schema.diffing.SchemaGraphFactory", - "graphql.schema.diffing.SortSourceGraph", - "graphql.schema.diffing.Util", - "graphql.schema.diffing.Vertex", - "graphql.schema.diffing.ana.EditOperationAnalysisResult", - "graphql.schema.diffing.ana.EditOperationAnalyzer", - "graphql.schema.diffing.ana.SchemaDifference", - "graphql.schema.fetching.LambdaFetchingSupport", - "graphql.schema.idl.ArgValueOfAllowedTypeChecker", - "graphql.schema.idl.CombinedWiringFactory", - "graphql.schema.idl.DirectiveInfo", - "graphql.schema.idl.EchoingWiringFactory", - "graphql.schema.idl.EnumValuesProvider", - "graphql.schema.idl.FieldWiringEnvironment", - "graphql.schema.idl.ImplementingTypesChecker", - "graphql.schema.idl.InterfaceWiringEnvironment", - "graphql.schema.idl.MapEnumValuesProvider", - "graphql.schema.idl.MockedWiringFactory", - "graphql.schema.idl.NaturalEnumValuesProvider", - "graphql.schema.idl.NoopWiringFactory", - "graphql.schema.idl.RuntimeWiring", - "graphql.schema.idl.ScalarInfo", - "graphql.schema.idl.ScalarWiringEnvironment", - "graphql.schema.idl.SchemaDirectiveWiring", - "graphql.schema.idl.SchemaDirectiveWiringEnvironment", - "graphql.schema.idl.SchemaDirectiveWiringEnvironmentImpl", - "graphql.schema.idl.SchemaDirectiveWiringSchemaGeneratorPostProcessing", - "graphql.schema.idl.SchemaExtensionsChecker", - "graphql.schema.idl.SchemaGenerator", - "graphql.schema.idl.SchemaGeneratorAppliedDirectiveHelper", - "graphql.schema.idl.SchemaGeneratorDirectiveHelper", - "graphql.schema.idl.SchemaGeneratorHelper", - "graphql.schema.idl.SchemaParseOrder", - "graphql.schema.idl.SchemaParser", - "graphql.schema.idl.SchemaPrinter", - "graphql.schema.idl.SchemaTypeChecker", - "graphql.schema.idl.SchemaTypeDirectivesChecker", - "graphql.schema.idl.SchemaTypeExtensionsChecker", - "graphql.schema.idl.TypeDefinitionRegistry", - "graphql.schema.idl.TypeInfo", - "graphql.schema.idl.TypeRuntimeWiring", - "graphql.schema.idl.TypeUtil", - "graphql.schema.idl.UnExecutableSchemaGenerator", - "graphql.schema.idl.UnionTypesChecker", - "graphql.schema.idl.UnionWiringEnvironment", - "graphql.schema.idl.WiringEnvironment", - "graphql.schema.idl.WiringFactory", - "graphql.schema.idl.errors.BaseError", - "graphql.schema.idl.errors.DirectiveIllegalArgumentTypeError", - "graphql.schema.idl.errors.DirectiveIllegalLocationError", - "graphql.schema.idl.errors.DirectiveIllegalReferenceError", - "graphql.schema.idl.errors.DirectiveMissingNonNullArgumentError", - "graphql.schema.idl.errors.DirectiveRedefinitionError", - "graphql.schema.idl.errors.DirectiveUndeclaredError", - "graphql.schema.idl.errors.DirectiveUnknownArgumentError", - "graphql.schema.idl.errors.IllegalNameError", - "graphql.schema.idl.errors.InterfaceFieldArgumentNotOptionalError", - "graphql.schema.idl.errors.InterfaceFieldArgumentRedefinitionError", - "graphql.schema.idl.errors.InterfaceFieldRedefinitionError", - "graphql.schema.idl.errors.InterfaceImplementedMoreThanOnceError", - "graphql.schema.idl.errors.InterfaceImplementingItselfError", - "graphql.schema.idl.errors.InterfaceWithCircularImplementationHierarchyError", - "graphql.schema.idl.errors.MissingInterfaceFieldArgumentsError", - "graphql.schema.idl.errors.MissingInterfaceFieldError", - "graphql.schema.idl.errors.MissingInterfaceTypeError", - "graphql.schema.idl.errors.MissingScalarImplementationError", - "graphql.schema.idl.errors.MissingTransitiveInterfaceError", - "graphql.schema.idl.errors.MissingTypeError", - "graphql.schema.idl.errors.MissingTypeResolverError", - "graphql.schema.idl.errors.NonSDLDefinitionError", - "graphql.schema.idl.errors.NonUniqueArgumentError", - "graphql.schema.idl.errors.NonUniqueDirectiveError", - "graphql.schema.idl.errors.NonUniqueNameError", - "graphql.schema.idl.errors.NotAnInputTypeError", - "graphql.schema.idl.errors.NotAnOutputTypeError", - "graphql.schema.idl.errors.OperationRedefinitionError", - "graphql.schema.idl.errors.OperationTypesMustBeObjects", - "graphql.schema.idl.errors.QueryOperationMissingError", - "graphql.schema.idl.errors.SchemaMissingError", - "graphql.schema.idl.errors.SchemaProblem", - "graphql.schema.idl.errors.SchemaRedefinitionError", - "graphql.schema.idl.errors.StrictModeWiringException", - "graphql.schema.idl.errors.TypeExtensionDirectiveRedefinitionError", - "graphql.schema.idl.errors.TypeExtensionEnumValueRedefinitionError", - "graphql.schema.idl.errors.TypeExtensionFieldRedefinitionError", - "graphql.schema.idl.errors.TypeExtensionMissingBaseTypeError", - "graphql.schema.idl.errors.TypeRedefinitionError", - "graphql.schema.idl.errors.UnionTypeError", - "graphql.schema.impl.GraphQLTypeCollectingVisitor", - "graphql.schema.impl.MultiReadOnlyGraphQLTypeVisitor", - "graphql.schema.impl.SchemaUtil", - "graphql.schema.impl.StronglyConnectedComponentsTopologicallySorted", - "graphql.schema.transform.FieldVisibilitySchemaTransformation", - "graphql.schema.transform.VisibleFieldPredicate", - "graphql.schema.transform.VisibleFieldPredicateEnvironment", - "graphql.schema.usage.SchemaUsage", - "graphql.schema.usage.SchemaUsageSupport", - "graphql.schema.validation.AppliedDirectiveArgumentsAreValid", - "graphql.schema.validation.AppliedDirectivesAreValid", - "graphql.schema.validation.DefaultValuesAreValid", - "graphql.schema.validation.DeprecatedInputObjectAndArgumentsAreValid", - "graphql.schema.validation.InputAndOutputTypesUsedAppropriately", - "graphql.schema.validation.InvalidSchemaException", - "graphql.schema.validation.NoUnbrokenInputCycles", - "graphql.schema.validation.OneOfInputObjectRules", - "graphql.schema.validation.SchemaValidationError", - "graphql.schema.validation.SchemaValidationErrorClassification", - "graphql.schema.validation.SchemaValidationErrorCollector", - "graphql.schema.validation.SchemaValidationErrorType", - "graphql.schema.validation.SchemaValidator", - "graphql.schema.validation.TypeAndFieldRule", - "graphql.schema.validation.TypesImplementInterfaces", - "graphql.schema.visibility.BlockedFields", - "graphql.schema.visibility.DefaultGraphqlFieldVisibility", - "graphql.schema.visibility.GraphqlFieldVisibility", - "graphql.schema.visibility.NoIntrospectionGraphqlFieldVisibility", - "graphql.schema.visitor.GraphQLSchemaTraversalControl", - "graphql.schema.visitor.GraphQLSchemaVisitor", - "graphql.schema.visitor.GraphQLSchemaVisitorAdapter", - "graphql.schema.visitor.GraphQLSchemaVisitorEnvironment", - "graphql.schema.visitor.GraphQLSchemaVisitorEnvironmentImpl", - "graphql.util.Anonymizer", - "graphql.util.Breadcrumb", - "graphql.util.CyclicSchemaAnalyzer", - "graphql.util.DefaultTraverserContext", - "graphql.util.EscapeUtil", - "graphql.util.FpKit", - "graphql.util.IdGenerator", - "graphql.util.InterThreadMemoizedSupplier", - "graphql.util.Interning", - "graphql.util.IntraThreadMemoizedSupplier", - "graphql.util.LockKit", - "graphql.util.MutableRef", - "graphql.util.NodeAdapter", - "graphql.util.NodeLocation", - "graphql.util.NodeMultiZipper", - "graphql.util.NodeZipper", - "graphql.util.Pair", - "graphql.util.ReplaceNode", - "graphql.util.StringKit", - "graphql.util.TraversalControl", - "graphql.util.Traverser", - "graphql.util.TraverserContext", - "graphql.util.TraverserResult", - "graphql.util.TraverserState", - "graphql.util.TraverserVisitor", - "graphql.util.TraverserVisitorStub", - "graphql.util.TreeParallelTransformer", - "graphql.util.TreeParallelTraverser", - "graphql.util.TreeTransformer", - "graphql.util.TreeTransformerUtil", - "graphql.validation.AbstractRule", - "graphql.validation.ArgumentValidationUtil", - "graphql.validation.DocumentVisitor", - "graphql.validation.LanguageTraversal", - "graphql.validation.RulesVisitor", - "graphql.validation.TraversalContext", - "graphql.validation.ValidationContext", - "graphql.validation.ValidationError", - "graphql.validation.ValidationErrorClassification", - "graphql.validation.ValidationErrorCollector", - "graphql.validation.ValidationErrorType", - "graphql.validation.ValidationUtil", - "graphql.validation.Validator", - "graphql.validation.rules.ArgumentsOfCorrectType", - "graphql.validation.rules.DeferDirectiveLabel", - "graphql.validation.rules.DeferDirectiveOnRootLevel", - "graphql.validation.rules.DeferDirectiveOnValidOperation", - "graphql.validation.rules.ExecutableDefinitions", - "graphql.validation.rules.FieldsOnCorrectType", - "graphql.validation.rules.FragmentsOnCompositeType", - "graphql.validation.rules.KnownArgumentNames", - "graphql.validation.rules.KnownDirectives", - "graphql.validation.rules.KnownFragmentNames", - "graphql.validation.rules.KnownOperationTypes", - "graphql.validation.rules.KnownTypeNames", - "graphql.validation.rules.LoneAnonymousOperation", - "graphql.validation.rules.NoFragmentCycles", - "graphql.validation.rules.NoUndefinedVariables", - "graphql.validation.rules.NoUnusedFragments", - "graphql.validation.rules.NoUnusedVariables", - "graphql.validation.rules.OverlappingFieldsCanBeMerged", - "graphql.validation.rules.PossibleFragmentSpreads", - "graphql.validation.rules.ProvidedNonNullArguments", - "graphql.validation.rules.ScalarLeaves", - "graphql.validation.rules.SubscriptionRootField", - "graphql.validation.rules.UniqueArgumentNames", - "graphql.validation.rules.UniqueDirectiveNamesPerLocation", - "graphql.validation.rules.UniqueFragmentNames", - "graphql.validation.rules.UniqueObjectFieldName", - "graphql.validation.rules.UniqueOperationNames", - "graphql.validation.rules.UniqueVariableNames", - "graphql.validation.rules.VariableDefaultValuesOfCorrectType", - "graphql.validation.rules.VariableTypesMatch", - "graphql.validation.rules.VariablesAreInputTypes", - "graphql.validation.rules.VariablesTypesMatcher", - ] + private static final Set ALLOWLIST = [] as Set - def "should ensure all new classes have @NullMarked annotation"() { + def "should ensure all public API and experimental API classes have @NullMarked annotation"() { given: - // Get all classes in the graphql package - JavaClasses allClasses = new ClassFileImporter() + def classes = new ClassFileImporter() .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) .importPackages("graphql") - - // Filter out allowlisted classes - def newClasses = allClasses.stream() - .filter { javaClass -> !ALLOWLIST.contains(javaClass.name) } + .stream() + .filter { it.isAnnotatedWith("graphql.PublicApi") || it.isAnnotatedWith("graphql.ExperimentalApi") } .collect() when: - List classesMissingAnnotation = [] - - newClasses.each { JavaClass javaClass -> - // Check if class has @NullMarked annotation - if (!javaClass.isAnnotatedWith("org.jspecify.annotations.NullMarked")) { - classesMissingAnnotation.add(javaClass.name) - } - } + def classesMissingAnnotation = classes + .stream() + .filter { !it.isAnnotatedWith("org.jspecify.annotations.NullMarked") } + .map { it.name } + .filter { it -> !ALLOWLIST.contains(it) } + .collect() then: - classesMissingAnnotation.isEmpty() - - cleanup: "if the test fails, provide detailed information about which classes have violations" if (!classesMissingAnnotation.isEmpty()) { - def errorMessage = new StringBuilder("Classes missing @NullMarked annotation:\n") - - classesMissingAnnotation.each { className -> - errorMessage.append(" - ${className}\n") - } - - errorMessage.append("\nTo fix these issues:\n") - errorMessage.append("1. Add @NullMarked to classes\n") - errorMessage.append("2. Add @Nullable annotations for nullable elements within that class\n") - - println errorMessage.toString() + println """The following public API and experimental API classes are missing @NullMarked annotation: + ${classesMissingAnnotation.sort().join("\n")} + +Add @NullMarked to these public API classes and add @Nullable annotations where appropriate.""" + assert false, "Found ${classesMissingAnnotation.size()} public API and experimental API classes missing @NullMarked annotation" } } } \ No newline at end of file From 22d7c8ffd6a23acf3b94fe6c232b32b5b2c72469 Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Sat, 5 Apr 2025 17:28:37 +1100 Subject: [PATCH 03/15] Add existing public and experimental API classes to the allow list --- .../graphql/JSpecifyAnnotationsCheck.groovy | 362 +++++++++++++++++- 1 file changed, 359 insertions(+), 3 deletions(-) diff --git a/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy b/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy index 22fb8ed253..48c5fd3e5e 100644 --- a/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy +++ b/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy @@ -5,14 +5,370 @@ import com.tngtech.archunit.core.importer.ImportOption import spock.lang.Specification /** - * This test ensures that all public API and experimental API classes in the graphql package + * This test ensures that all public API and experimental API classes * are properly annotated with JSpecify annotations. */ class JSpecifyAnnotationsCheck extends Specification { - private static final Set ALLOWLIST = [] as Set + private static final Set ALLOWLIST = [ + "graphql.AssertException", + "graphql.Directives", + "graphql.ErrorClassification", + "graphql.ErrorType", + "graphql.ExceptionWhileDataFetching", + "graphql.ExecutionInput", + "graphql.ExecutionResult", + "graphql.GraphQL", + "graphql.GraphQL\$Builder", + "graphql.GraphQLContext", + "graphql.GraphQLError", + "graphql.GraphqlErrorBuilder", + "graphql.GraphqlErrorException", + "graphql.ParseAndValidate", + "graphql.ParseAndValidateResult", + "graphql.Scalars", + "graphql.SerializationError", + "graphql.TypeMismatchError", + "graphql.TypeResolutionEnvironment", + "graphql.UnresolvedTypeError", + "graphql.agent.result.ExecutionTrackingResult", + "graphql.analysis.FieldComplexityCalculator", + "graphql.analysis.FieldComplexityEnvironment", + "graphql.analysis.MaxQueryComplexityInstrumentation", + "graphql.analysis.MaxQueryDepthInstrumentation", + "graphql.analysis.QueryComplexityCalculator", + "graphql.analysis.QueryComplexityInfo", + "graphql.analysis.QueryComplexityInfo\$Builder", + "graphql.analysis.QueryDepthInfo", + "graphql.analysis.QueryDepthInfo\$Builder", + "graphql.analysis.QueryReducer", + "graphql.analysis.QueryTransformer", + "graphql.analysis.QueryTransformer\$Builder", + "graphql.analysis.QueryTraversalOptions", + "graphql.analysis.QueryTraverser", + "graphql.analysis.QueryTraverser\$Builder", + "graphql.analysis.QueryVisitor", + "graphql.analysis.QueryVisitorFieldArgumentEnvironment", + "graphql.analysis.QueryVisitorFieldArgumentInputValue", + "graphql.analysis.QueryVisitorFieldArgumentValueEnvironment", + "graphql.analysis.QueryVisitorFieldEnvironment", + "graphql.analysis.QueryVisitorFragmentDefinitionEnvironment", + "graphql.analysis.QueryVisitorFragmentSpreadEnvironment", + "graphql.analysis.QueryVisitorInlineFragmentEnvironment", + "graphql.analysis.QueryVisitorStub", + "graphql.analysis.values.ValueTraverser", + "graphql.execution.AbortExecutionException", + "graphql.execution.AsyncExecutionStrategy", + "graphql.execution.AsyncSerialExecutionStrategy", + "graphql.execution.CoercedVariables", + "graphql.execution.DataFetcherExceptionHandlerParameters", + "graphql.execution.DataFetcherExceptionHandlerResult", + "graphql.execution.DataFetcherResult", + "graphql.execution.DefaultValueUnboxer", + "graphql.execution.ExecutionContext", + "graphql.execution.ExecutionId", + "graphql.execution.ExecutionStepInfo", + "graphql.execution.ExecutionStrategyParameters", + "graphql.execution.FetchedValue", + "graphql.execution.FieldValueInfo", + "graphql.execution.InputMapDefinesTooManyFieldsException", + "graphql.execution.MergedField", + "graphql.execution.MergedSelectionSet", + "graphql.execution.MissingRootTypeException", + "graphql.execution.NonNullableValueCoercedAsNullException", + "graphql.execution.NormalizedVariables", + "graphql.execution.OneOfNullValueException", + "graphql.execution.OneOfTooManyKeysException", + "graphql.execution.RawVariables", + "graphql.execution.ResultNodesInfo", + "graphql.execution.ResultPath", + "graphql.execution.SimpleDataFetcherExceptionHandler", + "graphql.execution.SubscriptionExecutionStrategy", + "graphql.execution.UnknownOperationException", + "graphql.execution.UnresolvedTypeException", + "graphql.execution.conditional.ConditionalNodeDecision", + "graphql.execution.directives.QueryAppliedDirective", + "graphql.execution.directives.QueryAppliedDirectiveArgument", + "graphql.execution.directives.QueryDirectives", + "graphql.execution.incremental.DeferredExecution", + "graphql.execution.instrumentation.ChainedInstrumentation", + "graphql.execution.instrumentation.DocumentAndVariables", + "graphql.execution.instrumentation.NoContextChainedInstrumentation", + "graphql.execution.instrumentation.SimpleInstrumentation", + "graphql.execution.instrumentation.SimpleInstrumentationContext", + "graphql.execution.instrumentation.SimplePerformantInstrumentation", + "graphql.execution.instrumentation.fieldvalidation.FieldAndArguments", + "graphql.execution.instrumentation.fieldvalidation.FieldValidationEnvironment", + "graphql.execution.instrumentation.fieldvalidation.FieldValidationInstrumentation", + "graphql.execution.instrumentation.fieldvalidation.SimpleFieldValidation", + "graphql.execution.instrumentation.parameters.InstrumentationCreateStateParameters", + "graphql.execution.instrumentation.parameters.InstrumentationExecuteOperationParameters", + "graphql.execution.instrumentation.parameters.InstrumentationExecutionParameters", + "graphql.execution.instrumentation.parameters.InstrumentationExecutionStrategyParameters", + "graphql.execution.instrumentation.parameters.InstrumentationFieldCompleteParameters", + "graphql.execution.instrumentation.parameters.InstrumentationFieldFetchParameters", + "graphql.execution.instrumentation.parameters.InstrumentationFieldParameters", + "graphql.execution.instrumentation.parameters.InstrumentationValidationParameters", + "graphql.execution.instrumentation.tracing.TracingInstrumentation", + "graphql.execution.instrumentation.tracing.TracingSupport", + "graphql.execution.preparsed.PreparsedDocumentEntry", + "graphql.execution.preparsed.persisted.ApolloPersistedQuerySupport", + "graphql.execution.preparsed.persisted.InMemoryPersistedQueryCache", + "graphql.execution.preparsed.persisted.PersistedQueryCacheMiss", + "graphql.execution.preparsed.persisted.PersistedQueryIdInvalid", + "graphql.execution.preparsed.persisted.PersistedQueryNotFound", + "graphql.execution.reactive.DelegatingSubscription", + "graphql.execution.reactive.SubscriptionPublisher", + "graphql.extensions.ExtensionsBuilder", + "graphql.incremental.DeferPayload", + "graphql.incremental.DelayedIncrementalPartialResult", + "graphql.incremental.DelayedIncrementalPartialResultImpl", + "graphql.incremental.IncrementalExecutionResult", + "graphql.incremental.IncrementalExecutionResultImpl", + "graphql.incremental.IncrementalPayload", + "graphql.incremental.StreamPayload", + "graphql.introspection.GoodFaithIntrospection", + "graphql.introspection.Introspection", + "graphql.introspection.IntrospectionQuery", + "graphql.introspection.IntrospectionQueryBuilder", + "graphql.introspection.IntrospectionResultToSchema", + "graphql.introspection.IntrospectionWithDirectivesSupport", + "graphql.introspection.IntrospectionWithDirectivesSupport\$DirectivePredicateEnvironment", + "graphql.language.AbstractDescribedNode", + "graphql.language.AbstractNode", + "graphql.language.Argument", + "graphql.language.ArrayValue", + "graphql.language.AstNodeAdapter", + "graphql.language.AstPrinter", + "graphql.language.AstSignature", + "graphql.language.AstSorter", + "graphql.language.AstTransformer", + "graphql.language.BooleanValue", + "graphql.language.Comment", + "graphql.language.Definition", + "graphql.language.DescribedNode", + "graphql.language.Description", + "graphql.language.Directive", + "graphql.language.DirectiveDefinition", + "graphql.language.DirectiveLocation", + "graphql.language.DirectivesContainer", + "graphql.language.Document", + "graphql.language.EnumTypeDefinition", + "graphql.language.EnumTypeExtensionDefinition", + "graphql.language.EnumValue", + "graphql.language.EnumValueDefinition", + "graphql.language.Field", + "graphql.language.FieldDefinition", + "graphql.language.FloatValue", + "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.IntValue", + "graphql.language.InterfaceTypeDefinition", + "graphql.language.InterfaceTypeExtensionDefinition", + "graphql.language.ListType", + "graphql.language.NamedNode", + "graphql.language.Node", + "graphql.language.NodeBuilder", + "graphql.language.NodeChildrenContainer", + "graphql.language.NodeDirectivesBuilder", + "graphql.language.NodeParentTree", + "graphql.language.NodeTraverser", + "graphql.language.NodeVisitor", + "graphql.language.NodeVisitorStub", + "graphql.language.NonNullType", + "graphql.language.NullValue", + "graphql.language.ObjectField", + "graphql.language.ObjectTypeDefinition", + "graphql.language.ObjectTypeExtensionDefinition", + "graphql.language.ObjectValue", + "graphql.language.OperationDefinition", + "graphql.language.OperationTypeDefinition", + "graphql.language.PrettyAstPrinter", + "graphql.language.SDLDefinition", + "graphql.language.SDLExtensionDefinition", + "graphql.language.SDLNamedDefinition", + "graphql.language.ScalarTypeDefinition", + "graphql.language.ScalarTypeExtensionDefinition", + "graphql.language.ScalarValue", + "graphql.language.SchemaDefinition", + "graphql.language.SchemaExtensionDefinition", + "graphql.language.Selection", + "graphql.language.SelectionSet", + "graphql.language.SelectionSetContainer", + "graphql.language.SourceLocation", + "graphql.language.StringValue", + "graphql.language.Type", + "graphql.language.TypeDefinition", + "graphql.language.TypeKind", + "graphql.language.TypeName", + "graphql.language.UnionTypeDefinition", + "graphql.language.UnionTypeExtensionDefinition", + "graphql.language.Value", + "graphql.language.VariableDefinition", + "graphql.language.VariableReference", + "graphql.normalized.ExecutableNormalizedField", + "graphql.normalized.ExecutableNormalizedOperation", + "graphql.normalized.ExecutableNormalizedOperationFactory", + "graphql.normalized.ExecutableNormalizedOperationToAstCompiler", + "graphql.normalized.NormalizedInputValue", + "graphql.normalized.incremental.NormalizedDeferredExecution", + "graphql.normalized.nf.NormalizedDocument", + "graphql.normalized.nf.NormalizedDocumentFactory", + "graphql.normalized.nf.NormalizedField", + "graphql.normalized.nf.NormalizedOperation", + "graphql.normalized.nf.NormalizedOperationToAstCompiler", + "graphql.parser.InvalidSyntaxException", + "graphql.parser.MultiSourceReader", + "graphql.parser.Parser", + "graphql.parser.ParserEnvironment", + "graphql.parser.ParserOptions", + "graphql.relay.Connection", + "graphql.relay.ConnectionCursor", + "graphql.relay.DefaultConnection", + "graphql.relay.DefaultConnectionCursor", + "graphql.relay.DefaultEdge", + "graphql.relay.DefaultPageInfo", + "graphql.relay.Edge", + "graphql.relay.PageInfo", + "graphql.relay.Relay", + "graphql.relay.SimpleListConnection", + "graphql.schema.AsyncDataFetcher", + "graphql.schema.CoercingParseLiteralException", + "graphql.schema.CoercingParseValueException", + "graphql.schema.CoercingSerializeException", + "graphql.schema.DataFetcherFactories", + "graphql.schema.DataFetcherFactoryEnvironment", + "graphql.schema.DataFetchingEnvironment", + "graphql.schema.DataFetchingFieldSelectionSet", + "graphql.schema.DefaultGraphqlTypeComparatorRegistry", + "graphql.schema.DelegatingDataFetchingEnvironment", + "graphql.schema.FieldCoordinates", + "graphql.schema.GraphQLAppliedDirective", + "graphql.schema.GraphQLAppliedDirectiveArgument", + "graphql.schema.GraphQLArgument", + "graphql.schema.GraphQLCodeRegistry", + "graphql.schema.GraphQLCompositeType", + "graphql.schema.GraphQLDirective", + "graphql.schema.GraphQLDirectiveContainer", + "graphql.schema.GraphQLEnumType", + "graphql.schema.GraphQLEnumValueDefinition", + "graphql.schema.GraphQLEnumValueDefinition\$Builder", + "graphql.schema.GraphQLFieldDefinition", + "graphql.schema.GraphQLFieldDefinition\$Builder", + "graphql.schema.GraphQLFieldsContainer", + "graphql.schema.GraphQLImplementingType", + "graphql.schema.GraphQLInputFieldsContainer", + "graphql.schema.GraphQLInputObjectField", + "graphql.schema.GraphQLInputObjectField\$Builder", + "graphql.schema.GraphQLInputObjectType", + "graphql.schema.GraphQLInputObjectType\$Builder", + "graphql.schema.GraphQLInputSchemaElement", + "graphql.schema.GraphQLInputType", + "graphql.schema.GraphQLInputValueDefinition", + "graphql.schema.GraphQLInterfaceType", + "graphql.schema.GraphQLInterfaceType\$Builder", + "graphql.schema.GraphQLList", + "graphql.schema.GraphQLModifiedType", + "graphql.schema.GraphQLNamedInputType", + "graphql.schema.GraphQLNamedOutputType", + "graphql.schema.GraphQLNamedSchemaElement", + "graphql.schema.GraphQLNamedType", + "graphql.schema.GraphQLNonNull", + "graphql.schema.GraphQLNullableType", + "graphql.schema.GraphQLObjectType", + "graphql.schema.GraphQLObjectType\$Builder", + "graphql.schema.GraphQLOutputType", + "graphql.schema.GraphQLScalarType", + "graphql.schema.GraphQLScalarType\$Builder", + "graphql.schema.GraphQLSchema", + "graphql.schema.GraphQLSchemaElement", + "graphql.schema.GraphQLType", + "graphql.schema.GraphQLTypeReference", + "graphql.schema.GraphQLTypeUtil", + "graphql.schema.GraphQLTypeVisitor", + "graphql.schema.GraphQLTypeVisitorStub", + "graphql.schema.GraphQLUnionType", + "graphql.schema.GraphQLUnionType\$Builder", + "graphql.schema.GraphQLUnmodifiedType", + "graphql.schema.GraphqlElementParentTree", + "graphql.schema.GraphqlTypeComparatorEnvironment", + "graphql.schema.GraphqlTypeComparatorRegistry", + "graphql.schema.InputValueWithState", + "graphql.schema.PropertyDataFetcher", + "graphql.schema.SchemaElementChildrenContainer", + "graphql.schema.SchemaTransformer", + "graphql.schema.SchemaTraverser", + "graphql.schema.SelectedField", + "graphql.schema.StaticDataFetcher", + "graphql.schema.diff.DiffCategory", + "graphql.schema.diff.DiffEvent", + "graphql.schema.diff.DiffLevel", + "graphql.schema.diff.DiffSet", + "graphql.schema.diff.SchemaDiffSet", + "graphql.schema.diff.reporting.CapturingReporter", + "graphql.schema.diff.reporting.ChainedReporter", + "graphql.schema.diff.reporting.PrintStreamReporter", + "graphql.schema.diffing.SchemaGraph", + "graphql.schema.idl.CombinedWiringFactory", + "graphql.schema.idl.DirectiveInfo", + "graphql.schema.idl.FieldWiringEnvironment", + "graphql.schema.idl.InterfaceWiringEnvironment", + "graphql.schema.idl.MapEnumValuesProvider", + "graphql.schema.idl.MockedWiringFactory", + "graphql.schema.idl.NaturalEnumValuesProvider", + "graphql.schema.idl.RuntimeWiring", + "graphql.schema.idl.RuntimeWiring\$Builder", + "graphql.schema.idl.ScalarInfo", + "graphql.schema.idl.ScalarWiringEnvironment", + "graphql.schema.idl.SchemaDirectiveWiring", + "graphql.schema.idl.SchemaDirectiveWiringEnvironment", + "graphql.schema.idl.SchemaGenerator", + "graphql.schema.idl.SchemaParser", + "graphql.schema.idl.SchemaPrinter", + "graphql.schema.idl.TypeDefinitionRegistry", + "graphql.schema.idl.TypeRuntimeWiring", + "graphql.schema.idl.UnionWiringEnvironment", + "graphql.schema.idl.WiringEnvironment", + "graphql.schema.idl.errors.SchemaProblem", + "graphql.schema.idl.errors.StrictModeWiringException", + "graphql.schema.transform.FieldVisibilitySchemaTransformation", + "graphql.schema.transform.VisibleFieldPredicateEnvironment", + "graphql.schema.usage.SchemaUsage", + "graphql.schema.usage.SchemaUsageSupport", + "graphql.schema.validation.OneOfInputObjectRules", + "graphql.schema.validation.SchemaValidationErrorClassification", + "graphql.schema.visibility.BlockedFields", + "graphql.schema.visibility.DefaultGraphqlFieldVisibility", + "graphql.schema.visibility.GraphqlFieldVisibility", + "graphql.schema.visibility.NoIntrospectionGraphqlFieldVisibility", + "graphql.schema.visitor.GraphQLSchemaTraversalControl", + "graphql.util.Anonymizer", + "graphql.util.Breadcrumb", + "graphql.util.CyclicSchemaAnalyzer", + "graphql.util.NodeAdapter", + "graphql.util.NodeLocation", + "graphql.util.NodeMultiZipper", + "graphql.util.NodeZipper", + "graphql.util.TraversalControl", + "graphql.util.TraverserContext", + "graphql.util.TreeTransformer", + "graphql.util.TreeTransformerUtil", + "graphql.validation.ValidationError", + "graphql.validation.ValidationErrorClassification", + "graphql.validation.ValidationErrorType", + "graphql.validation.rules.DeferDirectiveLabel", + "graphql.validation.rules.DeferDirectiveOnRootLevel", + "graphql.validation.rules.DeferDirectiveOnValidOperation" + ] as Set - def "should ensure all public API and experimental API classes have @NullMarked annotation"() { + def "ensure all public API and experimental API classes have @NullMarked annotation"() { given: def classes = new ClassFileImporter() .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) From 1aab71d4020bed4791abb8f4210fc1d662bcdc29 Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Sat, 5 Apr 2025 17:33:36 +1100 Subject: [PATCH 04/15] Add documentation link --- src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy b/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy index 48c5fd3e5e..6bb57c4f6f 100644 --- a/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy +++ b/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy @@ -390,7 +390,7 @@ class JSpecifyAnnotationsCheck extends Specification { println """The following public API and experimental API classes are missing @NullMarked annotation: ${classesMissingAnnotation.sort().join("\n")} -Add @NullMarked to these public API classes and add @Nullable annotations where appropriate.""" +Add @NullMarked to these public API classes and add @Nullable annotations where appropriate. See documentation at https://jspecify.dev/docs/user-guide/#nullmarked""" assert false, "Found ${classesMissingAnnotation.size()} public API and experimental API classes missing @NullMarked annotation" } } From 0efd768338e865a2c363308401bcfe29ce3454eb Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Sat, 5 Apr 2025 17:41:46 +1100 Subject: [PATCH 05/15] Deliberately fail test as demonstration --- src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy b/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy index 6bb57c4f6f..94f0feacc9 100644 --- a/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy +++ b/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy @@ -11,7 +11,7 @@ import spock.lang.Specification class JSpecifyAnnotationsCheck extends Specification { private static final Set ALLOWLIST = [ - "graphql.AssertException", +// "graphql.AssertException", "graphql.Directives", "graphql.ErrorClassification", "graphql.ErrorType", From 3c3e1a7266da47ec6ef535b0b3d112ece295f8b4 Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Sat, 5 Apr 2025 17:47:39 +1100 Subject: [PATCH 06/15] Add back exemption --- src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy b/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy index 94f0feacc9..670b2e1b42 100644 --- a/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy +++ b/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy @@ -10,8 +10,8 @@ import spock.lang.Specification */ class JSpecifyAnnotationsCheck extends Specification { - private static final Set ALLOWLIST = [ -// "graphql.AssertException", + private static final Set JSPECIFY_EXEMPTION_LIST = [ + "graphql.AssertException", "graphql.Directives", "graphql.ErrorClassification", "graphql.ErrorType", @@ -382,7 +382,7 @@ class JSpecifyAnnotationsCheck extends Specification { .stream() .filter { !it.isAnnotatedWith("org.jspecify.annotations.NullMarked") } .map { it.name } - .filter { it -> !ALLOWLIST.contains(it) } + .filter { it -> !JSPECIFY_EXEMPTION_LIST.contains(it) } .collect() then: From ca194666ce7f4ab8f9f92e07da4c170564984129 Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Sun, 20 Jul 2025 19:41:46 +1000 Subject: [PATCH 07/15] Remove class that has been annotated --- src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy b/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy index 670b2e1b42..24a0d71e53 100644 --- a/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy +++ b/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy @@ -16,7 +16,6 @@ class JSpecifyAnnotationsCheck extends Specification { "graphql.ErrorClassification", "graphql.ErrorType", "graphql.ExceptionWhileDataFetching", - "graphql.ExecutionInput", "graphql.ExecutionResult", "graphql.GraphQL", "graphql.GraphQL\$Builder", From fec10045d1299aa7aa54bd5ad83ec93955afaa14 Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Sun, 20 Jul 2025 19:53:32 +1000 Subject: [PATCH 08/15] Add new exemptions --- src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy b/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy index 24a0d71e53..fb0bf1ece4 100644 --- a/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy +++ b/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy @@ -93,6 +93,7 @@ class JSpecifyAnnotationsCheck extends Specification { "graphql.execution.instrumentation.ChainedInstrumentation", "graphql.execution.instrumentation.DocumentAndVariables", "graphql.execution.instrumentation.NoContextChainedInstrumentation", + "graphql.execution.ResponseMapFactory", "graphql.execution.instrumentation.SimpleInstrumentation", "graphql.execution.instrumentation.SimpleInstrumentationContext", "graphql.execution.instrumentation.SimplePerformantInstrumentation", @@ -355,6 +356,10 @@ class JSpecifyAnnotationsCheck extends Specification { "graphql.util.NodeLocation", "graphql.util.NodeMultiZipper", "graphql.util.NodeZipper", + "graphql.util.querygenerator.QueryGenerator", + "graphql.util.querygenerator.QueryGeneratorOptions", + "graphql.util.querygenerator.QueryGeneratorOptions\$QueryGeneratorOptionsBuilder", + "graphql.util.querygenerator.QueryGeneratorResult", "graphql.util.TraversalControl", "graphql.util.TraverserContext", "graphql.util.TreeTransformer", From a281c63f5e427b1d9afe52100c3869a71846be8e Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Sun, 20 Jul 2025 20:03:45 +1000 Subject: [PATCH 09/15] Test improved failure message --- .../groovy/graphql/JSpecifyAnnotationsCheck.groovy | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy b/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy index fb0bf1ece4..4c5b4ca093 100644 --- a/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy +++ b/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy @@ -356,7 +356,7 @@ class JSpecifyAnnotationsCheck extends Specification { "graphql.util.NodeLocation", "graphql.util.NodeMultiZipper", "graphql.util.NodeZipper", - "graphql.util.querygenerator.QueryGenerator", +// "graphql.util.querygenerator.QueryGenerator", "graphql.util.querygenerator.QueryGeneratorOptions", "graphql.util.querygenerator.QueryGeneratorOptions\$QueryGeneratorOptionsBuilder", "graphql.util.querygenerator.QueryGeneratorResult", @@ -391,11 +391,10 @@ class JSpecifyAnnotationsCheck extends Specification { then: if (!classesMissingAnnotation.isEmpty()) { - println """The following public API and experimental API classes are missing @NullMarked annotation: - ${classesMissingAnnotation.sort().join("\n")} - -Add @NullMarked to these public API classes and add @Nullable annotations where appropriate. See documentation at https://jspecify.dev/docs/user-guide/#nullmarked""" - assert false, "Found ${classesMissingAnnotation.size()} public API and experimental API classes missing @NullMarked annotation" + throw new AssertionError("""The following public API and experimental API classes are missing @NullMarked annotation: +${classesMissingAnnotation.sort().join("\n")} + +Add @NullMarked to these public API classes and add @Nullable annotations where appropriate. See documentation at https://jspecify.dev/docs/user-guide/#nullmarked""") } } } \ No newline at end of file From fd41bfa0eb11987be13eaa8ce3023ce737941a2e Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Sun, 20 Jul 2025 20:18:36 +1000 Subject: [PATCH 10/15] Add back query generator exemption --- src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy b/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy index 4c5b4ca093..85543417b9 100644 --- a/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy +++ b/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy @@ -356,7 +356,7 @@ class JSpecifyAnnotationsCheck extends Specification { "graphql.util.NodeLocation", "graphql.util.NodeMultiZipper", "graphql.util.NodeZipper", -// "graphql.util.querygenerator.QueryGenerator", + "graphql.util.querygenerator.QueryGenerator", "graphql.util.querygenerator.QueryGeneratorOptions", "graphql.util.querygenerator.QueryGeneratorOptions\$QueryGeneratorOptionsBuilder", "graphql.util.querygenerator.QueryGeneratorResult", From 021766ecdb46bc5e767de36c5f7bc60c963afc68 Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Sun, 3 Aug 2025 09:12:48 +1000 Subject: [PATCH 11/15] Move ArchUnit test --- .../groovy/graphql/{ => archunit}/JSpecifyAnnotationsCheck.groovy | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/test/groovy/graphql/{ => archunit}/JSpecifyAnnotationsCheck.groovy (100%) diff --git a/src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy b/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy similarity index 100% rename from src/test/groovy/graphql/JSpecifyAnnotationsCheck.groovy rename to src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy From 8b46e31448f7c2c88a6aaaffb309435b9a1bef68 Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Sun, 3 Aug 2025 09:13:14 +1000 Subject: [PATCH 12/15] Add a test to ensure we keep the exemption list up to date --- .../archunit/JSpecifyAnnotationsCheck.groovy | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy b/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy index 85543417b9..44516ebdfd 100644 --- a/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy +++ b/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy @@ -397,4 +397,26 @@ ${classesMissingAnnotation.sort().join("\n")} Add @NullMarked to these public API classes and add @Nullable annotations where appropriate. See documentation at https://jspecify.dev/docs/user-guide/#nullmarked""") } } + + def "exempted classes should not be annotated with @NullMarked"() { + given: + def classes = new ClassFileImporter() + .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) + .importPackages("graphql") + + when: + def annotatedButExempted = classes.stream() + .filter { JSPECIFY_EXEMPTION_LIST.contains(it.name) } + .filter { it.isAnnotatedWith("org.jspecify.annotations.NullMarked") } + .map { it.name } + .collect() + + then: + if (!annotatedButExempted.isEmpty()) { + throw new AssertionError("""The following classes are in the JSpecify exemption list but are annotated with @NullMarked: +${annotatedButExempted.sort().join("\n")} + +Please remove them from the exemption list in ${JSpecifyAnnotationsCheck.class.simpleName}.groovy.""") + } + } } \ No newline at end of file From 3e29b8a9453694ab4dedb68a38355675ec2115bb Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Sun, 3 Aug 2025 09:14:10 +1000 Subject: [PATCH 13/15] Remove exemptions that now have nullmarked annotations --- .../graphql/archunit/JSpecifyAnnotationsCheck.groovy | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy b/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy index 44516ebdfd..0cb8f160d8 100644 --- a/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy +++ b/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy @@ -62,7 +62,6 @@ class JSpecifyAnnotationsCheck extends Specification { "graphql.execution.CoercedVariables", "graphql.execution.DataFetcherExceptionHandlerParameters", "graphql.execution.DataFetcherExceptionHandlerResult", - "graphql.execution.DataFetcherResult", "graphql.execution.DefaultValueUnboxer", "graphql.execution.ExecutionContext", "graphql.execution.ExecutionId", @@ -78,7 +77,6 @@ class JSpecifyAnnotationsCheck extends Specification { "graphql.execution.NormalizedVariables", "graphql.execution.OneOfNullValueException", "graphql.execution.OneOfTooManyKeysException", - "graphql.execution.RawVariables", "graphql.execution.ResultNodesInfo", "graphql.execution.ResultPath", "graphql.execution.SimpleDataFetcherExceptionHandler", @@ -245,7 +243,6 @@ class JSpecifyAnnotationsCheck extends Specification { "graphql.schema.CoercingSerializeException", "graphql.schema.DataFetcherFactories", "graphql.schema.DataFetcherFactoryEnvironment", - "graphql.schema.DataFetchingEnvironment", "graphql.schema.DataFetchingFieldSelectionSet", "graphql.schema.DefaultGraphqlTypeComparatorRegistry", "graphql.schema.DelegatingDataFetchingEnvironment", @@ -318,24 +315,15 @@ class JSpecifyAnnotationsCheck extends Specification { "graphql.schema.diffing.SchemaGraph", "graphql.schema.idl.CombinedWiringFactory", "graphql.schema.idl.DirectiveInfo", - "graphql.schema.idl.FieldWiringEnvironment", - "graphql.schema.idl.InterfaceWiringEnvironment", "graphql.schema.idl.MapEnumValuesProvider", - "graphql.schema.idl.MockedWiringFactory", "graphql.schema.idl.NaturalEnumValuesProvider", "graphql.schema.idl.RuntimeWiring", "graphql.schema.idl.RuntimeWiring\$Builder", - "graphql.schema.idl.ScalarInfo", - "graphql.schema.idl.ScalarWiringEnvironment", "graphql.schema.idl.SchemaDirectiveWiring", "graphql.schema.idl.SchemaDirectiveWiringEnvironment", "graphql.schema.idl.SchemaGenerator", - "graphql.schema.idl.SchemaParser", "graphql.schema.idl.SchemaPrinter", - "graphql.schema.idl.TypeDefinitionRegistry", "graphql.schema.idl.TypeRuntimeWiring", - "graphql.schema.idl.UnionWiringEnvironment", - "graphql.schema.idl.WiringEnvironment", "graphql.schema.idl.errors.SchemaProblem", "graphql.schema.idl.errors.StrictModeWiringException", "graphql.schema.transform.FieldVisibilitySchemaTransformation", From 70ed6007d123ac6f0f6d2f8cf9687c847f330196 Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Sun, 3 Aug 2025 09:20:01 +1000 Subject: [PATCH 14/15] Remove more classes already annotated --- .../groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy b/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy index 0cb8f160d8..203038dc1d 100644 --- a/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy +++ b/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy @@ -17,7 +17,6 @@ class JSpecifyAnnotationsCheck extends Specification { "graphql.ErrorType", "graphql.ExceptionWhileDataFetching", "graphql.ExecutionResult", - "graphql.GraphQL", "graphql.GraphQL\$Builder", "graphql.GraphQLContext", "graphql.GraphQLError", @@ -70,7 +69,6 @@ class JSpecifyAnnotationsCheck extends Specification { "graphql.execution.FetchedValue", "graphql.execution.FieldValueInfo", "graphql.execution.InputMapDefinesTooManyFieldsException", - "graphql.execution.MergedField", "graphql.execution.MergedSelectionSet", "graphql.execution.MissingRootTypeException", "graphql.execution.NonNullableValueCoercedAsNullException", From c36c533135e98c99933cf8ec33e769ba8e735b4d Mon Sep 17 00:00:00 2001 From: dondonz <13839920+dondonz@users.noreply.github.com> Date: Sun, 3 Aug 2025 10:52:31 +1000 Subject: [PATCH 15/15] Also allow classes (usually Builders) to have NullUnmarked --- .../archunit/JSpecifyAnnotationsCheck.groovy | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy b/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy index 203038dc1d..ee8e4aec58 100644 --- a/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy +++ b/src/test/groovy/graphql/archunit/JSpecifyAnnotationsCheck.groovy @@ -17,7 +17,6 @@ class JSpecifyAnnotationsCheck extends Specification { "graphql.ErrorType", "graphql.ExceptionWhileDataFetching", "graphql.ExecutionResult", - "graphql.GraphQL\$Builder", "graphql.GraphQLContext", "graphql.GraphQLError", "graphql.GraphqlErrorBuilder", @@ -370,21 +369,21 @@ class JSpecifyAnnotationsCheck extends Specification { when: def classesMissingAnnotation = classes .stream() - .filter { !it.isAnnotatedWith("org.jspecify.annotations.NullMarked") } + .filter { !it.isAnnotatedWith("org.jspecify.annotations.NullMarked") && !it.isAnnotatedWith("org.jspecify.annotations.NullUnmarked") } .map { it.name } .filter { it -> !JSPECIFY_EXEMPTION_LIST.contains(it) } .collect() then: if (!classesMissingAnnotation.isEmpty()) { - throw new AssertionError("""The following public API and experimental API classes are missing @NullMarked annotation: + throw new AssertionError("""The following public API and experimental API classes are missing a JSpecify annotation: ${classesMissingAnnotation.sort().join("\n")} -Add @NullMarked to these public API classes and add @Nullable annotations where appropriate. See documentation at https://jspecify.dev/docs/user-guide/#nullmarked""") +Add @NullMarked or @NullUnmarked to these public API classes. See documentation at https://jspecify.dev/docs/user-guide/#nullmarked""") } } - def "exempted classes should not be annotated with @NullMarked"() { + def "exempted classes should not be annotated with @NullMarked or @NullUnmarked"() { given: def classes = new ClassFileImporter() .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS) @@ -393,13 +392,13 @@ Add @NullMarked to these public API classes and add @Nullable annotations where when: def annotatedButExempted = classes.stream() .filter { JSPECIFY_EXEMPTION_LIST.contains(it.name) } - .filter { it.isAnnotatedWith("org.jspecify.annotations.NullMarked") } + .filter { it.isAnnotatedWith("org.jspecify.annotations.NullMarked") || it.isAnnotatedWith("org.jspecify.annotations.NullUnmarked") } .map { it.name } .collect() then: if (!annotatedButExempted.isEmpty()) { - throw new AssertionError("""The following classes are in the JSpecify exemption list but are annotated with @NullMarked: + throw new AssertionError("""The following classes are in the JSpecify exemption list but are annotated with @NullMarked or @NullUnmarked: ${annotatedButExempted.sort().join("\n")} Please remove them from the exemption list in ${JSpecifyAnnotationsCheck.class.simpleName}.groovy.""")