diff --git a/src/builtins/attrs.rs b/src/builtins/attrs.rs index b6ea2c99..5e3a04ca 100644 --- a/src/builtins/attrs.rs +++ b/src/builtins/attrs.rs @@ -172,7 +172,7 @@ fn list_to_attrs(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult = Vec::with_capacity(l.len as usize); let mut seen: FxHashSet = FxHashSet::default(); for c in l.cells() { @@ -223,7 +223,7 @@ fn zip_attrs_with(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult> = FxHashMap::default(); let mut order: Vec = Vec::new(); for c in l.cells() { @@ -265,7 +265,7 @@ fn cat_attrs(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult { pos, "while evaluating the second argument passed to builtins.catAttrs", )?; - parallel::fanout_list(ev, l); // parallel + parallel::fanout_list(ev, l); let mut out = Vec::new(); for c in l.cells() { let w = c.get(); @@ -292,7 +292,7 @@ fn group_by(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult { pos, "while evaluating the second argument passed to builtins.groupBy", )?; - parallel::fanout_list(ev, l); // parallel + parallel::fanout_list(ev, l); let mut groups: FxHashMap> = FxHashMap::default(); for c in l.cells() { let elem = c.get(); diff --git a/src/builtins/derivation.rs b/src/builtins/derivation.rs index 1914c502..8995def7 100644 --- a/src/builtins/derivation.rs +++ b/src/builtins/derivation.rs @@ -44,10 +44,10 @@ pub struct DrvInfo { /// The ATerm this evaluation produced; `None` when the drv was read from `/nix/store`. pub aterm: Option>>, /// Where the derivation was defined. `derivation` is a wrapper around - /// `derivationStrict`, so the primop's call position is inside - /// `«derivation-internal»` for every normal call; the position of the - /// `name` attribute's value stands in, which is the same position Nix - /// names in "whose name attribute is located at". A direct + /// `derivationStrict`, so for every normal call the position of the + /// primop call is inside `«derivation-internal»`. The position of the + /// value of the `name` attribute is used instead. Nix names the same + /// position in "whose name attribute is located at". A direct /// `builtins.derivationStrict` call keeps its own call position. pub pos: Option, /// The attribute path a cli walk reached this derivation by. @@ -160,7 +160,7 @@ fn derivation_strict(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult< pos, "while evaluating the argument passed to builtins.derivationStrict", )?; - parallel::fanout_attrs_coerce(ev, attrs); // parallel + parallel::fanout_attrs_coerce(ev, attrs); let Some(name_attr) = attrs.get(ev.s.name) else { let mut e = Error::new( ErrorKind::MissingAttr, diff --git a/src/builtins/fetch.rs b/src/builtins/fetch.rs index c90d541b..4677dcb1 100644 --- a/src/builtins/fetch.rs +++ b/src/builtins/fetch.rs @@ -1,8 +1,8 @@ -//! `builtins.fetchTree` for locked inputs (fetchTree.cc, fetchers.cc): the -//! attrs the evaluator passes become a `source::SourceRef`, which resolves -//! to a tree, and the result attrset mirrors `emitTreeAttrs`. A `path` input -//! is a virtual copy of the local directory, like the root tree; everything -//! else is locked, and `crate::source` fetches it. +//! `builtins.fetchTree` for locked inputs (fetchTree.cc, fetchers.cc). The +//! attrs from the evaluator become a `source::SourceRef`, which resolves to +//! a tree. The result attrset mirrors `emitTreeAttrs`. A `path` input is a +//! virtual copy of the local directory, like the root tree. Every other +//! input is locked, and `crate::source` fetches it. use std::path::Path; diff --git a/src/builtins/lists.rs b/src/builtins/lists.rs index 0b2a05e8..db60b737 100644 --- a/src/builtins/lists.rs +++ b/src/builtins/lists.rs @@ -72,7 +72,7 @@ fn elem(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult { pos, "while evaluating the second argument passed to builtins.elem", )?; - parallel::fanout_list(ev, l); // parallel + parallel::fanout_list(ev, l); for c in l.cells() { let x = ev.force(args[0])?; let v = c.get(); @@ -135,7 +135,7 @@ fn filter(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult { pos, "while evaluating the second argument passed to builtins.filter", )?; - parallel::fanout_list(ev, l); // parallel + parallel::fanout_list(ev, l); if l.len == 0 { return Ok(Word::from_ref(l)); } @@ -176,7 +176,7 @@ fn foldl_strict(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult pos, "while evaluating the third argument passed to builtins.foldlStrict", )?; - parallel::fanout_list(ev, l); // parallel + parallel::fanout_list(ev, l); let mut acc = args[1]; for c in l.cells() { acc = ev.call2(f, acc, c.get(), pos)?; @@ -190,7 +190,7 @@ fn concat_lists(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult pos, "while evaluating the first argument passed to builtins.concatLists", )?; - parallel::fanout_list(ev, l); // parallel + parallel::fanout_list(ev, l); let mut lists = Vec::with_capacity(l.len as usize); for c in l.cells() { let v = c.get(); @@ -214,7 +214,7 @@ fn concat_map(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult { pos, "while evaluating the second argument passed to builtins.concatMap", )?; - parallel::fanout_list(ev, l); // parallel + parallel::fanout_list(ev, l); let mut lists = Vec::with_capacity(l.len as usize); for c in l.cells() { let r = ev.call1(f, c.get(), pos)?; @@ -263,7 +263,7 @@ fn any(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult { pos, "while evaluating the second argument passed to builtins.any", )?; - parallel::fanout_list(ev, l); // parallel + parallel::fanout_list(ev, l); for c in l.cells() { let r = ev.call1(f, c.get(), pos)?; if ev.force_bool( @@ -288,7 +288,7 @@ fn all(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult { pos, "while evaluating the second argument passed to builtins.all", )?; - parallel::fanout_list(ev, l); // parallel + parallel::fanout_list(ev, l); for c in l.cells() { let r = ev.call1(f, c.get(), pos)?; if !ev.force_bool( @@ -312,7 +312,7 @@ fn sort(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult { pos, "while evaluating the second argument passed to builtins.sort", )?; - parallel::fanout_list(ev, l); // parallel + parallel::fanout_list(ev, l); if l.len == 0 { return Ok(Word::from_ref(l)); } @@ -525,7 +525,7 @@ fn partition(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult { pos, "while evaluating the second argument passed to builtins.partition", )?; - parallel::fanout_list(ev, l); // parallel + parallel::fanout_list(ev, l); let mut right = Vec::new(); let mut wrong = Vec::new(); for c in l.cells() { diff --git a/src/builtins/paths.rs b/src/builtins/paths.rs index e6351610..c806c3f7 100644 --- a/src/builtins/paths.rs +++ b/src/builtins/paths.rs @@ -1,9 +1,9 @@ //! File system primops (primops.cc): readFile, readDir, readFileType, //! pathExists, toPath, storePath, filterSource, path, toFile. The network -//! fetchers and flake primops are registered so `builtins ? fetchMercurial` -//! holds, but they fail when called; only `fetchurl`, `fetchTree` on a +//! fetchers and flake primops are registered, so `builtins ? fetchMercurial` +//! holds, but a call fails. Only these work: `fetchurl`, `fetchTree` on a //! locked input, `fetchTarball` with a `sha256`, `fetchGit`, `getFlake`, -//! `parseFlakeRef` and `flakeRefToString` work (`flake`). +//! `parseFlakeRef` and `flakeRefToString` (`flake`). use crate::sys::errno; use crate::sys::{self, OsStrExt}; diff --git a/src/builtins/store_hooks.rs b/src/builtins/store_hooks.rs index 7b8e6e2f..4037802b 100644 --- a/src/builtins/store_hooks.rs +++ b/src/builtins/store_hooks.rs @@ -7,12 +7,18 @@ //! evaluator reads for a store path goes through [`locate`]: `import`, //! `readFile`, `readDir`, `pathExists`, `readFileType`, `hashFile`, //! `findFile`, `storePath`, `fetchTree`, `builtins.path`, `filterSource` and -//! string coercion, and a read of a file through [`open`], which applies -//! pure evaluation's allow list first. The answer is a [`SrcPath`]: the real -//! file system, a local tree a virtual copy stands for, a member of a fetched -//! flake input served out of its spool by offset (`crate::treestore`), or a -//! `toFile` text or `.drv` this evaluation holds in memory. Its readers below -//! record what they saw in the tracing cache's read set. +//! string coercion. A read of a file goes through [`open`], which applies +//! the allow list of pure evaluation first. The answer is a [`SrcPath`], +//! one of: +//! +//! - the real file system; +//! - a local tree that a virtual copy stands for; +//! - a member of a fetched flake input, served out of its spool by offset +//! (`crate::treestore`); +//! - a `toFile` text or a `.drv` that this evaluation holds in memory. +//! +//! The readers below record what they saw in the read set of the tracing +//! cache. use std::ffi::OsStr; use std::fs; diff --git a/src/builtins/strings.rs b/src/builtins/strings.rs index d7c91ac5..760f0a66 100644 --- a/src/builtins/strings.rs +++ b/src/builtins/strings.rs @@ -92,7 +92,7 @@ fn concat_strings_sep(ev: &mut Evaluator, args: &[Word], pos: Pos) -> EvalResult pos, "while evaluating the second argument (the list of strings to concat) passed to builtins.concatStringsSep", )?; - parallel::fanout_list_coerce(ev, l); // parallel + parallel::fanout_list_coerce(ev, l); let mut ctx: Vec = sep.ctx.to_vec(); let mut out: Vec = Vec::new(); for (i, c) in l.cells().iter().enumerate() { diff --git a/src/compile/cache.rs b/src/compile/cache.rs index b513ce52..79b8b049 100644 --- a/src/compile/cache.rs +++ b/src/compile/cache.rs @@ -1,15 +1,19 @@ -//! Compiled-IR cache on disk. A file's IR is written after its first -//! compile and loaded instead of parsing and compiling on later runs +//! Compiled-IR cache on disk. The first compile of a file writes its IR. +//! Later runs load it and do not parse or compile the file again //! (docs/explanation/caching.md, "The IR cache: the same file, compiled once"). //! -//! The IR is process-specific in three ways, and the entry undoes each: -//! `Sym` ids (a per-entry name table is re-interned on load), `Pos` values -//! (stored relative to the file, rebased on load), and constant `Word`s -//! (literals are stored by content, globals and `builtins.x` by name and -//! looked up in the live tables, so `__nixPath` follows the run). Binding -//! frames (`let`, `rec`, formals) are slot-ordered by `Sym` id, so the -//! loader re-sorts them and rewrites every `Local` index through a -//! permutation stack that mirrors the compiler's scope pushes. +//! The IR depends on the process in three ways, and the entry undoes each: +//! +//! - `Sym` ids: the entry has its own name table, which the load interns +//! again. +//! - `Pos` values: stored relative to the file, rebased on load. +//! - Constant `Word`s: literals are stored by content. Globals and +//! `builtins.x` are stored by name and looked up in the live tables, so +//! `__nixPath` follows the run. +//! +//! Binding frames (`let`, `rec`, formals) are in `Sym` id order. So the +//! loader sorts them again and rewrites every `Local` index through a +//! permutation stack that mirrors the scope pushes of the compiler. use std::path::{Path, PathBuf}; diff --git a/src/compile/ir.rs b/src/compile/ir.rs index 6b62a9a6..4fc6cf79 100644 --- a/src/compile/ir.rs +++ b/src/compile/ir.rs @@ -1,7 +1,7 @@ -//! Resolved intermediate representation. Variables are `(depth, index)` -//! pairs into the runtime env chain, attrpaths are merged, `inherit` is -//! expanded, and `builtins.x` on the unshadowed global is folded to a -//! constant. IR nodes live forever (`&'static`) so thunks can point at them. +//! Resolved intermediate representation. A variable is a `(depth, index)` +//! pair into the runtime env chain. The compiler merges attrpaths, expands +//! `inherit` and folds `builtins.x` on the unshadowed global to a constant. +//! IR nodes live forever (`&'static`) so thunks can point at them. use crate::eval::Pos; use crate::runtime::Word; diff --git a/src/eval/mod.rs b/src/eval/mod.rs index 62fc7b56..58c9c920 100644 --- a/src/eval/mod.rs +++ b/src/eval/mod.rs @@ -458,8 +458,8 @@ impl EvalConfig { self } - /// What [`EvalConfig::pure`] settled on: the tracing-cache key needs it, - /// and a frontend's flag is not the whole answer any more. + /// The value [`EvalConfig::pure`] settled on. The tracing-cache key needs + /// it, because the flag of a frontend is not the whole answer. pub fn is_pure(&self) -> bool { self.pure } diff --git a/src/eval/tests.rs b/src/eval/tests.rs index bef66fc0..cba987c3 100644 --- a/src/eval/tests.rs +++ b/src/eval/tests.rs @@ -5,21 +5,22 @@ use rstest::rstest; use crate::print; -fn eval_str(src: &str) -> String { - let mut ev = Evaluator::new(); - let v = ev.eval_string(src, b"/").expect("eval"); - let v = ev.force_deep(v).expect("force"); +fn eval_with(mut ev: Evaluator, src: &str) -> Result { + let v = ev + .eval_string(src, b"/") + .and_then(|v| ev.force_deep(v)) + .map_err(|e| e.msg)?; let mut out = Vec::new(); print::print_ambiguous(&mut ev, v, 1, &mut out, &mut HashSet::default(), 0).unwrap(); - String::from_utf8(out).unwrap() + Ok(String::from_utf8(out).unwrap()) +} + +fn eval_str(src: &str) -> String { + eval_with(Evaluator::new(), src).unwrap_or_else(|e| panic!("{src:?}: {e}")) } fn eval_err(src: &str) -> String { - let mut ev = Evaluator::new(); - match ev.eval_string(src, b"/").and_then(|v| ev.force_deep(v)) { - Ok(_) => panic!("expected an error"), - Err(e) => e.msg, - } + eval_with(Evaluator::new(), src).expect_err("expected an error") } #[test] @@ -195,14 +196,7 @@ fn eval_config_defaults_to_the_system_this_build_is_for() { } fn eval_pure(src: &str) -> Result { - let mut ev = EvalConfig::new().pure(true).build(); - let v = ev - .eval_string(src, b"/") - .and_then(|v| ev.force_deep(v)) - .map_err(|e| e.msg)?; - let mut out = Vec::new(); - print::print_ambiguous(&mut ev, v, 1, &mut out, &mut HashSet::default(), 0).unwrap(); - Ok(String::from_utf8(out).unwrap()) + eval_with(EvalConfig::new().pure(true).build(), src) } /// The answers of `nix-instantiate --pure-eval --read-write-mode` (Nix 2.34.8). @@ -473,13 +467,7 @@ fn nix_eq_forces_what_the_shortcut_skips() { let eq = |src: &str| { let mut ev = Evaluator::new(); ev.nix_eq = true; - let v = ev - .eval_string(src, b"/") - .and_then(|v| ev.force_deep(v)) - .map_err(|e| e.msg)?; - let mut out = Vec::new(); - print::print_ambiguous(&mut ev, v, 1, &mut out, &mut HashSet::default(), 0).unwrap(); - Ok::<_, String>(String::from_utf8(out).unwrap()) + eval_with(ev, src) }; let x = "let x = { a = throw \"a\"; }; in"; assert_eq!(eval_str(&format!("{x} x == x")), "true"); diff --git a/src/parallel/force.rs b/src/parallel/force.rs index a1bd6f66..1912849a 100644 --- a/src/parallel/force.rs +++ b/src/parallel/force.rs @@ -518,7 +518,6 @@ pub fn fanout_range(ev: &Evaluator, kind: u8, obj: Word, lo: usize, hi: usize, b let mut start = lo as u32; while (start as usize) < n { let len = batch.min(n as u32 - start); - // Only ranges with an unclaimed thunk are worth a task. let worth = (start..start + len) .any(|i| matches!(cell_state(cell(i as usize)), CellState::Unclaimed)); if worth { diff --git a/src/parallel/sched.rs b/src/parallel/sched.rs index f96189b4..aa260535 100644 --- a/src/parallel/sched.rs +++ b/src/parallel/sched.rs @@ -804,7 +804,6 @@ impl Runtime { } } - /// Note that forcing the thunk at `key` failed. #[inline] pub fn note_failed(&self, key: u64) { ANY_FAILED.store(true, Ordering::Relaxed); diff --git a/src/runtime/arena.rs b/src/runtime/arena.rs index ca502326..ea98a056 100644 --- a/src/runtime/arena.rs +++ b/src/runtime/arena.rs @@ -1,7 +1,7 @@ -//! Bump allocator. Memory is never freed while its session lives: every -//! allocation hands out a `'static` reference, and the chunks go only when -//! the session's `Heap` drops. That is what makes graph sharing between -//! cells trivially sound. +//! Bump allocator. Memory is never freed while its session lives. Every +//! allocation gives a `'static` reference, and the chunks go only when the +//! `Heap` of the session drops. Thus it is sound for cells to share any +//! part of the graph with no further check. use std::sync::Arc; use std::time::{Duration, Instant}; @@ -243,7 +243,6 @@ impl Arena { } } - /// Move the elements of `v` into one arena slice. pub(crate) fn alloc_vec(&mut self, v: Vec) -> &'static [T] { let n = v.len(); if n == 0 { @@ -264,7 +263,6 @@ impl Arena { self.alloc_slice(b) } - /// Concatenate byte slices into one arena allocation. pub(crate) fn alloc_concat(&mut self, parts: &[&[u8]]) -> &'static [u8] { let len: usize = parts.iter().map(|p| p.len()).sum(); if len == 0 { diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index f4955348..5ef40ff0 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -7,9 +7,8 @@ pub mod word; pub use arena::Arena; pub use value::{ - Attr, AttrIter, AttrsObj, Ctx, EnvObj, FloatObj, IntObj, Kind, LambdaObj, ListObj, MapDesc, - PathObj, PrimFn, PrimOpAppObj, PrimOpObj, StringObj, Taint, ThunkApp2Obj, ThunkAppObj, - ThunkObj, UnionTail, Value, ValueType, split_built, + Attr, AttrsObj, Ctx, EnvObj, Kind, ListObj, PrimFn, StringObj, Taint, ThunkObj, Value, + ValueType, split_built, }; pub(crate) use value::{MAX_LEAVES, UNION_MIN, ctx_to_arena, leaf_count}; pub use word::{Cell, Word}; diff --git a/src/runtime/value.rs b/src/runtime/value.rs index 074213a4..43a12666 100644 --- a/src/runtime/value.rs +++ b/src/runtime/value.rs @@ -300,12 +300,13 @@ unsafe impl Obj for AttrsObj {} unsafe impl Obj for PrimOpObj {} unsafe impl Obj for PrimOpAppObj {} -/// A suspended `ir` in `env`, overwritten in place when forced. Thunk +/// A suspended `ir` in `env`. A force overwrites it in place. Thunk /// objects carry no `Kind` byte: the word tag tells the layouts apart. -/// Every layout has one `state` cell that goes from the payload it holds -/// while unforced (never EMPTY, never a BLACKHOLE) to BLACKHOLE while being -/// forced to EMPTY once forced, and one `code` word that holds the IR or -/// function until then and the forced value after (docs/explanation/evaluation.md). +/// Every layout has one `state` cell and one `code` word +/// (docs/explanation/evaluation.md). `state` holds the payload while +/// unforced (never EMPTY, never a BLACKHOLE), a BLACKHOLE during the +/// force, and EMPTY after it. `code` holds the IR or the function until +/// the force, and the forced value after it. #[repr(C)] pub struct ThunkObj { /// `&Ir`, then the value. @@ -334,11 +335,11 @@ pub struct ThunkApp2Obj { pub state: Cell, } -/// One per `mapAttrs` call; entry `i` of `out` starts as the map word -/// `(desc, i)`, which is a thunk with no object of its own: its `code` -/// word is that entry's cell (the map word, then the value) and its -/// state is `states[i]` (NULL, BLACKHOLE, EMPTY). Forcing applies `f` to -/// the name and `src`'s entry `i`. Attr cells never hold a BLACKHOLE, so +/// One per `mapAttrs` call. Entry `i` of `out` starts as the map word +/// `(desc, i)`: a thunk with no object of its own. Its `code` word is the +/// cell of that entry (the map word, then the value). Its state is +/// `states[i]` (NULL, BLACKHOLE, EMPTY). A force applies `f` to the name +/// and to entry `i` of `src`. Attr cells never hold a BLACKHOLE, so /// readers of `attr.cell` need no special case. #[repr(C)] pub struct MapDesc { diff --git a/src/runtime/word.rs b/src/runtime/word.rs index 0880a31d..862047e1 100644 --- a/src/runtime/word.rs +++ b/src/runtime/word.rs @@ -20,8 +20,8 @@ //! passes it back to the library, and reads it through `api::value` //! (docs/reference/library.md). Inside the crate a pointer-shaped word //! comes from a `&'static` arena object, so no safe path invents an -//! address; the two raw-bit entries are `from_bits`, which is `unsafe`, -//! and `imm_from_bits`, which admits immediates only. +//! address. Only two entries take raw bits: `from_bits`, which is +//! `unsafe`, and `imm_from_bits`, which accepts immediates only. //! //! Outside the crate the bits cannot be invented: //! diff --git a/src/syntax/tests.rs b/src/syntax/tests.rs index 4a545c20..2bed21df 100644 --- a/src/syntax/tests.rs +++ b/src/syntax/tests.rs @@ -1,7 +1,7 @@ use super::ast::{Ast, Expr, ExprId, FileId, Note, StrPart}; use super::dump::{dump, json, tree}; use super::symbol::Interner; -use super::{line_col, parse}; +use super::{ParseError, line_col, parse}; use crate::lint::LintId; use crate::lint::allow::bit; use rstest::rstest; @@ -19,17 +19,21 @@ fn dump_src(src: &str) -> String { dump(&ast, &interner) } -fn parse_err(src: &str) -> String { +fn parse_error(src: &str) -> ParseError { let mut interner = Interner::new(); match parse(src.as_bytes(), FileId(0), &mut interner) { Ok(ast) => panic!( "expected parse error for {src:?}, got {}", dump(&ast, &interner) ), - Err(e) => e.msg, + Err(e) => e, } } +fn parse_err(src: &str) -> String { + parse_error(src).msg +} + fn str_value(src: &str) -> String { let d = dump_src(src); let inner = d @@ -320,9 +324,7 @@ fn a_line_ends_at_cr_lf_and_lone_cr() { assert_eq!(line_col(src, 5), (3, 1)); assert_eq!(line_col(src, 7), (4, 1)); assert_eq!(line_col(src, 8), (5, 1)); - let e = parse(b"l\r =", FileId(0), &mut Interner::new()) - .err() - .unwrap(); + let e = parse_error("l\r ="); assert_eq!(line_col(b"l\r =", e.span.start), (2, 2)); } @@ -526,9 +528,7 @@ fn nul_in_string_literals() { ("\"$\0\"", "$␀", (1, 2)), ]; for (src, shown, pos) in cases { - let e = parse(src.as_bytes(), FileId(0), &mut Interner::new()) - .err() - .unwrap_or_else(|| panic!("{src:?} parsed")); + let e = parse_error(src); assert_eq!( (e.msg, line_col(src.as_bytes(), e.span.start)), ( @@ -561,9 +561,7 @@ fn end_of_file_inside_a_path_is_at_its_last_token() { ("./a/ ", (1, 5)), ]; for (src, pos) in cases { - let e = parse(src.as_bytes(), FileId(0), &mut Interner::new()) - .err() - .unwrap_or_else(|| panic!("{src:?} parsed")); + let e = parse_error(src); assert_eq!(line_col(src.as_bytes(), e.span.start), pos, "{src:?}"); } } @@ -609,9 +607,7 @@ fn the_error_reported_is_the_one_nix_meets_first() { ), ]; for (src, msg, pos) in cases { - let e = parse(src.as_bytes(), FileId(0), &mut Interner::new()) - .err() - .unwrap_or_else(|| panic!("{src:?} parsed")); + let e = parse_error(src); assert!(e.msg.contains(msg), "{src:?}: {}", e.msg); assert_eq!(line_col(src.as_bytes(), e.span.start), pos, "{src:?}"); } diff --git a/src/trace/watch.rs b/src/trace/watch.rs index cafbf0cf..a1134856 100644 --- a/src/trace/watch.rs +++ b/src/trace/watch.rs @@ -1,7 +1,7 @@ //! Which paths of a read set a watcher can use. //! -//! Selected for a watcher that compares modification times, which is all a -//! file watcher has. A consumer that can do better picks by `kind`. +//! The selection is for a watcher that compares modification times, which +//! is all a file watcher has. A consumer that can do more picks by `kind`. use super::{Filtered, Kind, Obs}; use crate::sys;