Repository: microsoft/qdk (Rust)

Issue:
Two errors at EOF when parser error recovery occurs
Some syntax errors cause two error messages to be emitted with the same location (EOF). The messages seem to contradict each other because they mention expecting different tokens.

Code:

[Playground link](https://microsoft.github.io/qsharp/?code=H4sIAAAAAAAACstLzE0tLkhMTlXwrQwsTcwrKc11LChQqOZSAIL8gtQ8Bd%2FM5KL84vy0Ej2ovJ5LZmJ6Xn5xSWZysTUXWKGDa15JUWVAfmZeiYYmTGtRYklmPlB%2FYmaehqaClUJQanFpTkl0rAIALT3ts3QAAAA%3D)

```qsharp
namespace MyQuantumApp {
    open Microsoft.Quantum.Diagnostics;

    @EntryPoint()
    operation Main() : Result[] 
```

Produces errors:

```
main.qs@(5,33): syntax error: expected `{`, found EOF
main.qs@(5,33): syntax error: expected `}`, found EOF
```

**Expected behavior**

A single syntax error

`git bisect` narrowed this down to #380.






Discussion:

Patch:
diff --git a/compiler/qsc_parse/src/scan.rs b/compiler/qsc_parse/src/scan.rs
index 2e848adacc..77da3c9f57 100644
--- a/compiler/qsc_parse/src/scan.rs
+++ b/compiler/qsc_parse/src/scan.rs
@@ -20,6 +20,7 @@ pub(super) struct Scanner<'a> {
     tokens: Lexer<'a>,
     barriers: Vec<&'a [TokenKind]>,
     errors: Vec<Error>,
+    recovered_eof: bool,
     peek: Token,
     offset: u32,
 }
@@ -36,6 +37,7 @@ impl<'a> Scanner<'a> {
                 .into_iter()
                 .map(|e| Error(ErrorKind::Lex(e)))
                 .collect(),
+            recovered_eof: false,
             peek: peek.unwrap_or_else(|| eof(input.len())),
             offset: 0,
         }
@@ -98,7 +100,14 @@ impl<'a> Scanner<'a> {
     }
 
     pub(super) fn push_error(&mut self, error: Error) {
-        self.errors.push(error);
+        let is_eof_err = matches!(
+            error.0,
+            ErrorKind::Token(_, TokenKind::Eof, _) | ErrorKind::Rule(_, TokenKind::Eof, _)
+        );
+        if !is_eof_err || !self.recovered_eof {
+            self.errors.push(error);
+            self.recovered_eof = self.recovered_eof || is_eof_err;
+        }
     }
 
     pub(super) fn into_errors(self) -> Vec<Error> {


Test patch:
diff --git a/compiler/qsc_frontend/src/compile/tests.rs b/compiler/qsc_frontend/src/compile/tests.rs
index 8a142e8539..d89844ef5b 100644
--- a/compiler/qsc_frontend/src/compile/tests.rs
+++ b/compiler/qsc_frontend/src/compile/tests.rs
@@ -707,4 +707,12 @@ fn two_files_error_eof() {
         .collect();
 
     assert_eq!(vec![("test1", Span { lo: 15, hi: 15 }),], errors);
+
+    expect![[r#"
+        Package:
+            Item 0 [0-15] (Public):
+                Namespace (Ident 0 [10-13] "Foo"): <empty>
+            Item 1 [16-32] (Public):
+                Namespace (Ident 1 [26-29] "Bar"): <empty>"#]]
+    .assert_eq(&unit.package.to_string());
 }
diff --git a/compiler/qsc_parse/src/item/tests.rs b/compiler/qsc_parse/src/item/tests.rs
index 18f016b0dc..00ba32d11e 100644
--- a/compiler/qsc_parse/src/item/tests.rs
+++ b/compiler/qsc_parse/src/item/tests.rs
@@ -1037,18 +1037,6 @@ fn recover_unclosed_callable_item() {
                         },
                     ),
                 ),
-                Error(
-                    Token(
-                        Close(
-                            Brace,
-                        ),
-                        Eof,
-                        Span {
-                            lo: 48,
-                            hi: 48,
-                        },
-                    ),
-                ),
             ]"#]],
     );
 }