Skip to content

Commit 4bb273d

Browse files
parser: add specialized error for extension with descriptions (#3242)
Fixes #2568 Fixes #2385
1 parent 91bc70b commit 4bb273d

File tree

2 files changed

+57
-63
lines changed

2 files changed

+57
-63
lines changed

src/language/__tests__/schema-parser-test.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -340,8 +340,9 @@ describe('Schema Parser', () => {
340340
world: String
341341
}
342342
`).to.deep.equal({
343-
message: 'Syntax Error: Unexpected Name "extend".',
344-
locations: [{ line: 3, column: 7 }],
343+
message:
344+
'Syntax Error: Unexpected description, descriptions are supported only on type definitions.',
345+
locations: [{ line: 2, column: 7 }],
345346
});
346347

347348
expectSyntaxError(`
@@ -361,8 +362,9 @@ describe('Schema Parser', () => {
361362
world: String
362363
}
363364
`).to.deep.equal({
364-
message: 'Syntax Error: Unexpected Name "extend".',
365-
locations: [{ line: 3, column: 7 }],
365+
message:
366+
'Syntax Error: Unexpected description, descriptions are supported only on type definitions.',
367+
locations: [{ line: 2, column: 7 }],
366368
});
367369

368370
expectSyntaxError(`

src/language/parser.ts

Lines changed: 51 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ import type {
4141
NamedTypeNode,
4242
ListTypeNode,
4343
NonNullTypeNode,
44-
TypeSystemDefinitionNode,
4544
SchemaDefinitionNode,
4645
OperationTypeDefinitionNode,
4746
ScalarTypeDefinitionNode,
@@ -226,35 +225,72 @@ export class Parser {
226225
* ExecutableDefinition :
227226
* - OperationDefinition
228227
* - FragmentDefinition
228+
*
229+
* TypeSystemDefinition :
230+
* - SchemaDefinition
231+
* - TypeDefinition
232+
* - DirectiveDefinition
233+
*
234+
* TypeDefinition :
235+
* - ScalarTypeDefinition
236+
* - ObjectTypeDefinition
237+
* - InterfaceTypeDefinition
238+
* - UnionTypeDefinition
239+
* - EnumTypeDefinition
240+
* - InputObjectTypeDefinition
229241
*/
230242
parseDefinition(): DefinitionNode {
231-
if (this.peek(TokenKind.NAME)) {
232-
switch (this._lexer.token.value) {
233-
case 'query':
234-
case 'mutation':
235-
case 'subscription':
236-
return this.parseOperationDefinition();
237-
case 'fragment':
238-
return this.parseFragmentDefinition();
243+
if (this.peek(TokenKind.BRACE_L)) {
244+
return this.parseOperationDefinition();
245+
}
246+
247+
// Many definitions begin with a description and require a lookahead.
248+
const hasDescription = this.peekDescription();
249+
const keywordToken = hasDescription
250+
? this._lexer.lookahead()
251+
: this._lexer.token;
252+
253+
if (keywordToken.kind === TokenKind.NAME) {
254+
switch (keywordToken.value) {
239255
case 'schema':
256+
return this.parseSchemaDefinition();
240257
case 'scalar':
258+
return this.parseScalarTypeDefinition();
241259
case 'type':
260+
return this.parseObjectTypeDefinition();
242261
case 'interface':
262+
return this.parseInterfaceTypeDefinition();
243263
case 'union':
264+
return this.parseUnionTypeDefinition();
244265
case 'enum':
266+
return this.parseEnumTypeDefinition();
245267
case 'input':
268+
return this.parseInputObjectTypeDefinition();
246269
case 'directive':
247-
return this.parseTypeSystemDefinition();
270+
return this.parseDirectiveDefinition();
271+
}
272+
273+
if (hasDescription) {
274+
throw syntaxError(
275+
this._lexer.source,
276+
this._lexer.token.start,
277+
'Unexpected description, descriptions are supported only on type definitions.',
278+
);
279+
}
280+
281+
switch (keywordToken.value) {
282+
case 'query':
283+
case 'mutation':
284+
case 'subscription':
285+
return this.parseOperationDefinition();
286+
case 'fragment':
287+
return this.parseFragmentDefinition();
248288
case 'extend':
249289
return this.parseTypeSystemExtension();
250290
}
251-
} else if (this.peek(TokenKind.BRACE_L)) {
252-
return this.parseOperationDefinition();
253-
} else if (this.peekDescription()) {
254-
return this.parseTypeSystemDefinition();
255291
}
256292

257-
throw this.unexpected();
293+
throw this.unexpected(keywordToken);
258294
}
259295

260296
// Implements the parsing rules in the Operations section.
@@ -731,50 +767,6 @@ export class Parser {
731767

732768
// Implements the parsing rules in the Type Definition section.
733769

734-
/**
735-
* TypeSystemDefinition :
736-
* - SchemaDefinition
737-
* - TypeDefinition
738-
* - DirectiveDefinition
739-
*
740-
* TypeDefinition :
741-
* - ScalarTypeDefinition
742-
* - ObjectTypeDefinition
743-
* - InterfaceTypeDefinition
744-
* - UnionTypeDefinition
745-
* - EnumTypeDefinition
746-
* - InputObjectTypeDefinition
747-
*/
748-
parseTypeSystemDefinition(): TypeSystemDefinitionNode {
749-
// Many definitions begin with a description and require a lookahead.
750-
const keywordToken = this.peekDescription()
751-
? this._lexer.lookahead()
752-
: this._lexer.token;
753-
754-
if (keywordToken.kind === TokenKind.NAME) {
755-
switch (keywordToken.value) {
756-
case 'schema':
757-
return this.parseSchemaDefinition();
758-
case 'scalar':
759-
return this.parseScalarTypeDefinition();
760-
case 'type':
761-
return this.parseObjectTypeDefinition();
762-
case 'interface':
763-
return this.parseInterfaceTypeDefinition();
764-
case 'union':
765-
return this.parseUnionTypeDefinition();
766-
case 'enum':
767-
return this.parseEnumTypeDefinition();
768-
case 'input':
769-
return this.parseInputObjectTypeDefinition();
770-
case 'directive':
771-
return this.parseDirectiveDefinition();
772-
}
773-
}
774-
775-
throw this.unexpected(keywordToken);
776-
}
777-
778770
peekDescription(): boolean {
779771
return this.peek(TokenKind.STRING) || this.peek(TokenKind.BLOCK_STRING);
780772
}

0 commit comments

Comments
 (0)