From cda9f8b5375dd8901e96b859e08fef1820b95d68 Mon Sep 17 00:00:00 2001 From: Johannes Kirschbauer Date: Thu, 19 Sep 2024 11:23:11 +0200 Subject: [PATCH] Pesto: handle callPackage and makeOverridable edge cases --- pesto/README.md | 12 ++++ pesto/src/position.rs | 87 ++++++++++++++++------- pesto/test_data/makeOverridable/test.json | 44 ++++++++++++ 3 files changed, 119 insertions(+), 24 deletions(-) diff --git a/pesto/README.md b/pesto/README.md index 63b0708..8820747 100644 --- a/pesto/README.md +++ b/pesto/README.md @@ -19,3 +19,15 @@ pesto --file "attrsets.nix" --line "11" --column "3" "countApplied": 3 } ``` + +## Contribute + +Generating test dataset + +`nix build .#pasta -L` + +Which can the be passed + +`cargo run -- --pos-file result --format json ./out.json` + +It is recommended to remove all unneeded entries and operate on minimal list that contains only one entry. diff --git a/pesto/src/position.rs b/pesto/src/position.rs index 9b3d963..6231312 100644 --- a/pesto/src/position.rs +++ b/pesto/src/position.rs @@ -51,33 +51,61 @@ pub fn get_src(path: &PathBuf) -> String { exit(1); } -/// Returns the node path +/// Returns rnix::SyntaxKind::NODE_PATH pub fn get_call_package_file(node: Option<&SyntaxNode>) -> Option { if let Some(node) = node { - match node.kind() { - rnix::SyntaxKind::NODE_ATTRPATH_VALUE => { - get_call_package_file(node.last_child().as_ref()) + for ev in node.preorder() { + let res = match ev { + WalkEvent::Enter(node) => match node.kind() { + rnix::SyntaxKind::NODE_ATTRPATH_VALUE => { + get_call_package_file(node.last_child().as_ref()) + } + rnix::SyntaxKind::NODE_APPLY => { + let maybe_path = filter_path_from_apply(&node); + match maybe_path.as_ref().map(|n| n.kind()) { + Some(rnix::SyntaxKind::NODE_PATH) => maybe_path.clone(), + _ => { + // callPackage ./path/file.nix {} + // Maybe the path is a dynamic expression which is non-trivial to resolve? + println!("Could not find path in apply {:?}", &node); + None + } + } + } + _ => None, + }, + _ => None, + }; + if let Some(res) = res { + return Some(res); } - rnix::SyntaxKind::NODE_PATH => Some(node.clone()), - rnix::SyntaxKind::NODE_APPLY => match node.first_child().map(|n| n.kind()) { - Some(rnix::SyntaxKind::NODE_APPLY) => { - get_call_package_file(node.first_child().as_ref()) - } - _ => get_call_package_file(node.last_child().as_ref()), - }, - _ => { - println!( - "Unhandled node when trying to unpack callPackage expression: {:?}", - node.kind() - ); - None - } // n => get_call_package_file(node.last_child().as_ref()), } - } else { - None } + None } +fn filter_path_from_apply(apply: &SyntaxNode) -> Option { + for ev in apply.preorder() { + let res = match ev { + WalkEvent::Enter(node) => match node.kind() { + rnix::SyntaxKind::NODE_IDENT => { + // If the first child is callPackage. + // Assume CallPackage takes a second argument, which is the next node. + if node.text().to_string() == "callPackage" { + return node.next_sibling(); + } + None + } + _ => None, + }, + _ => None, + }; + if let Some(res) = res { + return res; + } + } + None +} /// Goes up the tree to find the parent node that matches the predicate, /// checks starting with the current node /// Stops when the limit is reached @@ -301,19 +329,30 @@ pub fn seek_file_position(path: &PathBuf, text_pos: &TextSize) -> Option x: ... // Returns the lambda node, if one exists // Aborts if the node is not a lambda +// Returns rnix::SyntaxKind::NODE_LAMBDA fn unpack_lambda(node: &SyntaxNode) -> Option { for ev in node.preorder() { let res = match ev { WalkEvent::Enter(node) => { match node.kind() { // The top level callpackage lambda - rnix::SyntaxKind::NODE_PAREN => None, rnix::SyntaxKind::NODE_LAMBDA => Some(node), + rnix::SyntaxKind::NODE_APPLY => node + .first_child() + .as_ref() + .map(|n| unpack_lambda(n)) + .flatten(), + rnix::SyntaxKind::NODE_PAREN => None, + rnix::SyntaxKind::NODE_SELECT => None, + rnix::SyntaxKind::NODE_IDENT => None, + rnix::SyntaxKind::NODE_ATTRPATH => None, + rnix::SyntaxKind::NODE_ATTR_SET => None, + rnix::SyntaxKind::NODE_IF_ELSE => None, _ => { println!( - "Unexpected node kind: {:?}. Expected Parenthesis '(x: ...)' or Lambda 'x: ... '", - node.kind() - ); + "Unexpected node kind: {:?}. Expected Parenthesis '(x: ...)' or Lambda 'x: ... '", + node.kind() + ); exit(1); } } diff --git a/pesto/test_data/makeOverridable/test.json b/pesto/test_data/makeOverridable/test.json index 2becb91..65a896f 100644 --- a/pesto/test_data/makeOverridable/test.json +++ b/pesto/test_data/makeOverridable/test.json @@ -1,4 +1,48 @@ [ + { + "docs": { + "attr": { + "position": { + "column": 3, + "file": "/nix/store/sv1ix2lrwxbflx43b1pqbpj5y1fm5frk-nixpkgs-migrated/pkgs/top-level/all-packages.nix", + "line": 937 + } + }, + "lambda": { + "countApplied": 1, + "isFunctor": true, + "isPrimop": false, + "position": { + "column": 17, + "file": "/nix/store/sv1ix2lrwxbflx43b1pqbpj5y1fm5frk-nixpkgs-migrated/lib/customisation.nix", + "line": 136 + } + } + }, + "path": ["pkgs", "fetchgit"] + }, + { + "docs": { + "attr": { + "position": { + "column": 3, + "file": "/nix/store/sv1ix2lrwxbflx43b1pqbpj5y1fm5frk-nixpkgs-migrated/pkgs/top-level/all-packages.nix", + "line": 922 + } + }, + "lambda": { + "countApplied": 1, + "isFunctor": true, + "isPrimop": false, + "position": { + "column": 17, + "file": "/nix/store/sv1ix2lrwxbflx43b1pqbpj5y1fm5frk-nixpkgs-migrated/lib/customisation.nix", + "line": 136 + } + } + }, + "path": ["pkgs", "fetchcvs"] + }, { "docs": { "attr": {