rust: Create readable messages for query syntax errors

This commit is contained in:
Max Brunsfeld 2019-09-26 15:58:41 -07:00
parent a45dc67390
commit 324c259cbb
2 changed files with 65 additions and 11 deletions

View file

@ -16,37 +16,73 @@ fn test_query_errors_on_invalid_syntax() {
// Mismatched parens
assert_eq!(
Query::new(language, "(if_statement"),
Err(QueryError::Syntax(13))
Err(QueryError::Syntax("Unexpected EOF".to_string()))
);
assert_eq!(
Query::new(language, "(if_statement))"),
Err(QueryError::Syntax(14))
Query::new(language, "; comment 1\n; comment 2\n (if_statement))"),
Err(QueryError::Syntax(
[
" (if_statement))", //
" ^",
]
.join("\n")
))
);
// Return an error at the *beginning* of a bare identifier not followed a colon.
// If there's a colon but no pattern, return an error at the end of the colon.
assert_eq!(
Query::new(language, "(if_statement identifier)"),
Err(QueryError::Syntax(14))
Err(QueryError::Syntax(
[
"(if_statement identifier)", //
" ^",
]
.join("\n")
))
);
assert_eq!(
Query::new(language, "(if_statement condition:)"),
Err(QueryError::Syntax(24))
Err(QueryError::Syntax(
[
"(if_statement condition:)", //
" ^",
]
.join("\n")
))
);
// Return an error at the beginning of an unterminated string.
assert_eq!(
Query::new(language, r#"(identifier) "h "#),
Err(QueryError::Syntax(13))
Err(QueryError::Syntax(
[
r#"(identifier) "h "#, //
r#" ^"#,
]
.join("\n")
))
);
assert_eq!(
Query::new(language, r#"((identifier) ()"#),
Err(QueryError::Syntax(16))
Err(QueryError::Syntax(
[
"((identifier) ()", //
" ^",
]
.join("\n")
))
);
assert_eq!(
Query::new(language, r#"((identifier) @x (eq? @x a"#),
Err(QueryError::Syntax(26))
Err(QueryError::Syntax(
[
r#"((identifier) @x (eq? @x a"#,
r#" ^"#,
]
.join("\n")
))
);
});
}

View file

@ -186,7 +186,7 @@ pub struct QueryCapture<'a> {
#[derive(Debug, PartialEq, Eq)]
pub enum QueryError {
Syntax(usize),
Syntax(String),
NodeType(String),
Field(String),
Capture(String),
@ -997,6 +997,24 @@ impl Query {
// On failure, build an error based on the error code and offset.
if ptr.is_null() {
let offset = error_offset as usize;
let mut line_start = 0;
let line_containing_error = source.split("\n").find_map(|line| {
let line_end = line_start + line.len() + 1;
if line_end > offset {
Some(line)
} else {
line_start = line_end;
None
}
});
let message = if let Some(line) = line_containing_error {
line.to_string() + "\n" + &" ".repeat(offset - line_start) + "^"
} else {
"Unexpected EOF".to_string()
};
// if line_containing_error
return if error_type != ffi::TSQueryError_TSQueryErrorSyntax {
let suffix = source.split_at(offset).1;
let end_offset = suffix
@ -1007,10 +1025,10 @@ impl Query {
ffi::TSQueryError_TSQueryErrorNodeType => Err(QueryError::NodeType(name)),
ffi::TSQueryError_TSQueryErrorField => Err(QueryError::Field(name)),
ffi::TSQueryError_TSQueryErrorCapture => Err(QueryError::Capture(name)),
_ => Err(QueryError::Syntax(offset)),
_ => Err(QueryError::Syntax(message)),
}
} else {
Err(QueryError::Syntax(offset))
Err(QueryError::Syntax(message))
};
}