Repository: apollographql/apollo-rs
Pull request #320 (2022-09-30 13:48:11)

fix(apollo-parser): unterminated string with unicode and line terminators
We were missing adding a line terminator character to the errors created by the lexer in case of a unterminated string. This showed up incidentally while dealing with unicode and the fact that it's of a different byte length than most other characters.

fixes https://github.com/apollographql/apollo-rs/issues/318


Patch:
diff --git a/crates/apollo-parser/src/lexer/mod.rs b/crates/apollo-parser/src/lexer/mod.rs
index 6b36120..c0532c2 100644
--- a/crates/apollo-parser/src/lexer/mod.rs
+++ b/crates/apollo-parser/src/lexer/mod.rs
@@ -144,18 +144,13 @@ impl Cursor<'_> {
                         self.add_err(Error::new("unexpected escaped character", c.to_string()));
                     }
 
+                    buf.push(c);
                     if c == '"' {
-                        buf.push(c);
                         if !was_backslash {
                             break;
                         }
-                    } else if is_escaped_char(c)
-                        || is_source_char(c) && c != '\\' && c != '"' && !is_line_terminator(c)
-                    {
-                        buf.push(c);
-                    // TODO @lrlna: this should error if c == \ or has a line terminator
-                    } else {
-                        break;
+                    } else if is_line_terminator(c) {
+                        self.add_err(Error::new("unexpected line terminator", c.to_string()));
                     }
                     was_backslash = c == '\\';
                 }
@@ -426,12 +421,7 @@ mod test {
 
     #[test]
     fn tests() {
-        let gql_1 = r#"
-        """
-        **Example**: "Saturn5"
-        """
-        name: String @join__field(graph: PRODUCTS)
-        "#;
+        let gql_1 = "\"\nhello";
         let lexer_1 = Lexer::new(gql_1);
         dbg!(lexer_1.tokens);
         dbg!(lexer_1.errors);


Test patch:
diff --git a/crates/apollo-parser/test_data/lexer/err/0006_unterminated_string_value_in_list_value_argument.txt b/crates/apollo-parser/test_data/lexer/err/0006_unterminated_string_value_in_list_value_argument.txt
index ba2513d..b17f32d 100644
--- a/crates/apollo-parser/test_data/lexer/err/0006_unterminated_string_value_in_list_value_argument.txt
+++ b/crates/apollo-parser/test_data/lexer/err/0006_unterminated_string_value_in_list_value_argument.txt
@@ -18,10 +18,12 @@ L_BRACKET@87:88 "["
 STRING_VALUE@88:94 "\"@key\""
 COMMA@94:95 ","
 WHITESPACE@95:96 " "
-R_CURLY@234:235 "}"
+NAME@137:139 "id"
 EOF@235:235
-ERROR@96:234 "unterminated string value" "@external])
-type Vehicle @key(fields: "id") {
+ERROR@96:137 "unexpected line terminator" "@external])
+
+type Vehicle @key(fields: "
+ERROR@139:235 "unterminated string value" ") {
   id: ID!,
   type: String,
   modelCode: String,
diff --git a/crates/apollo-parser/test_data/lexer/err/0007_unterminated_string_value_in_object_value_argument.txt b/crates/apollo-parser/test_data/lexer/err/0007_unterminated_string_value_in_object_value_argument.txt
index fcae1a1..faabf52 100644
--- a/crates/apollo-parser/test_data/lexer/err/0007_unterminated_string_value_in_object_value_argument.txt
+++ b/crates/apollo-parser/test_data/lexer/err/0007_unterminated_string_value_in_object_value_argument.txt
@@ -24,9 +24,11 @@ WHITESPACE@60:61 " "
 NAME@61:66 "value"
 COLON@66:67 ":"
 WHITESPACE@67:68 " "
-R_CURLY@115:116 "}"
+NAME@106:108 "en"
 EOF@116:116
-ERROR@68:115 "unterminated string value" "my store }]  }) {
-    name(locale: "en")
+ERROR@68:106 "unexpected line terminator" "my store }]
+  }) {
+    name(locale: "
+ERROR@108:116 "unterminated string value" ")
   }
 }
diff --git a/crates/apollo-parser/test_data/lexer/err/0008_unterminated_string_value.txt b/crates/apollo-parser/test_data/lexer/err/0008_unterminated_string_value.txt
index a55b1e6..1e201b9 100644
--- a/crates/apollo-parser/test_data/lexer/err/0008_unterminated_string_value.txt
+++ b/crates/apollo-parser/test_data/lexer/err/0008_unterminated_string_value.txt
@@ -11,7 +11,7 @@ L_PAREN@35:36 "("
 NAME@36:42 "locale"
 COLON@42:43 ":"
 WHITESPACE@43:44 " "
-R_CURLY@53:54 "}"
 EOF@54:54
-ERROR@44:53 "unterminated string value" "en)  }
+ERROR@44:54 "unterminated string value" "en)
+  }
 }
