Fix a bug that prevented early termination of query matches

This commit is contained in:
Max Brunsfeld 2019-09-18 15:28:46 -07:00
parent d9b8bae629
commit a6b6a681ec
3 changed files with 106 additions and 33 deletions

View file

@ -1,7 +1,7 @@
use super::helpers::allocations;
use super::helpers::fixtures::get_language;
use tree_sitter::{Node, Parser, Query, QueryCapture, QueryCursor, QueryError, QueryMatch};
use std::fmt::Write;
use tree_sitter::{Node, Parser, Query, QueryCapture, QueryCursor, QueryError, QueryMatch};
#[test]
fn test_query_errors_on_invalid_syntax() {
@ -625,16 +625,13 @@ fn test_query_captures_with_duplicates() {
let captures = cursor.captures(&query, tree.root_node(), to_callback(source));
assert_eq!(
collect_captures(captures, &query, source),
&[
("function", "x"),
("variable", "x"),
],
&[("function", "x"), ("variable", "x"),],
);
});
}
#[test]
fn test_query_captures_with_many_results() {
fn test_query_captures_with_many_nested_results_without_fields() {
allocations::record(|| {
let language = get_language("javascript");
@ -644,7 +641,7 @@ fn test_query_captures_with_many_results() {
r#"
(pair
key: * @method-def
value: (arrow_function))
(arrow_function))
":" @colon
"," @comma
@ -672,21 +669,24 @@ fn test_query_captures_with_many_results() {
let captures = cursor.captures(&query, tree.root_node(), to_callback(&source));
let captures = collect_captures(captures, &query, &source);
assert_eq!(&captures[0..13], &[
("colon", ":"),
("method-def", "method0"),
("colon", ":"),
("comma", ","),
("method-def", "method1"),
("colon", ":"),
("comma", ","),
("method-def", "method2"),
("colon", ":"),
("comma", ","),
("method-def", "method3"),
("colon", ":"),
("comma", ","),
]);
assert_eq!(
&captures[0..13],
&[
("colon", ":"),
("method-def", "method0"),
("colon", ":"),
("comma", ","),
("method-def", "method1"),
("colon", ":"),
("comma", ","),
("method-def", "method2"),
("colon", ":"),
("comma", ","),
("method-def", "method3"),
("colon", ":"),
("comma", ","),
]
);
// Ensure that we don't drop matches because of needing to buffer too many.
assert_eq!(captures.len(), 1 + 3 * method_count);
@ -694,7 +694,74 @@ fn test_query_captures_with_many_results() {
}
#[test]
fn test_query_pattern_after_source_byte() {
fn test_query_captures_with_many_nested_results_with_fields() {
allocations::record(|| {
let language = get_language("javascript");
// Search expressions like `a ? a.b : null`
let query = Query::new(
language,
r#"
((ternary_expression
condition: (identifier) @left
consequence: (member_expression
object: (identifier) @right)
alternative: (null))
(eq? @left @right))
"#,
)
.unwrap();
// The outer expression does not match the pattern, but the consequence of the ternary
// is an object that *does* contain many occurences of the pattern.
let count = 50;
let mut source = "a ? {".to_owned();
for i in 0..count {
writeln!(&mut source, " x: y{} ? y{}.z : null,", i, i).unwrap();
}
source.push_str("} : null;\n");
let mut parser = Parser::new();
parser.set_language(language).unwrap();
let tree = parser.parse(&source, None).unwrap();
let mut cursor = QueryCursor::new();
let captures = cursor.captures(&query, tree.root_node(), to_callback(&source));
let captures = collect_captures(captures, &query, &source);
assert_eq!(
&captures[0..20],
&[
("left", "y0"),
("right", "y0"),
("left", "y1"),
("right", "y1"),
("left", "y2"),
("right", "y2"),
("left", "y3"),
("right", "y3"),
("left", "y4"),
("right", "y4"),
("left", "y5"),
("right", "y5"),
("left", "y6"),
("right", "y6"),
("left", "y7"),
("right", "y7"),
("left", "y8"),
("right", "y8"),
("left", "y9"),
("right", "y9"),
]
);
// Ensure that we don't drop matches because of needing to buffer too many.
assert_eq!(captures.len(), 2 * count);
});
}
#[test]
fn test_query_start_byte_for_pattern() {
let language = get_language("javascript");
let patterns_1 = r#"
@ -703,18 +770,21 @@ fn test_query_pattern_after_source_byte() {
"*" @operator
"=" @operator
"=>" @operator
"#.trim_start();
"#
.trim_start();
let patterns_2 = "
(identifier) @a
(string) @b
".trim_start();
"
.trim_start();
let patterns_3 = "
((identifier) @b (match? @b i))
(function_declaration name: (identifier) @c)
(method_definition name: (identifier) @d)
".trim_start();
"
.trim_start();
let mut source = String::new();
source += patterns_1;
@ -725,7 +795,10 @@ fn test_query_pattern_after_source_byte() {
assert_eq!(query.start_byte_for_pattern(0), 0);
assert_eq!(query.start_byte_for_pattern(5), patterns_1.len());
assert_eq!(query.start_byte_for_pattern(7), patterns_1.len() + patterns_2.len());
assert_eq!(
query.start_byte_for_pattern(7),
patterns_1.len() + patterns_2.len()
);
}
#[test]

View file

@ -78,10 +78,10 @@ typedef struct {
typedef struct {
uint16_t start_depth;
uint16_t pattern_index;
uint8_t step_index;
uint8_t capture_count;
uint8_t capture_list_id;
uint8_t consumed_capture_count;
uint16_t step_index;
uint16_t capture_count;
uint16_t capture_list_id;
uint16_t consumed_capture_count;
uint32_t id;
} QueryState;
@ -1083,7 +1083,7 @@ static inline bool ts_query_cursor__advance(TSQueryCursor *self) {
bool later_sibling_can_match = can_have_later_siblings;
if (step->field) {
if (step->field == field_id) {
if (!node_does_match && !can_have_later_siblings_with_this_field) {
if (!can_have_later_siblings_with_this_field) {
later_sibling_can_match = false;
}
} else {

View file

@ -272,7 +272,7 @@ TSFieldId ts_tree_cursor_current_status(
}
}
if (ts_subtree_child_count(*parent_entry->subtree) > entry->child_index) {
if (ts_subtree_child_count(*parent_entry->subtree) > entry->child_index + 1) {
*can_have_later_siblings = true;
}