Repository navigation
Split SchemaClassScanner and SchemaParser into smaller classes - #906
Conversation
The scanner's type dictionary moves into its own class in the next commit, and the scanner keeps reading it. Two reads need a shape that doesn't reach into the mutable map: - The enum-without-class check at the start of validateAndCreateResult becomes throwIfEnumWithoutClass(), so it can move with enumsWithoutClass. - DictionaryEntry implements a new EntryView with javaType and typeClass(), the only parts the rest of the scanner reads. setTypeIfMissing and addReference aren't on it. No behaviour change.
Moves the dictionary map, enumsWithoutClass, interfaceDefinitionsByName, both handleFoundType functions, handleNewType, scanInterfacesOfType, throwIfEnumWithoutClass, the input object field walk (handleInputObjectFields, handleInputValueWithoutJavaType, isPropertyMap, isLibraryClass, enumWithoutClassMessage, findInputValueTypeInType), EntryView, DictionaryEntry and InitialDictionaryEntry to ScanDictionary.kt. ScanDictionary is the only writer of the map. The scanner reads it as a Map<TypeDefinition<*>, EntryView>, so the only way to add a type is handleFoundType, which enters the type before it follows the type's class to more types. Edits to compile: - ScanDictionary's header. It delegates Map to the map it owns and takes the scanner's logger, so its warnings keep the SchemaClassScanner logger name. - The scanner passes its enqueue and handleFoundScalarType in as functions, so the queue and unvalidatedTypes keep one owner and their order. - private dropped on both handleFoundType functions, scanInterfacesOfType, throwIfEnumWithoutClass, DictionaryEntry and InitialDictionaryEntry, and for this commit only on InterfaceReference and InputObjectReference. - The scanner calls these through dictionary. - Imports.
Moves Reference and its subclasses next to DictionaryEntry, which stores them and prints them in the "Two different classes" error. TypeClassMatcher and the field resolvers no longer name SchemaClassScanner. Edits to compile: - InterfaceReference and InputObjectReference are private again. - private dropped on DictionaryReference, UnusedTypeReference, UnionMemberReference and SealedSubclassReference, which the scanner creates. - SchemaClassScanner.XReference becomes ScanDictionary.XReference in TypeClassMatcher, MethodFieldResolver, PropertyFieldResolver, MapFieldResolver and NestedGenericWrapperTest. - Imports.
Moves sealedSubclassHint, sealedSubclassMatches, sealedTypesOf, sealedSubclasses and withSealedTypeArguments to SealedSubclassMatcher.kt. It reads the dictionary as a Map<TypeDefinition<*>, EntryView> and never changes it. Edits to compile: - SealedSubclassMatcher's header. - private dropped on sealedSubclassHint and sealedSubclassMatches. - The scanner holds a sealedSubclassMatcher and calls both through it. - Imports.
SchemaParser uses it, and so do the error messages and the directive code that move out of SchemaParser next. Edits to compile: private becomes internal, and SchemaParser imports it.
Moves unexpectedTypeError, unreachedTypeHint, findUsage and fieldUsages to TypeReferenceErrors.kt. determineType stays in SchemaParser, because for input types it builds missing input objects through createInputObject. Edits to compile: - TypeReferenceErrors' header, with definitions, unusedDefinitions and typeExtensions. - private dropped on unexpectedTypeError. - SchemaParser holds a typeReferenceErrors, and determineType throws through it. - Imports.
Moves directivesOf, isNonRepeatableByFirstDeclaration, createDirectives,
createDirective, createDirectiveArgument, withSdlDirectives, the functions that
resolve and render applied directives, ResolvedDirective, ResolvedArgument,
missingArgumentsWithDefault, getDeprecated, the schemaDirectives map and
BUILT_IN_DIRECTIVES to SdlDirectives.kt.
SdlDirectives builds the schema's directive definitions in its constructor, so
nothing can apply a directive before they exist. SchemaParser builds one in
parseSchemaObjects() where it used to call createDirectives(), so directive
errors are still thrown from there, and every schema it builds gets its own
definitions.
Edits to compile:
- SdlDirectives' header with directiveDefinitions, options, runtimeWiring,
SchemaParser's logger (its warnings keep that logger name) and inputType,
the type lookup createDirectiveArgument called. The lookup's comment moves
with it to SchemaParser.
- init { createDirectives() }, and a definitions property for SchemaObjects.
- schemaAppliedDirectives, the line SchemaParser used for the schema's own
directives, so the resolving functions stay private.
- private dropped on directivesOf, withSdlDirectives and getDeprecated.
- SchemaParser: lateinit var sdlDirectives replaces the map, directivesOf and
getDeprecated are called through it, and a private withSdlDirectives calls
the one in SdlDirectives, so the builder chains don't change.
- Imports.
|
|
Verifier verdict for 3488a31: PASS |



Checklist
Description
Last part of the cleanup of code paths that drifted apart, no issue. Nothing should change for users.
SchemaClassScanner.ktwas 870 lines andSchemaParser.kt739. Every commit after the first moves one group of functions out verbatim, sogit show --color-moved=dimmed-zebrashows it as moved. Each commit message lists the edits it needed to compile.ScanDictionarygets the scanner's type dictionary together with everything that writes it:handleFoundType,handleNewType, the input object field walk,scanInterfacesOfTypeand theReferenceclasses. The scanner reads it as aMap<TypeDefinition<*>, EntryView>, so it can't add a type without going throughhandleFoundType. The first commit addsEntryViewand is the only one that changes code.SealedSubclassMatcheronly reads the dictionary.TypeReferenceErrorshas the messages for a type used where it can't be.SdlDirectiveshas all the directive code. It builds the directive definitions in its constructor, so nothing can apply a directive before they exist. The parser builds one perparseSchemaObjects(), where it used to callcreateDirectives(). One per parser would move directive errors tobuild(), and a secondmakeExecutableSchema()would point the first schema's directive arguments at the second schema's types.The plan was a shared scan-state object first, passed to the moved functions. But every piece of scanner state already has one writer, so passing it around would let every file that gets the object write it. Moving the writer with its state keeps that in one place. Classes that log take the owner's logger, so warnings keep the
SchemaClassScannerandSchemaParserlogger names.Not moved: directive scanning, root types, the field scan, the loop stages and type determination. They call back into the dictionary code, hold the stage order, or would only make a file shorter.
SchemaClassScanner.ktis now 580 lines andSchemaParser.kt466.NestedGenericWrapperTestonly changes itsReturnValueReferencequalifier.mvn clean verifypasses on JDK 25 against graphql-java 26.1 and 25.1 (410 tests), and on JDK 11 (409).Also ran all 33 verify-tools scenarios through the installed jar on master and on this branch, with graphql-java 26.1 and 25.1 at runtime. Printed schemas, operation results, errors and warnings (logger names included) are identical. The only diff is an identity hash in a 25.1 warning that prints
NodeUtil$DirectivesHolder@..., which changes with any build. Schema build time in the JMH benchmark is the same as master within noise (65.7 vs 65.6 ms for the edealer schema, 34.9 vs 33.6 for the large one).javapofSchemaParser,SchemaErrorandSchemaParserKtagainst 14.3.0 only differs in synthetic members Kotlin generates, andGRAPHQL_SCALARSandDEFAULT_DEPRECATION_MESSAGEstay inSchemaParserKt.