diff --git a/crates/apollo-parser/test_data/lexer/err/0009_unterminated_string_value_as_default.txt b/crates/apollo-parser/test_data/lexer/err/0009_unterminated_string_value_as_default.txt
index 1887a49..a80a165 100644
--- a/crates/apollo-parser/test_data/lexer/err/0009_unterminated_string_value_as_default.txt
+++ b/crates/apollo-parser/test_data/lexer/err/0009_unterminated_string_value_as_default.txt
@@ -18,6 +18,6 @@ NAME@44:50 "String"
 WHITESPACE@50:51 " "
 EQ@51:52 "="
 WHITESPACE@52:53 " "
-R_CURLY@111:112 "}"
 EOF@112:112
-ERROR@53:111 "unterminated string value" "https://spec.graphql.org/October2021/#DefaultValue): Url}
+ERROR@53:112 "unterminated string value" "https://spec.graphql.org/October2021/#DefaultValue): Url
+}
diff --git a/crates/apollo-parser/test_data/lexer/err/0010_unterminated_string_value_with_unicode.graphql b/crates/apollo-parser/test_data/lexer/err/0010_unterminated_string_value_with_unicode.graphql
new file mode 100644
index 0000000..8026561
--- /dev/null
+++ b/crates/apollo-parser/test_data/lexer/err/0010_unterminated_string_value_with_unicode.graphql
@@ -0,0 +1,1 @@
+"hello✨
\ No newline at end of file
diff --git a/crates/apollo-parser/test_data/lexer/err/0010_unterminated_string_value_with_unicode.txt b/crates/apollo-parser/test_data/lexer/err/0010_unterminated_string_value_with_unicode.txt
new file mode 100644
index 0000000..1d6df07
--- /dev/null
+++ b/crates/apollo-parser/test_data/lexer/err/0010_unterminated_string_value_with_unicode.txt
@@ -0,0 +1,2 @@
+EOF@9:9
+ERROR@0:9 "unterminated string value" "hello✨
diff --git a/crates/apollo-parser/test_data/lexer/err/0011_unterminated_string_value_with_unicode_and_escaped_characters.graphql b/crates/apollo-parser/test_data/lexer/err/0011_unterminated_string_value_with_unicode_and_escaped_characters.graphql
new file mode 100644
index 0000000..d2aa898
--- /dev/null
+++ b/crates/apollo-parser/test_data/lexer/err/0011_unterminated_string_value_with_unicode_and_escaped_characters.graphql
@@ -0,0 +1,1 @@
+"\n\n\\u{c}\nPSK\\u{1}\\0\\0\\0י
\ No newline at end of file
diff --git a/crates/apollo-parser/test_data/lexer/err/0011_unterminated_string_value_with_unicode_and_escaped_characters.txt b/crates/apollo-parser/test_data/lexer/err/0011_unterminated_string_value_with_unicode_and_escaped_characters.txt
new file mode 100644
index 0000000..9bb4ad5
--- /dev/null
+++ b/crates/apollo-parser/test_data/lexer/err/0011_unterminated_string_value_with_unicode_and_escaped_characters.txt
@@ -0,0 +1,2 @@
+EOF@33:33
+ERROR@0:33 "unterminated string value" "\n\n\\u{c}\nPSK\\u{1}\\0\\0\\0י
diff --git a/crates/apollo-parser/test_data/lexer/err/0012_string_value_with_line_terminators.graphql b/crates/apollo-parser/test_data/lexer/err/0012_string_value_with_line_terminators.graphql
new file mode 100644
index 0000000..1f3521c
--- /dev/null
+++ b/crates/apollo-parser/test_data/lexer/err/0012_string_value_with_line_terminators.graphql
@@ -0,0 +1,3 @@
+"
+hello
+"
\ No newline at end of file
diff --git a/crates/apollo-parser/test_data/lexer/err/0012_string_value_with_line_terminators.txt b/crates/apollo-parser/test_data/lexer/err/0012_string_value_with_line_terminators.txt
new file mode 100644
index 0000000..f9e0f03
--- /dev/null
+++ b/crates/apollo-parser/test_data/lexer/err/0012_string_value_with_line_terminators.txt
@@ -0,0 +1,4 @@
+EOF@9:9
+ERROR@0:9 "unexpected line terminator" "
+hello
+"
diff --git a/crates/apollo-parser/test_data/parser/err/0034_unterminated_string_value_in_list.txt b/crates/apollo-parser/test_data/parser/err/0034_unterminated_string_value_in_list.txt
index 6e29459..8123495 100644
--- a/crates/apollo-parser/test_data/parser/err/0034_unterminated_string_value_in_list.txt
+++ b/crates/apollo-parser/test_data/parser/err/0034_unterminated_string_value_in_list.txt
@@ -1,15 +1,15 @@
-- DOCUMENT@0..97
-    - SCHEMA_EXTENSION@0..97
+- DOCUMENT@0..98
+    - SCHEMA_EXTENSION@0..98
         - extend_KW@0..6 "extend"
         - WHITESPACE@6..7 " "
         - schema_KW@7..13 "schema"
         - WHITESPACE@13..16 "\n  "
