diff --git a/crates/biome_graphql_parser/src/parser/definitions/mod.rs b/crates/biome_graphql_parser/src/parser/definitions/mod.rs index 3b33e39c1429..f9ce303f602b 100644 --- a/crates/biome_graphql_parser/src/parser/definitions/mod.rs +++ b/crates/biome_graphql_parser/src/parser/definitions/mod.rs @@ -5,6 +5,7 @@ mod object; mod operation; mod scalar; mod schema; +mod union; use crate::parser::{parse_error::expected_any_definition, GraphqlParser}; use biome_graphql_syntax::GraphqlSyntaxKind::{self, *}; @@ -20,6 +21,7 @@ use self::{ operation::{is_at_operation, parse_operation_definition}, scalar::{is_at_scalar_type_definition, parse_scalar_type_definition}, schema::{is_at_schema_definition, parse_schema_definition}, + union::{is_at_union_type_definition, parse_union_type_definition}, }; pub(crate) use operation::is_at_selection_set_end; @@ -76,6 +78,8 @@ fn parse_definition(p: &mut GraphqlParser) -> ParsedSyntax { parse_object_type_definition(p) } else if is_at_interface_type_definition(p) { parse_interface_type_definition(p) + } else if is_at_union_type_definition(p) { + parse_union_type_definition(p) } else { Absent } @@ -90,4 +94,5 @@ fn is_at_definition(p: &mut GraphqlParser<'_>) -> bool { || is_at_scalar_type_definition(p) || is_at_object_type_definition(p) || is_at_interface_type_definition(p) + || is_at_union_type_definition(p) } diff --git a/crates/biome_graphql_parser/src/parser/definitions/union.rs b/crates/biome_graphql_parser/src/parser/definitions/union.rs new file mode 100644 index 000000000000..473897541c9f --- /dev/null +++ b/crates/biome_graphql_parser/src/parser/definitions/union.rs @@ -0,0 +1,139 @@ +use crate::parser::{ + directive::DirectiveList, + is_at_name, parse_description, + parse_error::{expected_name, expected_named_type}, + parse_name, + r#type::parse_named_type, + value::is_at_string, + GraphqlParser, +}; +use biome_graphql_syntax::{ + GraphqlSyntaxKind::{self, *}, + T, +}; +use biome_parser::{ + parse_lists::{ParseNodeList, ParseSeparatedList}, + parse_recovery::ParseRecovery, + parsed_syntax::ParsedSyntax, + prelude::ParsedSyntax::*, + token_source::TokenSource, + Parser, +}; + +use super::is_at_definition; + +#[inline] +pub(crate) fn parse_union_type_definition(p: &mut GraphqlParser) -> ParsedSyntax { + if !is_at_union_type_definition(p) { + return Absent; + } + let m = p.start(); + + // description is optional + parse_description(p).ok(); + + p.bump(T![union]); + + parse_name(p).or_add_diagnostic(p, expected_name); + + DirectiveList.parse_list(p); + + // union member types are optional + parse_union_member_types(p).ok(); + + Present(m.complete(p, GRAPHQL_UNION_TYPE_DEFINITION)) +} + +#[inline] +fn parse_union_member_types(p: &mut GraphqlParser) -> ParsedSyntax { + if !is_at_union_member_types(p) { + return Absent; + } + let m = p.start(); + p.expect(T![=]); + + if p.at(T![|]) { + p.bump(T![|]); + } + + let position = p.source().position(); + UnionMemberTypeList.parse_list(p); + + // has not progressed, meaning no union member types were parsed + if position == p.source().position() { + p.error(expected_named_type(p, p.cur_range())); + } + Present(m.complete(p, GRAPHQL_UNION_MEMBER_TYPES)) +} + +#[derive(Default)] +struct UnionMemberTypeList; + +impl ParseSeparatedList for UnionMemberTypeList { + type Kind = GraphqlSyntaxKind; + type Parser<'source> = GraphqlParser<'source>; + + const LIST_KIND: Self::Kind = GRAPHQL_UNION_MEMBER_TYPE_LIST; + + fn parse_element(&mut self, p: &mut Self::Parser<'_>) -> ParsedSyntax { + parse_named_type(p) + } + + fn is_at_list_end(&self, p: &mut Self::Parser<'_>) -> bool { + is_at_union_member_types_end(p) + } + + fn recover( + &mut self, + p: &mut Self::Parser<'_>, + parsed_element: ParsedSyntax, + ) -> biome_parser::parse_recovery::RecoveryResult { + parsed_element.or_recover(p, &UnionMemberListParseRecovery, expected_named_type) + } + + fn separating_element_kind(&mut self) -> Self::Kind { + T![|] + } + + fn allow_trailing_separating_element(&self) -> bool { + false + } +} + +struct UnionMemberListParseRecovery; + +impl ParseRecovery for UnionMemberListParseRecovery { + type Kind = GraphqlSyntaxKind; + type Parser<'source> = GraphqlParser<'source>; + const RECOVERED_KIND: Self::Kind = GRAPHQL_BOGUS; + + fn is_at_recovered(&self, p: &mut Self::Parser<'_>) -> bool { + p.at(T![|]) + // After a union definition is a new type definition so it's safe to + // assume any name we see before a new type definition is a union + // member type + || is_at_name(p) + || is_at_union_member_types_end(p) + } +} + +#[inline] +pub(crate) fn is_at_union_type_definition(p: &mut GraphqlParser<'_>) -> bool { + p.at(T![union]) || (is_at_string(p) && p.nth_at(1, T![union])) +} + +#[inline] +fn is_at_union_member_types(p: &mut GraphqlParser<'_>) -> bool { + p.at(T![=]) + // missing = + || p.at(T![|]) + // missing both = and |. After a union definition is a new type definition + // so it's safe to assume any name we see before a new type definition is + // a union member type + || is_at_name(p) +} + +#[inline] +fn is_at_union_member_types_end(p: &mut GraphqlParser<'_>) -> bool { + is_at_definition(p) +} diff --git a/crates/biome_graphql_parser/tests/graphql_test_suite/err/union.graphql b/crates/biome_graphql_parser/tests/graphql_test_suite/err/union.graphql new file mode 100644 index 000000000000..6b7329792600 --- /dev/null +++ b/crates/biome_graphql_parser/tests/graphql_test_suite/err/union.graphql @@ -0,0 +1,14 @@ +union SearchResult = Photo | + +union SearchResult + | Photo + | Person + + +union SearchResult | + +union SearchResult = + +union SearchResult = | + +union SearchResult @ = diff --git a/crates/biome_graphql_parser/tests/graphql_test_suite/err/union.graphql.snap b/crates/biome_graphql_parser/tests/graphql_test_suite/err/union.graphql.snap new file mode 100644 index 000000000000..c7d690efe6d3 --- /dev/null +++ b/crates/biome_graphql_parser/tests/graphql_test_suite/err/union.graphql.snap @@ -0,0 +1,361 @@ +--- +source: crates/biome_graphql_parser/tests/spec_test.rs +expression: snapshot +--- +## Input +```graphql +union SearchResult = Photo | + +union SearchResult + | Photo + | Person + + +union SearchResult | + +union SearchResult = + +union SearchResult = | + +union SearchResult @ = + +``` + +## AST + +``` +GraphqlRoot { + bom_token: missing (optional), + definitions: GraphqlDefinitionList [ + GraphqlUnionTypeDefinition { + description: missing (optional), + union_token: UNION_KW@0..6 "union" [] [Whitespace(" ")], + name: GraphqlName { + value_token: GRAPHQL_NAME@6..19 "SearchResult" [] [Whitespace(" ")], + }, + directives: GraphqlDirectiveList [], + union_members: GraphqlUnionMemberTypes { + eq_token: EQ@19..21 "=" [] [Whitespace(" ")], + bitwise_or_token: missing (optional), + members: GraphqlUnionMemberTypeList [ + GraphqlNamedType { + name: GraphqlName { + value_token: GRAPHQL_NAME@21..27 "Photo" [] [Whitespace(" ")], + }, + }, + PIPE@27..28 "|" [] [], + missing element, + ], + }, + }, + GraphqlUnionTypeDefinition { + description: missing (optional), + union_token: UNION_KW@28..36 "union" [Newline("\n"), Newline("\n")] [Whitespace(" ")], + name: GraphqlName { + value_token: GRAPHQL_NAME@36..48 "SearchResult" [] [], + }, + directives: GraphqlDirectiveList [], + union_members: GraphqlUnionMemberTypes { + eq_token: missing (required), + bitwise_or_token: PIPE@48..52 "|" [Newline("\n"), Whitespace("\t")] [Whitespace(" ")], + members: GraphqlUnionMemberTypeList [ + GraphqlNamedType { + name: GraphqlName { + value_token: GRAPHQL_NAME@52..57 "Photo" [] [], + }, + }, + PIPE@57..61 "|" [Newline("\n"), Whitespace("\t")] [Whitespace(" ")], + GraphqlNamedType { + name: GraphqlName { + value_token: GRAPHQL_NAME@61..67 "Person" [] [], + }, + }, + ], + }, + }, + GraphqlUnionTypeDefinition { + description: missing (optional), + union_token: UNION_KW@67..76 "union" [Newline("\n"), Newline("\n"), Newline("\n")] [Whitespace(" ")], + name: GraphqlName { + value_token: GRAPHQL_NAME@76..89 "SearchResult" [] [Whitespace(" ")], + }, + directives: GraphqlDirectiveList [], + union_members: GraphqlUnionMemberTypes { + eq_token: missing (required), + bitwise_or_token: PIPE@89..90 "|" [] [], + members: GraphqlUnionMemberTypeList [], + }, + }, + GraphqlUnionTypeDefinition { + description: missing (optional), + union_token: UNION_KW@90..98 "union" [Newline("\n"), Newline("\n")] [Whitespace(" ")], + name: GraphqlName { + value_token: GRAPHQL_NAME@98..111 "SearchResult" [] [Whitespace(" ")], + }, + directives: GraphqlDirectiveList [], + union_members: GraphqlUnionMemberTypes { + eq_token: EQ@111..112 "=" [] [], + bitwise_or_token: missing (optional), + members: GraphqlUnionMemberTypeList [], + }, + }, + GraphqlUnionTypeDefinition { + description: missing (optional), + union_token: UNION_KW@112..120 "union" [Newline("\n"), Newline("\n")] [Whitespace(" ")], + name: GraphqlName { + value_token: GRAPHQL_NAME@120..133 "SearchResult" [] [Whitespace(" ")], + }, + directives: GraphqlDirectiveList [], + union_members: GraphqlUnionMemberTypes { + eq_token: EQ@133..135 "=" [] [Whitespace(" ")], + bitwise_or_token: PIPE@135..136 "|" [] [], + members: GraphqlUnionMemberTypeList [], + }, + }, + GraphqlUnionTypeDefinition { + description: missing (optional), + union_token: UNION_KW@136..144 "union" [Newline("\n"), Newline("\n")] [Whitespace(" ")], + name: GraphqlName { + value_token: GRAPHQL_NAME@144..157 "SearchResult" [] [Whitespace(" ")], + }, + directives: GraphqlDirectiveList [ + GraphqlDirective { + at_token: AT@157..159 "@" [] [Whitespace(" ")], + name: missing (required), + arguments: missing (optional), + }, + ], + union_members: GraphqlUnionMemberTypes { + eq_token: EQ@159..160 "=" [] [], + bitwise_or_token: missing (optional), + members: GraphqlUnionMemberTypeList [], + }, + }, + ], + eof_token: EOF@160..161 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: GRAPHQL_ROOT@0..161 + 0: (empty) + 1: GRAPHQL_DEFINITION_LIST@0..160 + 0: GRAPHQL_UNION_TYPE_DEFINITION@0..28 + 0: (empty) + 1: UNION_KW@0..6 "union" [] [Whitespace(" ")] + 2: GRAPHQL_NAME@6..19 + 0: GRAPHQL_NAME@6..19 "SearchResult" [] [Whitespace(" ")] + 3: GRAPHQL_DIRECTIVE_LIST@19..19 + 4: GRAPHQL_UNION_MEMBER_TYPES@19..28 + 0: EQ@19..21 "=" [] [Whitespace(" ")] + 1: (empty) + 2: GRAPHQL_UNION_MEMBER_TYPE_LIST@21..28 + 0: GRAPHQL_NAMED_TYPE@21..27 + 0: GRAPHQL_NAME@21..27 + 0: GRAPHQL_NAME@21..27 "Photo" [] [Whitespace(" ")] + 1: PIPE@27..28 "|" [] [] + 2: (empty) + 1: GRAPHQL_UNION_TYPE_DEFINITION@28..67 + 0: (empty) + 1: UNION_KW@28..36 "union" [Newline("\n"), Newline("\n")] [Whitespace(" ")] + 2: GRAPHQL_NAME@36..48 + 0: GRAPHQL_NAME@36..48 "SearchResult" [] [] + 3: GRAPHQL_DIRECTIVE_LIST@48..48 + 4: GRAPHQL_UNION_MEMBER_TYPES@48..67 + 0: (empty) + 1: PIPE@48..52 "|" [Newline("\n"), Whitespace("\t")] [Whitespace(" ")] + 2: GRAPHQL_UNION_MEMBER_TYPE_LIST@52..67 + 0: GRAPHQL_NAMED_TYPE@52..57 + 0: GRAPHQL_NAME@52..57 + 0: GRAPHQL_NAME@52..57 "Photo" [] [] + 1: PIPE@57..61 "|" [Newline("\n"), Whitespace("\t")] [Whitespace(" ")] + 2: GRAPHQL_NAMED_TYPE@61..67 + 0: GRAPHQL_NAME@61..67 + 0: GRAPHQL_NAME@61..67 "Person" [] [] + 2: GRAPHQL_UNION_TYPE_DEFINITION@67..90 + 0: (empty) + 1: UNION_KW@67..76 "union" [Newline("\n"), Newline("\n"), Newline("\n")] [Whitespace(" ")] + 2: GRAPHQL_NAME@76..89 + 0: GRAPHQL_NAME@76..89 "SearchResult" [] [Whitespace(" ")] + 3: GRAPHQL_DIRECTIVE_LIST@89..89 + 4: GRAPHQL_UNION_MEMBER_TYPES@89..90 + 0: (empty) + 1: PIPE@89..90 "|" [] [] + 2: GRAPHQL_UNION_MEMBER_TYPE_LIST@90..90 + 3: GRAPHQL_UNION_TYPE_DEFINITION@90..112 + 0: (empty) + 1: UNION_KW@90..98 "union" [Newline("\n"), Newline("\n")] [Whitespace(" ")] + 2: GRAPHQL_NAME@98..111 + 0: GRAPHQL_NAME@98..111 "SearchResult" [] [Whitespace(" ")] + 3: GRAPHQL_DIRECTIVE_LIST@111..111 + 4: GRAPHQL_UNION_MEMBER_TYPES@111..112 + 0: EQ@111..112 "=" [] [] + 1: (empty) + 2: GRAPHQL_UNION_MEMBER_TYPE_LIST@112..112 + 4: GRAPHQL_UNION_TYPE_DEFINITION@112..136 + 0: (empty) + 1: UNION_KW@112..120 "union" [Newline("\n"), Newline("\n")] [Whitespace(" ")] + 2: GRAPHQL_NAME@120..133 + 0: GRAPHQL_NAME@120..133 "SearchResult" [] [Whitespace(" ")] + 3: GRAPHQL_DIRECTIVE_LIST@133..133 + 4: GRAPHQL_UNION_MEMBER_TYPES@133..136 + 0: EQ@133..135 "=" [] [Whitespace(" ")] + 1: PIPE@135..136 "|" [] [] + 2: GRAPHQL_UNION_MEMBER_TYPE_LIST@136..136 + 5: GRAPHQL_UNION_TYPE_DEFINITION@136..160 + 0: (empty) + 1: UNION_KW@136..144 "union" [Newline("\n"), Newline("\n")] [Whitespace(" ")] + 2: GRAPHQL_NAME@144..157 + 0: GRAPHQL_NAME@144..157 "SearchResult" [] [Whitespace(" ")] + 3: GRAPHQL_DIRECTIVE_LIST@157..159 + 0: GRAPHQL_DIRECTIVE@157..159 + 0: AT@157..159 "@" [] [Whitespace(" ")] + 1: (empty) + 2: (empty) + 4: GRAPHQL_UNION_MEMBER_TYPES@159..160 + 0: EQ@159..160 "=" [] [] + 1: (empty) + 2: GRAPHQL_UNION_MEMBER_TYPE_LIST@160..160 + 2: EOF@160..161 "" [Newline("\n")] [] + +``` + +## Diagnostics + +``` +union.graphql:3:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected a named type but instead found 'union'. + + 1 │ union SearchResult = Photo | + 2 │ + > 3 │ union SearchResult + │ ^^^^^ + 4 │ | Photo + 5 │ | Person + + i Expected a named type here. + + 1 │ union SearchResult = Photo | + 2 │ + > 3 │ union SearchResult + │ ^^^^^ + 4 │ | Photo + 5 │ | Person + +union.graphql:4:2 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × expected `=` but instead found `|` + + 3 │ union SearchResult + > 4 │ | Photo + │ ^ + 5 │ | Person + 6 │ + + i Remove | + +union.graphql:8:20 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × expected `=` but instead found `|` + + > 8 │ union SearchResult | + │ ^ + 9 │ + 10 │ union SearchResult = + + i Remove | + +union.graphql:10:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected a named type but instead found 'union'. + + 8 │ union SearchResult | + 9 │ + > 10 │ union SearchResult = + │ ^^^^^ + 11 │ + 12 │ union SearchResult = | + + i Expected a named type here. + + 8 │ union SearchResult | + 9 │ + > 10 │ union SearchResult = + │ ^^^^^ + 11 │ + 12 │ union SearchResult = | + +union.graphql:12:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected a named type but instead found 'union'. + + 10 │ union SearchResult = + 11 │ + > 12 │ union SearchResult = | + │ ^^^^^ + 13 │ + 14 │ union SearchResult @ = + + i Expected a named type here. + + 10 │ union SearchResult = + 11 │ + > 12 │ union SearchResult = | + │ ^^^^^ + 13 │ + 14 │ union SearchResult @ = + +union.graphql:14:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected a named type but instead found 'union'. + + 12 │ union SearchResult = | + 13 │ + > 14 │ union SearchResult @ = + │ ^^^^^ + 15 │ + + i Expected a named type here. + + 12 │ union SearchResult = | + 13 │ + > 14 │ union SearchResult @ = + │ ^^^^^ + 15 │ + +union.graphql:14:22 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected a name but instead found '='. + + 12 │ union SearchResult = | + 13 │ + > 14 │ union SearchResult @ = + │ ^ + 15 │ + + i Expected a name here. + + 12 │ union SearchResult = | + 13 │ + > 14 │ union SearchResult @ = + │ ^ + 15 │ + +union.graphql:15:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + × Expected a named type but instead found the end of the file. + + 14 │ union SearchResult @ = + > 15 │ + │ + + i Expected a named type here. + + 14 │ union SearchResult @ = + > 15 │ + │ + +``` diff --git a/crates/biome_graphql_parser/tests/graphql_test_suite/ok/definitions/union.graphql b/crates/biome_graphql_parser/tests/graphql_test_suite/ok/definitions/union.graphql new file mode 100644 index 000000000000..da90c0882c35 --- /dev/null +++ b/crates/biome_graphql_parser/tests/graphql_test_suite/ok/definitions/union.graphql @@ -0,0 +1,11 @@ +union SearchResult = Photo | Person + +union SearchResult = + | Photo + | Person + +union SearchResult + +union SearchResult @deprecated + +union SearchResult @deprecated = Photo | Person diff --git a/crates/biome_graphql_parser/tests/graphql_test_suite/ok/definitions/union.graphql.snap b/crates/biome_graphql_parser/tests/graphql_test_suite/ok/definitions/union.graphql.snap new file mode 100644 index 000000000000..a8411d55de77 --- /dev/null +++ b/crates/biome_graphql_parser/tests/graphql_test_suite/ok/definitions/union.graphql.snap @@ -0,0 +1,224 @@ +--- +source: crates/biome_graphql_parser/tests/spec_test.rs +expression: snapshot +--- +## Input +```graphql +union SearchResult = Photo | Person + +union SearchResult = + | Photo + | Person + +union SearchResult + +union SearchResult @deprecated + +union SearchResult @deprecated = Photo | Person + +``` + +## AST + +``` +GraphqlRoot { + bom_token: missing (optional), + definitions: GraphqlDefinitionList [ + GraphqlUnionTypeDefinition { + description: missing (optional), + union_token: UNION_KW@0..6 "union" [] [Whitespace(" ")], + name: GraphqlName { + value_token: GRAPHQL_NAME@6..19 "SearchResult" [] [Whitespace(" ")], + }, + directives: GraphqlDirectiveList [], + union_members: GraphqlUnionMemberTypes { + eq_token: EQ@19..21 "=" [] [Whitespace(" ")], + bitwise_or_token: missing (optional), + members: GraphqlUnionMemberTypeList [ + GraphqlNamedType { + name: GraphqlName { + value_token: GRAPHQL_NAME@21..27 "Photo" [] [Whitespace(" ")], + }, + }, + PIPE@27..29 "|" [] [Whitespace(" ")], + GraphqlNamedType { + name: GraphqlName { + value_token: GRAPHQL_NAME@29..35 "Person" [] [], + }, + }, + ], + }, + }, + GraphqlUnionTypeDefinition { + description: missing (optional), + union_token: UNION_KW@35..43 "union" [Newline("\n"), Newline("\n")] [Whitespace(" ")], + name: GraphqlName { + value_token: GRAPHQL_NAME@43..56 "SearchResult" [] [Whitespace(" ")], + }, + directives: GraphqlDirectiveList [], + union_members: GraphqlUnionMemberTypes { + eq_token: EQ@56..57 "=" [] [], + bitwise_or_token: PIPE@57..61 "|" [Newline("\n"), Whitespace("\t")] [Whitespace(" ")], + members: GraphqlUnionMemberTypeList [ + GraphqlNamedType { + name: GraphqlName { + value_token: GRAPHQL_NAME@61..66 "Photo" [] [], + }, + }, + PIPE@66..70 "|" [Newline("\n"), Whitespace("\t")] [Whitespace(" ")], + GraphqlNamedType { + name: GraphqlName { + value_token: GRAPHQL_NAME@70..76 "Person" [] [], + }, + }, + ], + }, + }, + GraphqlUnionTypeDefinition { + description: missing (optional), + union_token: UNION_KW@76..84 "union" [Newline("\n"), Newline("\n")] [Whitespace(" ")], + name: GraphqlName { + value_token: GRAPHQL_NAME@84..96 "SearchResult" [] [], + }, + directives: GraphqlDirectiveList [], + union_members: missing (optional), + }, + GraphqlUnionTypeDefinition { + description: missing (optional), + union_token: UNION_KW@96..104 "union" [Newline("\n"), Newline("\n")] [Whitespace(" ")], + name: GraphqlName { + value_token: GRAPHQL_NAME@104..117 "SearchResult" [] [Whitespace(" ")], + }, + directives: GraphqlDirectiveList [ + GraphqlDirective { + at_token: AT@117..118 "@" [] [], + name: GraphqlName { + value_token: GRAPHQL_NAME@118..128 "deprecated" [] [], + }, + arguments: missing (optional), + }, + ], + union_members: missing (optional), + }, + GraphqlUnionTypeDefinition { + description: missing (optional), + union_token: UNION_KW@128..136 "union" [Newline("\n"), Newline("\n")] [Whitespace(" ")], + name: GraphqlName { + value_token: GRAPHQL_NAME@136..149 "SearchResult" [] [Whitespace(" ")], + }, + directives: GraphqlDirectiveList [ + GraphqlDirective { + at_token: AT@149..150 "@" [] [], + name: GraphqlName { + value_token: GRAPHQL_NAME@150..161 "deprecated" [] [Whitespace(" ")], + }, + arguments: missing (optional), + }, + ], + union_members: GraphqlUnionMemberTypes { + eq_token: EQ@161..163 "=" [] [Whitespace(" ")], + bitwise_or_token: missing (optional), + members: GraphqlUnionMemberTypeList [ + GraphqlNamedType { + name: GraphqlName { + value_token: GRAPHQL_NAME@163..169 "Photo" [] [Whitespace(" ")], + }, + }, + PIPE@169..171 "|" [] [Whitespace(" ")], + GraphqlNamedType { + name: GraphqlName { + value_token: GRAPHQL_NAME@171..177 "Person" [] [], + }, + }, + ], + }, + }, + ], + eof_token: EOF@177..178 "" [Newline("\n")] [], +} +``` + +## CST + +``` +0: GRAPHQL_ROOT@0..178 + 0: (empty) + 1: GRAPHQL_DEFINITION_LIST@0..177 + 0: GRAPHQL_UNION_TYPE_DEFINITION@0..35 + 0: (empty) + 1: UNION_KW@0..6 "union" [] [Whitespace(" ")] + 2: GRAPHQL_NAME@6..19 + 0: GRAPHQL_NAME@6..19 "SearchResult" [] [Whitespace(" ")] + 3: GRAPHQL_DIRECTIVE_LIST@19..19 + 4: GRAPHQL_UNION_MEMBER_TYPES@19..35 + 0: EQ@19..21 "=" [] [Whitespace(" ")] + 1: (empty) + 2: GRAPHQL_UNION_MEMBER_TYPE_LIST@21..35 + 0: GRAPHQL_NAMED_TYPE@21..27 + 0: GRAPHQL_NAME@21..27 + 0: GRAPHQL_NAME@21..27 "Photo" [] [Whitespace(" ")] + 1: PIPE@27..29 "|" [] [Whitespace(" ")] + 2: GRAPHQL_NAMED_TYPE@29..35 + 0: GRAPHQL_NAME@29..35 + 0: GRAPHQL_NAME@29..35 "Person" [] [] + 1: GRAPHQL_UNION_TYPE_DEFINITION@35..76 + 0: (empty) + 1: UNION_KW@35..43 "union" [Newline("\n"), Newline("\n")] [Whitespace(" ")] + 2: GRAPHQL_NAME@43..56 + 0: GRAPHQL_NAME@43..56 "SearchResult" [] [Whitespace(" ")] + 3: GRAPHQL_DIRECTIVE_LIST@56..56 + 4: GRAPHQL_UNION_MEMBER_TYPES@56..76 + 0: EQ@56..57 "=" [] [] + 1: PIPE@57..61 "|" [Newline("\n"), Whitespace("\t")] [Whitespace(" ")] + 2: GRAPHQL_UNION_MEMBER_TYPE_LIST@61..76 + 0: GRAPHQL_NAMED_TYPE@61..66 + 0: GRAPHQL_NAME@61..66 + 0: GRAPHQL_NAME@61..66 "Photo" [] [] + 1: PIPE@66..70 "|" [Newline("\n"), Whitespace("\t")] [Whitespace(" ")] + 2: GRAPHQL_NAMED_TYPE@70..76 + 0: GRAPHQL_NAME@70..76 + 0: GRAPHQL_NAME@70..76 "Person" [] [] + 2: GRAPHQL_UNION_TYPE_DEFINITION@76..96 + 0: (empty) + 1: UNION_KW@76..84 "union" [Newline("\n"), Newline("\n")] [Whitespace(" ")] + 2: GRAPHQL_NAME@84..96 + 0: GRAPHQL_NAME@84..96 "SearchResult" [] [] + 3: GRAPHQL_DIRECTIVE_LIST@96..96 + 4: (empty) + 3: GRAPHQL_UNION_TYPE_DEFINITION@96..128 + 0: (empty) + 1: UNION_KW@96..104 "union" [Newline("\n"), Newline("\n")] [Whitespace(" ")] + 2: GRAPHQL_NAME@104..117 + 0: GRAPHQL_NAME@104..117 "SearchResult" [] [Whitespace(" ")] + 3: GRAPHQL_DIRECTIVE_LIST@117..128 + 0: GRAPHQL_DIRECTIVE@117..128 + 0: AT@117..118 "@" [] [] + 1: GRAPHQL_NAME@118..128 + 0: GRAPHQL_NAME@118..128 "deprecated" [] [] + 2: (empty) + 4: (empty) + 4: GRAPHQL_UNION_TYPE_DEFINITION@128..177 + 0: (empty) + 1: UNION_KW@128..136 "union" [Newline("\n"), Newline("\n")] [Whitespace(" ")] + 2: GRAPHQL_NAME@136..149 + 0: GRAPHQL_NAME@136..149 "SearchResult" [] [Whitespace(" ")] + 3: GRAPHQL_DIRECTIVE_LIST@149..161 + 0: GRAPHQL_DIRECTIVE@149..161 + 0: AT@149..150 "@" [] [] + 1: GRAPHQL_NAME@150..161 + 0: GRAPHQL_NAME@150..161 "deprecated" [] [Whitespace(" ")] + 2: (empty) + 4: GRAPHQL_UNION_MEMBER_TYPES@161..177 + 0: EQ@161..163 "=" [] [Whitespace(" ")] + 1: (empty) + 2: GRAPHQL_UNION_MEMBER_TYPE_LIST@163..177 + 0: GRAPHQL_NAMED_TYPE@163..169 + 0: GRAPHQL_NAME@163..169 + 0: GRAPHQL_NAME@163..169 "Photo" [] [Whitespace(" ")] + 1: PIPE@169..171 "|" [] [Whitespace(" ")] + 2: GRAPHQL_NAMED_TYPE@171..177 + 0: GRAPHQL_NAME@171..177 + 0: GRAPHQL_NAME@171..177 "Person" [] [] + 2: EOF@177..178 "" [Newline("\n")] [] + +```