Repository: apollographql/apollo-rs
Pull request #918 (2024-10-07 15:15:42)

fix(parser): an empty document is a syntax error
In the [spec grammar](https://spec.graphql.org/draft/#sec-Document), both `Document` and `ExecutableDocument` have a [one or more](https://spec.graphql.org/draft/#sec-Grammar-Notation.Optionality-and-Lists) `list` suffix without an `opt` suffix so an empty list does not match the grammar.


Patch:
diff --git a/crates/apollo-parser/CHANGELOG.md b/crates/apollo-parser/CHANGELOG.md
index 674d563..93169e5 100644
--- a/crates/apollo-parser/CHANGELOG.md
+++ b/crates/apollo-parser/CHANGELOG.md
@@ -17,6 +17,15 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 
 ## Documentation -->
 
+# [0.8.3](https://crates.io/crates/apollo-parser/0.8.3) - 2024-10-07
+
+## Fixes
+- **Parsing an empty document is a syntax error - [SimonSapin], [pull/918].**
+
+[SimonSapin]: https://github.com/SimonSapin
+[pull/918]: https://github.com/apollographql/apollo-rs/pull/918
+
+
 # [0.8.2](https://crates.io/crates/apollo-parser/0.8.2) - 2024-09-09
 
 ## Fixes
diff --git a/crates/apollo-parser/Cargo.toml b/crates/apollo-parser/Cargo.toml
index 677ff6b..d858caf 100644
--- a/crates/apollo-parser/Cargo.toml
+++ b/crates/apollo-parser/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "apollo-parser"
-version = "0.8.2" # When bumping, also update README.md
+version = "0.8.3" # When bumping, also update README.md
 authors = ["Irina Shestak <shestak.irina@gmail.com>"]
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/apollographql/apollo-rs"
diff --git a/crates/apollo-parser/README.md b/crates/apollo-parser/README.md
index 06a6ea1..e5c6501 100644
--- a/crates/apollo-parser/README.md
+++ b/crates/apollo-parser/README.md
@@ -35,7 +35,7 @@ Or add this to your `Cargo.toml` for a manual installation:
 ```toml
 # Just an example, change to the necessary package version.
 [dependencies]
-apollo-parser = "0.8.2"
+apollo-parser = "0.8.3"
 ```
 
 ## Rust versions
diff --git a/crates/apollo-parser/src/parser/grammar/document.rs b/crates/apollo-parser/src/parser/grammar/document.rs
index 32c203c..694b927 100644
--- a/crates/apollo-parser/src/parser/grammar/document.rs
+++ b/crates/apollo-parser/src/parser/grammar/document.rs
@@ -21,6 +21,10 @@ use std::ops::ControlFlow;
 pub(crate) fn document(p: &mut Parser) {
     let doc = p.start_node(SyntaxKind::DOCUMENT);
 
+    if let None | Some(TokenKind::Eof) = p.peek() {
+        p.err("Unexpected <EOF>.");
+    }
+
     p.peek_while(|p, kind| {
         assert_eq!(
             p.recursion_limit.current, 0,


Test patch:
diff --git a/crates/apollo-compiler/test_data/diagnostics/0017_schema_with_unspecified_scalar.txt b/crates/apollo-compiler/test_data/diagnostics/0017_schema_with_unspecified_scalar.txt
index 12acf69..2f44dba 100644
--- a/crates/apollo-compiler/test_data/diagnostics/0017_schema_with_unspecified_scalar.txt
+++ b/crates/apollo-compiler/test_data/diagnostics/0017_schema_with_unspecified_scalar.txt
@@ -1,2 +1,9 @@
 Error: missing query root operation type in schema definition
+Error: syntax error: Unexpected <EOF>.
+   ╭─[0017_schema_with_unspecified_scalar.graphql:1:54]
+   │
+ 1 │ # Placeholder for removed test, to avoid renumbering
+   │                                                      │ 
+   │                                                      ╰─ Unexpected <EOF>.
+───╯
 
diff --git a/crates/apollo-compiler/test_data/diagnostics/0018_schema_with_specified_scalar_missing_values.txt b/crates/apollo-compiler/test_data/diagnostics/0018_schema_with_specified_scalar_missing_values.txt
index 12acf69..2e758c3 100644
--- a/crates/apollo-compiler/test_data/diagnostics/0018_schema_with_specified_scalar_missing_values.txt
+++ b/crates/apollo-compiler/test_data/diagnostics/0018_schema_with_specified_scalar_missing_values.txt
@@ -1,2 +1,9 @@
 Error: missing query root operation type in schema definition
+Error: syntax error: Unexpected <EOF>.
+   ╭─[0018_schema_with_specified_scalar_missing_values.graphql:1:54]
+   │
+ 1 │ # Placeholder for removed test, to avoid renumbering
+   │                                                      │ 
+   │                                                      ╰─ Unexpected <EOF>.
+───╯
 
diff --git a/crates/apollo-compiler/test_data/diagnostics/0020_enum_values_with_uncapitalised_values.txt b/crates/apollo-compiler/test_data/diagnostics/0020_enum_values_with_uncapitalised_values.txt
index 12acf69..b88d534 100644
--- a/crates/apollo-compiler/test_data/diagnostics/0020_enum_values_with_uncapitalised_values.txt
+++ b/crates/apollo-compiler/test_data/diagnostics/0020_enum_values_with_uncapitalised_values.txt
@@ -1,2 +1,9 @@
 Error: missing query root operation type in schema definition
+Error: syntax error: Unexpected <EOF>.
+   ╭─[0020_enum_values_with_uncapitalised_values.graphql:1:54]
+   │
+ 1 │ # Placeholder for removed test, to avoid renumbering
+   │                                                      │ 
+   │                                                      ╰─ Unexpected <EOF>.
+───╯
 
diff --git a/crates/apollo-compiler/tests/executable.rs b/crates/apollo-compiler/tests/executable.rs
index f33334c..8ee0e1c 100644
--- a/crates/apollo-compiler/tests/executable.rs
+++ b/crates/apollo-compiler/tests/executable.rs
@@ -12,7 +12,7 @@ fn get_operations() {
     let op = r#"{ name }"#;
     let named_op = r#"query getName { name } "#;
     let several_named_op = r#"query getName { name } query getAnotherName { name }"#;
-    let noop = r#""#;
+    let empty = r#""#;
 
     let schema = Schema::parse_and_validate(type_system, "ts.graphql").unwrap();
     let doc = ExecutableDocument::parse_and_validate(&schema, op, "op.graphql").unwrap();
@@ -27,7 +27,10 @@ fn get_operations() {
     assert!(doc.operations.get(Some("getName")).is_ok());
     assert!(doc.operations.get(None).is_err());
 
-    let doc = ExecutableDocument::parse_and_validate(&schema, noop, "op.graphql").unwrap();
+    // Empty document is a syntax error
+    let doc = ExecutableDocument::parse_and_validate(&schema, empty, "op.graphql")
+        .unwrap_err()
+        .partial;
     assert!(doc.operations.get(Some("getName")).is_err());
     assert!(doc.operations.get(None).is_err());
 }
diff --git a/crates/apollo-parser/test_data/parser/ok/0001_empty.graphql b/crates/apollo-parser/test_data/parser/err/0061_empty.graphql
similarity index 100%
rename from crates/apollo-parser/test_data/parser/ok/0001_empty.graphql
rename to crates/apollo-parser/test_data/parser/err/0061_empty.graphql
diff --git a/crates/apollo-parser/test_data/parser/err/0061_empty.txt b/crates/apollo-parser/test_data/parser/err/0061_empty.txt
new file mode 100644
index 0000000..7e2500c
--- /dev/null
+++ b/crates/apollo-parser/test_data/parser/err/0061_empty.txt
@@ -0,0 +1,3 @@
+- DOCUMENT@0..0
+- ERROR@1:1 "Unexpected <EOF>." EOF
+recursion limit: 500, high: 0
\ No newline at end of file
diff --git a/crates/apollo-parser/test_data/parser/ok/0001_empty.txt b/crates/apollo-parser/test_data/parser/ok/0001_empty.txt
deleted file mode 100644
index 1690c53..0000000
--- a/crates/apollo-parser/test_data/parser/ok/0001_empty.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-- DOCUMENT@0..0
-recursion limit: 500, high: 0
\ No newline at end of file