fix(lib): correct escape detection for invalid anonymous nodes

The current quotation escape checker fails in the case that
there is an anonymous node that is just an escaped backslash (it thinks
the backslash escapes the quote, when really it is just an escaped
backslash itself. See the added test case for an example of this).

This commit ensures the node identification logic keeps track of the
number of backslashes seen so it can accurately determine if the
quotation is escaped or not.
This commit is contained in:
Riley Bruins 2024-11-15 20:49:06 -08:00 committed by GitHub
parent 0f7d888883
commit fa6c1471ef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 46 additions and 19 deletions

View file

@ -201,6 +201,36 @@ fn test_query_errors_on_invalid_symbols() {
allocations::record(|| {
let language = get_language("javascript");
assert_eq!(
Query::new(&language, "\">>>>\"").unwrap_err(),
QueryError {
row: 0,
offset: 1,
column: 1,
kind: QueryErrorKind::NodeType,
message: ">>>>".to_string()
}
);
assert_eq!(
Query::new(&language, "\"te\\\"st\"").unwrap_err(),
QueryError {
row: 0,
offset: 1,
column: 1,
kind: QueryErrorKind::NodeType,
message: "te\\\"st".to_string()
}
);
assert_eq!(
Query::new(&language, "\"\\\\\" @cap").unwrap_err(),
QueryError {
row: 0,
offset: 1,
column: 1,
kind: QueryErrorKind::NodeType,
message: "\\\\".to_string()
}
);
assert_eq!(
Query::new(&language, "(clas)").unwrap_err(),
QueryError {

View file

@ -2353,27 +2353,24 @@ impl Query {
ffi::TSQueryErrorNodeType | ffi::TSQueryErrorField | ffi::TSQueryErrorCapture => {
let suffix = source.split_at(offset).1;
let in_quotes = source.as_bytes()[offset - 1] == b'"';
let mut end_offset = suffix.len();
if let Some(pos) = suffix
.char_indices()
.take_while(|(_, c)| *c != '\n')
.find_map(|(i, c)| match c {
'"' if in_quotes
&& i > 0
&& suffix.chars().nth(i - 1) != Some('\\') =>
{
Some(i)
let mut backslashes = 0;
let end_offset = suffix
.find(|c| {
if in_quotes {
if c == '"' && backslashes % 2 == 0 {
true
} else if c == '\\' {
backslashes += 1;
false
} else {
backslashes = 0;
false
}
} else {
!char::is_alphanumeric(c) && c != '_' && c != '-'
}
c if !in_quotes
&& (c.is_whitespace() || c == '(' || c == ')' || c == ':') =>
{
Some(i)
}
_ => None,
})
{
end_offset = pos;
}
.unwrap_or(suffix.len());
message = suffix.split_at(end_offset).0.to_string();
kind = match error_type {
ffi::TSQueryErrorNodeType => QueryErrorKind::NodeType,