Merge pull request #2462 from tree-sitter/api-ext-ub

Fix UB for `LookaheadIterator`
This commit is contained in:
Andrew Hlynskyi 2023-08-03 04:37:39 +03:00 committed by GitHub
commit 144c096a27
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 71 additions and 8 deletions

View file

@ -14,3 +14,7 @@ pub mod wasm;
#[cfg(test)]
mod tests;
// To run compile fail tests
#[cfg(doctest)]
mod tests;

View file

@ -27,7 +27,7 @@ fn test_lookahead_iterator() {
assert_ne!(cursor.node().grammar_id(), cursor.node().kind_id());
let expected_symbols = ["identifier", "block_comment", "line_comment"];
let lookahead = language.lookahead_iterator(next_state).unwrap();
let mut lookahead = language.lookahead_iterator(next_state).unwrap();
assert_eq!(lookahead.language(), language);
assert!(lookahead.iter_names().eq(expected_symbols));
@ -39,3 +39,57 @@ fn test_lookahead_iterator() {
.map(|s| language.node_kind_for_id(s).unwrap())
.eq(expected_symbols));
}
#[test]
fn test_lookahead_iterator_modifiable_only_by_mut() {
let mut parser = Parser::new();
let language = get_language("rust");
parser.set_language(language).unwrap();
let tree = parser.parse("struct Stuff {}", None).unwrap();
let mut cursor = tree.walk();
assert!(cursor.goto_first_child()); // struct
assert!(cursor.goto_first_child()); // struct keyword
let next_state = cursor.node().next_parse_state();
assert_ne!(next_state, 0);
let mut lookahead = language.lookahead_iterator(next_state).unwrap();
let _ = lookahead.next();
let mut names = lookahead.iter_names();
let _ = names.next();
}
/// It doesn't allowed to use lookahead iterator by shared ref:
/// error[E0596]: cannot borrow `lookahead` as mutable, as it is not declared as mutable
/// ```compile_fail
/// use tree_sitter::{Parser, Language};
/// let mut parser = Parser::new();
/// let language = unsafe { Language::from_raw(std::ptr::null()) };
/// let tree = parser.parse("", None).unwrap();
/// let mut cursor = tree.walk();
/// let next_state = cursor.node().next_parse_state();
/// let lookahead = language.lookahead_iterator(next_state).unwrap();
/// let _ = lookahead.next();
/// ```
/// It doesn't allowed to use lookahead names iterator by shared ref:
/// error[E0596]: cannot borrow `names` as mutable, as it is not declared as mutable
/// ```compile_fail
/// use tree_sitter::{Parser, Language};
/// let mut parser = Parser::new();
/// let language = unsafe { Language::from_raw(std::ptr::null()) };
/// let tree = parser.parse("", None).unwrap();
/// let mut cursor = tree.walk();
/// let next_state = cursor.node().next_parse_state();
/// if let Some(mut lookahead) = language.lookahead_iterator(next_state) {
/// let _ = lookahead.next();
/// let names = lookahead.iter_names();
/// let _ = names.next();
/// }
/// ```
fn _dummy() {}

View file

@ -90,6 +90,7 @@ pub struct Parser(NonNull<ffi::TSParser>);
/// A stateful object that is used to look up symbols valid in a specific parse state
#[doc(alias = "TSLookaheadIterator")]
pub struct LookaheadIterator(NonNull<ffi::TSLookaheadIterator>);
struct LookaheadNamesIterator<'a>(&'a mut LookaheadIterator);
/// A type of log message.
#[derive(Debug, PartialEq, Eq)]
@ -1525,7 +1526,7 @@ impl LookaheadIterator {
/// This returns `true` if the language was set successfully and `false`
/// otherwise.
#[doc(alias = "ts_lookahead_iterator_reset")]
pub fn reset(&self, language: Language, state: u16) -> bool {
pub fn reset(&mut self, language: Language, state: u16) -> bool {
unsafe { ffi::ts_lookahead_iterator_reset(self.0.as_ptr(), language.0, state) }
}
@ -1534,19 +1535,17 @@ impl LookaheadIterator {
/// This returns `true` if the iterator was reset to the given state and `false`
/// otherwise.
#[doc(alias = "ts_lookahead_iterator_reset_state")]
pub fn reset_state(&self, state: u16) -> bool {
pub fn reset_state(&mut self, state: u16) -> bool {
unsafe { ffi::ts_lookahead_iterator_reset_state(self.0.as_ptr(), state) }
}
/// Iterate symbol names.
pub fn iter_names<'a>(&'a self) -> impl Iterator<Item = &'static str> + 'a {
NameLookaheadIterator(&self)
pub fn iter_names(&mut self) -> impl Iterator<Item = &'static str> + '_ {
LookaheadNamesIterator(self)
}
}
struct NameLookaheadIterator<'a>(&'a LookaheadIterator);
impl<'a> Iterator for NameLookaheadIterator<'a> {
impl Iterator for LookaheadNamesIterator<'_> {
type Item = &'static str;
#[doc(alias = "ts_lookahead_iterator_advance")]
@ -2647,6 +2646,12 @@ unsafe impl Sync for Language {}
unsafe impl Send for Node<'_> {}
unsafe impl Sync for Node<'_> {}
unsafe impl Send for LookaheadIterator {}
unsafe impl Sync for LookaheadIterator {}
unsafe impl Send for LookaheadNamesIterator<'_> {}
unsafe impl Sync for LookaheadNamesIterator<'_> {}
unsafe impl Send for Parser {}
unsafe impl Sync for Parser {}