diff --git a/src/models/filter.rs b/src/models/filter.rs index f246fb20c..e8920d9fe 100644 --- a/src/models/filter.rs +++ b/src/models/filter.rs @@ -16,6 +16,7 @@ use std::collections::HashMap; use derive_builder::Builder; use getset::Getters; use itertools::Itertools; +use log::debug; use pyo3::prelude::{pyclass, pymethods}; use serde_derive::Deserialize; @@ -278,7 +279,9 @@ impl Instantiate for Filter { fn instantiate(&self, substitutions_for_holes: &HashMap) -> Filter { Filter { enclosing_node: self.enclosing_node().instantiate(substitutions_for_holes), - outermost_enclosing_node: self.enclosing_node().instantiate(substitutions_for_holes), + outermost_enclosing_node: self + .outermost_enclosing_node() + .instantiate(substitutions_for_holes), not_enclosing_node: self .not_enclosing_node() .instantiate(substitutions_for_holes), @@ -314,7 +317,7 @@ impl SourceCodeUnit { /// /// The `filter` is composed of: /// (i) `enclosing_node`, the node to inspect, optional. If not provided we check whether the contains or non_contains are satisfied in the current node. - /// (ii) `not_enclosing_node`, optionalquery that no ancestor of the primary match should match, + /// (ii) `not_enclosing_node`, optional query that no ancestor of the primary match should match, /// (iii) `not_contains` and `contains`, optional queries that should not and should match within the `enclosing_node`, /// (iv) `at_least` and `at_most`, optional parameters indicating the acceptable range of matches for `contains` within the `enclosing_node`. /// @@ -352,6 +355,17 @@ impl SourceCodeUnit { } } + // If an outermost enclosing node is provided + let query = instantiated_filter.outermost_enclosing_node(); + if !query.get_query().is_empty() { + debug!("here"); + if let Some(result) = self._match_outermost_ancestor(rule_store, node_to_check, query) { + node_to_check = result; + } else { + return false; + } + } + self._check_filter_not_contains(&instantiated_filter, rule_store, &node_to_check) && self._check_filter_contains(&instantiated_filter, rule_store, &node_to_check) } @@ -373,7 +387,26 @@ impl SourceCodeUnit { true } - /// Search for any ancestor of `node` (including itself) that matches `query_str` + /// Search for outermost ancestor of `node` (including itself) that matches `query_str` + fn _match_outermost_ancestor( + &self, rule_store: &mut RuleStore, node: Node, ts_query: &TSQuery, + ) -> Option { + let mut matched_ancestor = self._match_ancestor(rule_store, node, ts_query); + matched_ancestor?; + loop { + if let Some(parent) = matched_ancestor.unwrap().parent() { + if let Some(m) = self._match_ancestor(rule_store, parent, ts_query) { + if m.range() != matched_ancestor.unwrap().range() { + matched_ancestor = Some(m); + continue; + } + } + } + return matched_ancestor; + } + } + + /// Search for innermost ancestor of `node` (including itself) that matches `query_str` fn _match_ancestor( &self, rule_store: &mut RuleStore, node: Node, ts_query: &TSQuery, ) -> Option { diff --git a/src/models/unit_tests/source_code_unit_test.rs b/src/models/unit_tests/source_code_unit_test.rs index 6d4e7a3bc..c4159c45f 100644 --- a/src/models/unit_tests/source_code_unit_test.rs +++ b/src/models/unit_tests/source_code_unit_test.rs @@ -631,3 +631,69 @@ fn test_contains_no_enclosing_positive() { |result| result, ); } + +#[test] +fn test_satisfies_outermost_enclosing_node() { + let rule_positive = piranha_rule! { + name= "test", + query= "( + (method_declaration name: (_) @name) @md + (#eq? @name \"foobar\") + )", + filters= [filter!{ + , outermost_enclosing_node = "(class_declaration) @cd" + , contains = "((method_invocation name: (_) @mname) @mi (#eq? @mname \"foobar\"))" + }] + }; + let rule_positive = InstantiatedRule::new(&rule_positive, &HashMap::new()); + + let rule_negative = piranha_rule! { + name= "test", + query= "( + (method_declaration name: (_) @name) @md + (#eq? @name \"foobar\") + )", + filters= [filter!{ + , outermost_enclosing_node = "(class_declaration) @cd" + , not_contains = ["((method_invocation name: (_) @mname) @mi (#eq? @mname \"foobar\"))",] + }] + }; + let rule_negative = InstantiatedRule::new(&rule_negative, &HashMap::new()); + + let source_code = "class OuterClass { + + void someMethod() { + Test t = new Test(); + t.foobar(); + } + class Test { + private void foobar(){ + System.out.println(); + } + } + }"; + + let mut rule_store = RuleStore::default(); + let java = get_java_tree_sitter_language(); + let mut parser = java.parser(); + let piranha_arguments = &PiranhaArgumentsBuilder::default() + .path_to_codebase(UNUSED_CODE_PATH.to_string()) + .language(java) + .build(); + let source_code_unit = SourceCodeUnit::new( + &mut parser, + source_code.to_string(), + &HashMap::new(), + PathBuf::new().as_path(), + piranha_arguments, + ); + + let node = &source_code_unit + .root_node() + .descendant_for_byte_range(119, 178) + .unwrap(); + + assert!(!source_code_unit.is_satisfied(*node, &rule_negative, &HashMap::new(), &mut rule_store,)); + + assert!(source_code_unit.is_satisfied(*node, &rule_positive, &HashMap::new(), &mut rule_store,)); +} diff --git a/src/tests/test_piranha_swift.rs b/src/tests/test_piranha_swift.rs index 0301eeb57..697bfd882 100644 --- a/src/tests/test_piranha_swift.rs +++ b/src/tests/test_piranha_swift.rs @@ -41,9 +41,6 @@ create_rewrite_tests! { "stale_flag" => "one" }, cleanup_comments = true, delete_file_if_empty= false; - - test_cleanup_with_derivatives: "cleanup_with_derivatives", 1, - cleanup_comments = true, delete_file_if_empty= false; } fn execute_piranha_with_default_swift_args(scenario: &str, substitutions: Vec<(String, String)>) { @@ -75,6 +72,13 @@ fn test_cleanup_rules_file() { ); } +#[test] +#[ignore] // Long running test +fn test_cleanup_with_derivatives() { + super::initialize(); + execute_piranha_with_default_swift_args("cleanup_with_derivatives", vec![]); +} + #[test] #[ignore] // Long running test fn test_local_variable_inline_file() {