-        - DIRECTIVES@16..97
-            - DIRECTIVE@16..97
+        - DIRECTIVES@16..98
+            - DIRECTIVE@16..98
                 - AT@16..17 "@"
                 - NAME@17..21
                     - IDENT@17..21 "link"
-                - ARGUMENTS@21..97
+                - ARGUMENTS@21..98
                     - L_PAREN@21..22 "("
                     - ARGUMENT@22..79
                         - NAME@22..25
@@ -20,21 +20,21 @@
                             - STRING@27..69 "\"https://specs.apollo.dev/federation/v2.0\""
                             - COMMA@69..70 ","
                             - WHITESPACE@70..79 "\n        "
-                    - ARGUMENT@79..97
+                    - ARGUMENT@79..98
                         - NAME@79..85
                             - IDENT@79..85 "import"
                         - COLON@85..86 ":"
                         - WHITESPACE@86..87 " "
-                        - LIST_VALUE@87..97
+                        - LIST_VALUE@87..98
                             - L_BRACK@87..88 "["
-                            - STRING_VALUE@88..97
+                            - STRING_VALUE@88..96
                                 - STRING@88..94 "\"@key\""
                                 - COMMA@94..95 ","
                                 - WHITESPACE@95..96 " "
-                                - WHITESPACE@96..97 "\n"
-- ERROR@96:236 "unterminated string value" "@external])
-
-type Vehicle @key(fields: "id") {
+                            - ENUM_VALUE@96..98
+                                - NAME@96..98
+                                    - IDENT@96..98 "id"
+- ERROR@140:237 "unterminated string value" ") {
   id: ID!,
   type: String,
   modelCode: String,
@@ -42,5 +42,9 @@ type Vehicle @key(fields: "id") {
   launchDate: String
 }
 
+- ERROR@96:138 "unexpected line terminator" "@external])
+
+
+type Vehicle @key(fields: "
 - ERROR@237:237 "expected R_PAREN, got EOF" EOF
 recursion limit: 4096, high: 0
\ No newline at end of file
diff --git a/crates/apollo-parser/test_data/parser/err/0035_unterminated_string_value_in_object_value.txt b/crates/apollo-parser/test_data/parser/err/0035_unterminated_string_value_in_object_value.txt
index 6d676d8..1422631 100644
--- a/crates/apollo-parser/test_data/parser/err/0035_unterminated_string_value_in_object_value.txt
+++ b/crates/apollo-parser/test_data/parser/err/0035_unterminated_string_value_in_object_value.txt
@@ -1,32 +1,32 @@
-- DOCUMENT@0..68
-    - OPERATION_DEFINITION@0..68
+- DOCUMENT@0..70
+    - OPERATION_DEFINITION@0..70
         - OPERATION_TYPE@0..9
             - mutation_KW@0..8 "mutation"
             - WHITESPACE@8..9 " "
-        - SELECTION_SET@9..68
+        - SELECTION_SET@9..70
             - L_CURLY@9..10 "{"
             - WHITESPACE@10..13 "\n  "
-            - FIELD@13..68
+            - FIELD@13..70
                 - NAME@13..24
                     - IDENT@13..24 "createStore"
-                - ARGUMENTS@24..68
+                - ARGUMENTS@24..70
                     - L_PAREN@24..25 "("
-                    - ARGUMENT@25..68
+                    - ARGUMENT@25..70
                         - NAME@25..30
                             - IDENT@25..30 "draft"
                         - COLON@30..31 ":"
                         - WHITESPACE@31..32 " "
-                        - OBJECT_VALUE@32..68
+                        - OBJECT_VALUE@32..70
                             - L_CURLY@32..33 "{"
                             - WHITESPACE@33..38 "\n    "
-                            - OBJECT_FIELD@38..68
+                            - OBJECT_FIELD@38..70
                                 - NAME@38..42
                                     - IDENT@38..42 "name"
                                 - COLON@42..43 ":"
                                 - WHITESPACE@43..44 " "
-                                - LIST_VALUE@44..68
+                                - LIST_VALUE@44..70
                                     - L_BRACK@44..45 "["
-                                    - OBJECT_VALUE@45..68
+                                    - OBJECT_VALUE@45..70
                                         - L_CURLY@45..46 "{"
                                         - WHITESPACE@46..47 " "
                                         - OBJECT_FIELD@47..61
@@ -38,16 +38,20 @@
                                                 - STRING@55..59 "\"en\""
                                                 - COMMA@59..60 ","
                                                 - WHITESPACE@60..61 " "
-                                        - OBJECT_FIELD@61..68
+                                        - OBJECT_FIELD@61..70
                                             - NAME@61..66
                                                 - IDENT@61..66 "value"
                                             - COLON@66..67 ":"
                                             - WHITESPACE@67..68 " "
-- ERROR@68:115 "unterminated string value" "my store }]  }) {
-    name(locale: "en")
+                                            - ENUM_VALUE@68..70
+                                                - NAME@68..70
+                                                    - IDENT@68..70 "en"
+- ERROR@108:116 "unterminated string value" ")
   }
 }
