Repository: tweag/nickel
Pull request #816 (2022-09-04 00:11:02)

flake.lock: Update
Automated changes by the [update-flake-lock](https://github.com/DeterminateSystems/update-flake-lock) GitHub Action.

```
Flake lock file updates:

• Updated input 'nixpkgs':
    'github:NixOS/nixpkgs/f3d0897be466aa09a37f6bf59e62c360c3f9a6cc' (2022-08-25)
  → 'github:NixOS/nixpkgs/2da64a81275b68fdad38af669afeda43d401e94b' (2022-09-01)
• Updated input 'rust-overlay':
    'github:oxalica/rust-overlay/790c16639c7d8da8b36d0aa6a7c1f589af8c5f68' (2022-08-27)
  → 'github:oxalica/rust-overlay/8ac6d40380dc4ec86f1ff591d5c14c8ae1d77a18' (2022-09-03)
```

### Running GitHub Actions on this PR

GitHub Actions will not run workflows on pull requests which are opened by a GitHub Action.

To run GitHub Actions workflows on this PR, run:

```sh
git branch -D update_flake_lock_action
git fetch origin
git checkout update_flake_lock_action
git commit --amend --no-edit
git push origin update_flake_lock_action --force
```


Patch:
diff --git a/benches/stdlib.rs b/benches/stdlib.rs
index 62f0739a..21ed10a0 100644
--- a/benches/stdlib.rs
+++ b/benches/stdlib.rs
@@ -1,10 +1,10 @@
 use criterion::{criterion_group, criterion_main, Criterion};
 use pprof::criterion::{Output, PProfProfiler};
 
-use nickel_lang::cache::{Cache, ErrorTolerance};
+use nickel_lang::cache::Cache;
 
 pub fn typecheck_stdlib(c: &mut Criterion) {
-    let mut cache = Cache::new(ErrorTolerance::Strict);
+    let mut cache = Cache::new();
     cache.load_stdlib().unwrap();
     let type_env = cache.mk_type_env().unwrap();
     c.bench_function("typecheck stdlib", |b| {
diff --git a/flake.lock b/flake.lock
index 7f218289..a122e89b 100644
--- a/flake.lock
+++ b/flake.lock
@@ -32,11 +32,11 @@
     },
     "nixpkgs": {
       "locked": {
-        "lastModified": 1661450036,
-        "narHash": "sha256-0/9UyJLtfWqF4uvOrjFIzk8ue1YYUHa6JIhV0mALkH0=",
+        "lastModified": 1662019588,
+        "narHash": "sha256-oPEjHKGGVbBXqwwL+UjsveJzghWiWV0n9ogo1X6l4cw=",
         "owner": "NixOS",
         "repo": "nixpkgs",
-        "rev": "f3d0897be466aa09a37f6bf59e62c360c3f9a6cc",
+        "rev": "2da64a81275b68fdad38af669afeda43d401e94b",
         "type": "github"
       },
       "original": {
@@ -87,11 +87,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1661568992,
-        "narHash": "sha256-BKoSLBBI7tXHyulp9AnB1SINU2+zzSZEt6mdhkR5uvw=",
+        "lastModified": 1662173844,
+        "narHash": "sha256-+ZgW98Y8fZkgFSylE+Mzalumw+kw3SVivZznbJqQaj8=",
         "owner": "oxalica",
         "repo": "rust-overlay",
-        "rev": "790c16639c7d8da8b36d0aa6a7c1f589af8c5f68",
+        "rev": "8ac6d40380dc4ec86f1ff591d5c14c8ae1d77a18",
         "type": "github"
       },
       "original": {
diff --git a/lsp/nls/src/files.rs b/lsp/nls/src/files.rs
index ce692bc7..e9ed6693 100644
--- a/lsp/nls/src/files.rs
+++ b/lsp/nls/src/files.rs
@@ -82,7 +82,7 @@ fn typecheck(server: &mut Server, file_id: FileId) -> Result<CacheOp<()>, Vec<Di
 fn parse_and_typecheck(server: &mut Server, uri: Url, file_id: FileId) -> Result<()> {
     let diagnostics = server
         .cache
-        .parse(file_id)
+        .parse_lax(file_id)
         .map_err(|parse_err| parse_err.to_diagnostic(server.cache.files_mut(), None))
         .map(|parse_errs| {
             // Parse errors are not fatal
diff --git a/lsp/nls/src/server.rs b/lsp/nls/src/server.rs
index 358e91ec..f3de4d0b 100644
--- a/lsp/nls/src/server.rs
+++ b/lsp/nls/src/server.rs
@@ -16,7 +16,7 @@ use lsp_types::{
     TextDocumentSyncOptions, WorkDoneProgressOptions,
 };
 
-use nickel_lang::cache::{Cache, ErrorTolerance};
+use nickel_lang::cache::Cache;
 use nickel_lang::typecheck::Environment;
 
 use crate::{
@@ -59,7 +59,7 @@ impl Server {
     }
 
     pub fn new(connection: Connection) -> Server {
-        let mut cache = Cache::new(ErrorTolerance::Tolerant);
+        let mut cache = Cache::new();
         cache.load_stdlib().unwrap();
         let initial_env = cache.mk_type_env().unwrap();
         let lin_cache = HashMap::new();
diff --git a/src/cache.rs b/src/cache.rs
index 712de9eb..20d6fbe2 100644
--- a/src/cache.rs
+++ b/src/cache.rs
@@ -70,22 +70,12 @@ pub struct Cache {
     stdlib_ids: Option<Vec<FileId>>,
     /// The inferred type of wildcards for each `FileId`.
     wildcards: HashMap<FileId, Wildcards>,
-    /// Whether processing should try to continue even in case of errors. Needed by the NLS.
-    error_tolerance: ErrorTolerance,
 
     #[cfg(debug_assertions)]
     /// Skip loading the stdlib, used for debugging purpose
     pub skip_stdlib: bool,
 }
 
-/// The error tolerance mode used by the parser. The NLS needs to try to
-/// continue even in case of errors.
-#[derive(Debug, Clone)]
-pub enum ErrorTolerance {
-    Tolerant,
-    Strict,
-}
-
 /// wrapping eval environment with typing environment
 #[derive(Debug, Clone)]
 pub struct Envs {
@@ -212,7 +202,7 @@ pub enum ResolvedTerm {
 }
 
 impl Cache {
-    pub fn new(error_tolerance: ErrorTolerance) -> Self {
+    pub fn new() -> Self {
         Cache {
             files: Files::new(),
             file_ids: HashMap::new(),
@@ -220,7 +210,6 @@ impl Cache {
             wildcards: HashMap::new(),
             imports: HashMap::new(),
             stdlib_ids: None,
-            error_tolerance,
 
             #[cfg(debug_assertions)]
             skip_stdlib: false,
@@ -354,7 +343,7 @@ impl Cache {
     /// corresponding error messages are collected and returned.
     ///
     /// The `Err` part of the result corresponds to non-recoverable errors.
-    fn parse_lax(&mut self, file_id: FileId) -> Result<CacheOp<ParseErrors>, ParseError> {
+    pub fn parse_lax(&mut self, file_id: FileId) -> Result<CacheOp<ParseErrors>, ParseError> {
         if let Some(CachedTerm { parse_errs, .. }) = self.terms.get(&file_id) {
             Ok(CacheOp::Cached(parse_errs.clone()))
         } else {
@@ -367,31 +356,24 @@ impl Cache {
                     parse_errs: parse_errs.clone(),
                 },
             );
-
             Ok(CacheOp::Done(parse_errs))
         }
     }
 
-    /// Parse a source and populate the corresponding entry in the cache, or do
-    /// nothing if the entry has already been parsed. This function is error
-    /// tolerant if self.error_tolerant = true.
-    pub fn parse(&mut self, file_id: FileId) -> Result<CacheOp<ParseErrors>, ParseErrors> {
-        let result = self.parse_lax(file_id);
-
-        match self.error_tolerance {
-            ErrorTolerance::Tolerant => result.map_err(|err| err.into()),
-            ErrorTolerance::Strict => match result? {
-                CacheOp::Done(e) | CacheOp::Cached(e) if !e.no_errors() => Err(e),
-                CacheOp::Done(_) => Ok(CacheOp::Done(ParseErrors::none())),
-                CacheOp::Cached(_) => Ok(CacheOp::Cached(ParseErrors::none())),
-            },
+    /// Parse a source and populate the corresponding entry in the cache, or do nothing if the
+    /// entry has already been parsed. This function is not error tolerant and returns `Err` if at
+    /// least one parse error has been encountered.
+    pub fn parse(&mut self, file_id: FileId) -> Result<CacheOp<()>, ParseErrors> {
+        match self.parse_lax(file_id)? {
+            CacheOp::Done(e) | CacheOp::Cached(e) if !e.no_errors() => Err(e),
+            CacheOp::Done(_) => Ok(CacheOp::Done(())),
+            CacheOp::Cached(_) => Ok(CacheOp::Cached(())),
         }
     }
 
-    /// Parse a source and populate the corresponding entry in the cache, or do
-    /// nothing if the entry has already been parsed. Support multiple formats.
-    /// This function is error tolerant.
-    fn parse_multi_lax(
+    /// Parse a source and populate the corresponding entry in the cache, or do nothing if the
+    /// entry has already been parsed. Support multiple formats.
+    pub fn parse_multi(
         &mut self,
         file_id: FileId,
         format: InputFormat,
@@ -412,26 +394,6 @@ impl Cache {
         }
     }
 
-    /// Parse a source and populate the corresponding entry in the cache, or do
-    /// nothing if the entry has already been parsed. Support multiple formats.
-    /// This function is error tolerant if self.error_tolerant = true.
-    pub fn parse_multi(
-        &mut self,
-        file_id: FileId,
-        format: InputFormat,
-    ) -> Result<CacheOp<ParseErrors>, ParseErrors> {
-        let result = self.parse_multi_lax(file_id, format);
-
-        match self.error_tolerance {
-            ErrorTolerance::Tolerant => result.map_err(|err| err.into()),
-            ErrorTolerance::Strict => match result? {
-                CacheOp::Done(e) | CacheOp::Cached(e) if !e.no_errors() => Err(e),
-                CacheOp::Done(_) => Ok(CacheOp::Done(ParseErrors::none())),
-                CacheOp::Cached(_) => Ok(CacheOp::Cached(ParseErrors::none())),
-            },
-        }
-    }
-
     /// Parse a source without querying nor populating the cache.
     pub fn parse_nocache(&self, file_id: FileId) -> Result<(RichTerm, ParseErrors), ParseError> {
         self.parse_nocache_multi(file_id, InputFormat::Nickel)
@@ -448,7 +410,6 @@ impl Cache {
         match format {
             InputFormat::Nickel => {
                 let (t, parse_errs) =
-                    // TODO: Should this really be parse_term if self.error_tolerant = false?
                     parser::grammar::TermParser::new().parse_term_lax(file_id, Lexer::new(buf))?;
 
                 Ok((t, parse_errs))
@@ -681,7 +642,7 @@ impl Cache {
     ) -> Result<CacheOp<()>, Error> {
         let mut result = CacheOp::Cached(());
 
-        if let CacheOp::Done(_) = self.parse(file_id)? {
+        if self.parse(file_id)? == CacheOp::Done(()) {
             result = CacheOp::Done(());
         }
 
@@ -1025,6 +986,7 @@ impl ImportResolver for Cache {
             }
         };
 
+        // We ignore non fatal parse errors while importing.
         self.parse_multi(file_id, format)
             .map_err(|err| ImportError::ParseErrors(err.into(), *pos))?;
 
diff --git a/src/program.rs b/src/program.rs
index 30a369ec..21b9c1f2 100644
--- a/src/program.rs
+++ b/src/program.rs
@@ -50,7 +50,7 @@ impl Program {
     }
 
     pub fn new_from_file(path: impl Into<OsString>) -> std::io::Result<Program> {
-        let mut cache = Cache::new(ErrorTolerance::Strict);
+        let mut cache = Cache::new();
         let main_id = cache.add_file(path)?;
 
         Ok(Program { main_id, cache })
@@ -62,7 +62,7 @@ impl Program {
         T: Read,
         S: Into<OsString> + Clone,
     {
-        let mut cache = Cache::new(ErrorTolerance::Strict);
+        let mut cache = Cache::new();
         let main_id = cache.add_source(source_name, source)?;
 
         Ok(Program { main_id, cache })
diff --git a/src/repl/mod.rs b/src/repl/mod.rs
index c7f0af36..6ba6c85d 100644
--- a/src/repl/mod.rs
+++ b/src/repl/mod.rs
@@ -6,7 +6,7 @@
 //! Dually, the frontend is the user-facing part, which may be a CLI, a web application, a
 //! jupyter-kernel (which is not exactly user-facing, but still manages input/output and
 //! formatting), etc.
-use crate::cache::{Cache, Envs, ErrorTolerance};
+use crate::cache::{Cache, Envs};
 use crate::error::{Error, EvalError, IOError, ParseError, ParseErrors, ReplError};
 use crate::identifier::Ident;
 use crate::parser::{grammar, lexer, ExtendedTerm};
@@ -83,7 +83,7 @@ impl ReplImpl {
     /// Create a new empty REPL.
     pub fn new() -> Self {
         ReplImpl {
-            cache: Cache::new(ErrorTolerance::Strict),
+            cache: Cache::new(),
             parser: grammar::ExtendedTermParser::new(),
             env: Envs::new(),
             initial_type_env: typecheck::Environment::new(),
diff --git a/utilities/src/lib.rs b/utilities/src/lib.rs
index 4daac592..2aff59bd 100644
--- a/utilities/src/lib.rs
+++ b/utilities/src/lib.rs
@@ -2,7 +2,7 @@
 use codespan::Files;
 use criterion::Criterion;
 use nickel_lang::{
-    cache::{Cache, Envs, ErrorTolerance},
+    cache::Cache,
     error::{Error, ParseError},
     eval,
     parser::{grammar, lexer},
@@ -117,7 +117,9 @@ impl<'b> Bench<'b> {
 }
 
 pub fn bench_terms<'r>(rts: Vec<Bench<'r>>) -> Box<dyn Fn(&mut Criterion) + 'r> {
-    let mut cache = Cache::new(ErrorTolerance::Strict);
+    use nickel_lang::cache::Envs;
+
+    let mut cache = Cache::new();
     let Envs { eval_env, type_env } = cache.prepare_stdlib().unwrap();
     Box::new(move |c: &mut Criterion| {
         rts.iter().for_each(|bench| {


Test patch:
diff --git a/tests/imports.rs b/tests/imports.rs
index 6d5d7618..b208da39 100644
--- a/tests/imports.rs
+++ b/tests/imports.rs
@@ -1,5 +1,5 @@
 use assert_matches::assert_matches;
-use nickel_lang::error::{Error, EvalError, ImportError, TypecheckError};
+use nickel_lang::error::{Error, EvalError, ParseError, TypecheckError};
 use nickel_lang::program::Program;
 use nickel_lang::term::Term;
 use std::io::BufReader;
@@ -122,7 +122,9 @@ fn import_unexpected_token_fail() {
     .unwrap();
     assert_matches!(
         prog.eval(),
-        Err(Error::ImportError(ImportError::ParseErrors(..)))
+        Err(Error::EvalError(EvalError::ParseError(
+            ParseError::UnexpectedToken(..)
+        )))
     );
 }
 
@@ -141,25 +143,8 @@ fn import_unexpected_token_in_record_fail() {
     .unwrap();
     assert_matches!(
         prog.eval(),
-        Err(Error::ImportError(ImportError::ParseErrors(..)))
-    );
-}
-
-#[test]
-fn import_unexpected_token_buried_fail() {
-    let mut prog = Program::new_from_source(
-        BufReader::new(
-            format!(
-                "let _ign = {} in 1",
-                mk_import("unexpected_token_buried.ncl")
-            )
-            .as_bytes(),
-        ),
-        "should_fail",
-    )
-    .unwrap();
-    assert_matches!(
-        prog.eval(),
-        Err(Error::ImportError(ImportError::ParseErrors(..)))
+        Err(Error::EvalError(EvalError::ParseError(
+            ParseError::UnexpectedToken(..)
+        )))
     );
 }
diff --git a/tests/imports/unexpected_token_buried.ncl b/tests/imports/unexpected_token_buried.ncl
deleted file mode 100644
index e589efda..00000000
--- a/tests/imports/unexpected_token_buried.ncl
+++ /dev/null
@@ -1,6 +0,0 @@
-{ 
-  foo = {
-    bar = [2 @ ],
-  }, 
-  baz = 2 
-}
\ No newline at end of file
diff --git a/tests/parse_fail.rs b/tests/parse_fail.rs
index 6539309a..b65ee8b3 100644
--- a/tests/parse_fail.rs
+++ b/tests/parse_fail.rs
@@ -12,11 +12,3 @@ fn unexpected_token() {
 fn unexpected_token_in_record() {
     assert_matches!(eval("{ name = \"Nickel\",, }"), Err(Error::ParseErrors(..)));
 }
-
-#[test]
-fn unexpected_token_buried() {
-    assert_matches!(
-        eval("{ foo = {bar = [2 @ ],}, baz = 2 }"),
-        Err(Error::ParseErrors(..))
-    );
-}
diff --git a/tests/stdlib_typecheck.rs b/tests/stdlib_typecheck.rs
index 3c0e9a75..2ed9894e 100644
--- a/tests/stdlib_typecheck.rs
+++ b/tests/stdlib_typecheck.rs
@@ -1,9 +1,9 @@
 use assert_matches::assert_matches;
-use nickel_lang::cache::{Cache, ErrorTolerance};
+use nickel_lang::cache::Cache;
 
 #[test]
 fn stdlib_typecheck() {
-    let mut cache = Cache::new(ErrorTolerance::Strict);
+    let mut cache = Cache::new();
     assert_matches!(cache.load_stdlib(), Ok(_));
     assert_matches!(cache.typecheck_stdlib(), Ok(_));
 }