@@ -15,36 +15,27 @@ use std::fs::read_to_string;
1515use std:: path:: Path ;
1616use std:: path:: PathBuf ;
1717
18- /// A structure to indefinitely cache parse results of Nix files in memory,
18+ /// A structure to store parse results of Nix files in memory,
1919/// making sure that the same file never has to be parsed twice
20- pub struct NixFileCache {
21- entries : HashMap < PathBuf , NixFileResult > ,
20+ #[ derive( Default ) ]
21+ pub struct NixFileStore {
22+ entries : HashMap < PathBuf , NixFile > ,
2223}
2324
24- impl NixFileCache {
25- /// Creates a new empty NixFileCache
26- pub fn new ( ) -> NixFileCache {
27- NixFileCache {
28- entries : HashMap :: new ( ) ,
29- }
30- }
31-
32- /// Get the cache entry for a Nix file if it exists, otherwise parse the file, insert it into
33- /// the cache, and return the value
25+ impl NixFileStore {
26+ /// Get the store entry for a Nix file if it exists, otherwise parse the file, insert it into
27+ /// the store, and return the value
3428 ///
3529 /// Note that this function only gives an anyhow::Result::Err for I/O errors.
3630 /// A parse error is anyhow::Result::Ok(Result::Err(error))
37- pub fn get ( & mut self , path : & Path ) -> anyhow:: Result < & NixFileResult > {
31+ pub fn get ( & mut self , path : & Path ) -> anyhow:: Result < & NixFile > {
3832 match self . entries . entry ( path. to_owned ( ) ) {
3933 Entry :: Occupied ( entry) => Ok ( entry. into_mut ( ) ) ,
4034 Entry :: Vacant ( entry) => Ok ( entry. insert ( NixFile :: new ( path) ?) ) ,
4135 }
4236 }
4337}
4438
45- /// A type to represent the result when trying to initialise a `NixFile`.
46- type NixFileResult = Result < NixFile , rnix:: parser:: ParseError > ;
47-
4839/// A structure for storing a successfully parsed Nix file
4940pub struct NixFile {
5041 /// The parent directory of the Nix file, for more convenient error handling
@@ -57,7 +48,7 @@ pub struct NixFile {
5748
5849impl NixFile {
5950 /// Creates a new NixFile, failing for I/O or parse errors
60- fn new ( path : impl AsRef < Path > ) -> anyhow:: Result < NixFileResult > {
51+ fn new ( path : impl AsRef < Path > ) -> anyhow:: Result < NixFile > {
6152 let Some ( parent_dir) = path. as_ref ( ) . parent ( ) else {
6253 anyhow:: bail!( "Could not get parent of path {}" , path. as_ref( ) . display( ) )
6354 } ;
@@ -66,14 +57,21 @@ impl NixFile {
6657 . with_context ( || format ! ( "Could not read file {}" , path. as_ref( ) . display( ) ) ) ?;
6758 let line_index = LineIndex :: new ( & contents) ;
6859
69- Ok ( rnix:: Root :: parse ( & contents)
60+ // NOTE: There's now another Nixpkgs CI check to make sure all changed Nix files parse
61+ // correctly, though that uses mainline Nix instead of rnix, so it doesn't give the same
62+ // errors. In the future we should unify these two checks, ideally moving the other CI
63+ // check into this tool as well and checking for both mainline Nix and rnix.
64+ rnix:: Root :: parse ( & contents)
65+ // rnix's ::ok returns Result<_, _> , so no error is thrown away like it would be with
66+ // std::result's ::ok
7067 . ok ( )
7168 . map ( |syntax_root| NixFile {
7269 parent_dir : parent_dir. to_path_buf ( ) ,
7370 path : path. as_ref ( ) . to_owned ( ) ,
7471 syntax_root,
7572 line_index,
76- } ) )
73+ } )
74+ . with_context ( || format ! ( "Could not parse file {} with rnix" , path. as_ref( ) . display( ) ) )
7775 }
7876}
7977
@@ -88,7 +86,11 @@ pub struct CallPackageArgumentInfo {
8886
8987impl NixFile {
9088 /// Returns information about callPackage arguments for an attribute at a specific line/column
91- /// index. If there's no guaranteed `callPackage` at that location, `None` is returned.
89+ /// index.
90+ /// If the location is not of the form `<attr> = callPackage <arg1> <arg2>;`, `None` is
91+ /// returned.
92+ /// This function only returns `Err` for problems that can't be caused by the Nix contents,
93+ /// but rather problems in this programs code itself.
9294 ///
9395 /// This is meant to be used with the location returned from `builtins.unsafeGetAttrPos`, e.g.:
9496 /// - Create file `default.nix` with contents
@@ -102,7 +104,7 @@ impl NixFile {
102104 /// builtins.unsafeGetAttrPos "foo" (import ./default.nix { })
103105 /// ```
104106 /// results in `{ file = ./default.nix; line = 2; column = 3; }`
105- /// - Get the NixFile for `.file` from a `NixFileCache `
107+ /// - Get the NixFile for `.file` from a `NixFileStore `
106108 /// - Call this function with `.line`, `.column` and `relative_to` as the (absolute) current directory
107109 ///
108110 /// You'll get back
@@ -165,6 +167,7 @@ impl NixFile {
165167 } ;
166168
167169 if attrpath_node. kind ( ) != SyntaxKind :: NODE_ATTRPATH {
170+ // This can happen for e.g. `inherit foo`, so definitely not a syntactic `callPackage`
168171 return Ok ( None ) ;
169172 }
170173 // attrpath_node looks like "foo.bar"
@@ -183,7 +186,9 @@ impl NixFile {
183186 }
184187 // attrpath_value_node looks like "foo.bar = 10;"
185188
186- // unwrap is fine because we confirmed that we can cast with the above check
189+ // unwrap is fine because we confirmed that we can cast with the above check.
190+ // We could avoid this `unwrap` for a `clone`, since `cast` consumes the argument,
191+ // but we still need it for the error message when the cast fails.
187192 Ok ( Some ( ast:: AttrpathValue :: cast ( attrpath_value_node) . unwrap ( ) ) )
188193 }
189194
@@ -272,9 +277,8 @@ impl NixFile {
272277
273278 // Check that <fun2> is an identifier, or an attribute path with an identifier at the end
274279 let ident = match function2 {
275- Expr :: Ident ( ident) =>
276- // This means it's something like `foo = callPackage <arg2> <arg1>`
277- {
280+ Expr :: Ident ( ident) => {
281+ // This means it's something like `foo = callPackage <arg2> <arg1>`
278282 ident
279283 }
280284 Expr :: Select ( select) => {
@@ -340,13 +344,12 @@ pub enum ResolvedPath {
340344
341345impl NixFile {
342346 /// Statically resolves a Nix path expression and checks that it's within an absolute path
343- /// path
344347 ///
345348 /// E.g. for the path expression `./bar.nix` in `./foo.nix` and an absolute path of the
346349 /// current directory, the function returns `ResolvedPath::Within(./bar.nix)`
347350 pub fn static_resolve_path ( & self , node : ast:: Path , relative_to : & Path ) -> ResolvedPath {
348351 if node. parts ( ) . count ( ) != 1 {
349- // Only if there's more than 1 interpolated part, it's of the form `./foo/${bar}/baz`.
352+ // If there's more than 1 interpolated part, it's of the form `./foo/${bar}/baz`.
350353 return ResolvedPath :: Interpolated ;
351354 }
352355
@@ -364,9 +367,8 @@ impl NixFile {
364367 // That should be checked more strictly
365368 match self . parent_dir . join ( Path :: new ( & text) ) . canonicalize ( ) {
366369 Err ( resolution_error) => ResolvedPath :: Unresolvable ( resolution_error) ,
367- Ok ( resolved) =>
368- // Check if it's within relative_to
369- {
370+ Ok ( resolved) => {
371+ // Check if it's within relative_to
370372 match resolved. strip_prefix ( relative_to) {
371373 Err ( _prefix_error) => ResolvedPath :: Outside ,
372374 Ok ( suffix) => ResolvedPath :: Within ( suffix. to_path_buf ( ) ) ,
@@ -411,7 +413,7 @@ mod tests {
411413
412414 std:: fs:: write ( & file, contents) ?;
413415
414- let nix_file = NixFile :: new ( & file) ?? ;
416+ let nix_file = NixFile :: new ( & file) ?;
415417
416418 // These are builtins.unsafeGetAttrPos locations for the attributes
417419 let cases = [
@@ -454,7 +456,7 @@ mod tests {
454456
455457 std:: fs:: write ( & file, contents) ?;
456458
457- let nix_file = NixFile :: new ( & file) ?? ;
459+ let nix_file = NixFile :: new ( & file) ?;
458460
459461 let cases = [
460462 ( 2 , None ) ,
0 commit comments