-- ERROR@115:116 "expected a valid Value" }
+- ERROR@68:106 "unexpected line terminator" "my store }]
+  }) {
+    name(locale: "
 - ERROR@116:116 "expected }" EOF
 - ERROR@116:116 "expected }" EOF
 - ERROR@116:116 "expected R_PAREN, got EOF" EOF
diff --git a/crates/apollo-parser/test_data/parser/err/0036_unterminated_string_in_default_value.txt b/crates/apollo-parser/test_data/parser/err/0036_unterminated_string_in_default_value.txt
index 473909a..2d121af 100644
--- a/crates/apollo-parser/test_data/parser/err/0036_unterminated_string_in_default_value.txt
+++ b/crates/apollo-parser/test_data/parser/err/0036_unterminated_string_in_default_value.txt
@@ -1,11 +1,11 @@
-- DOCUMENT@0..54
-    - OBJECT_TYPE_DEFINITION@0..54
+- DOCUMENT@0..53
+    - OBJECT_TYPE_DEFINITION@0..53
         - type_KW@0..4 "type"
         - WHITESPACE@4..5 " "
         - NAME@5..12
             - IDENT@5..11 "Person"
             - WHITESPACE@11..12 " "
-        - FIELDS_DEFINITION@12..54
+        - FIELDS_DEFINITION@12..53
             - L_CURLY@12..13 "{"
             - WHITESPACE@13..16 "\n  "
             - FIELD_DEFINITION@16..31
@@ -34,9 +34,10 @@
                         - DEFAULT_VALUE@51..53
                             - EQ@51..52 "="
                             - WHITESPACE@52..53 " "
-            - R_CURLY@53..54 "}"
-- ERROR@53:111 "unterminated string value" "https://spec.graphql.org/October2021/#DefaultValue): Url}
-- ERROR@111:112 "expected a valid Value" }
-- ERROR@111:112 "expected R_PAREN, got }" }
-- ERROR@111:112 "expected a type" }
+- ERROR@53:112 "unterminated string value" "https://spec.graphql.org/October2021/#DefaultValue): Url
+}
+- ERROR@112:112 "expected a valid Value" EOF
+- ERROR@112:112 "expected R_PAREN, got EOF" EOF
+- ERROR@112:112 "expected a type" EOF
+- ERROR@112:112 "expected R_CURLY, got EOF" EOF
 recursion limit: 4096, high: 0
\ No newline at end of file