Merge pull request #2538 from amaanq/unsafety
fix(safety): mark functions that potentially deref a raw pointer as unsafe
This commit is contained in:
commit
62823fc333
4 changed files with 308 additions and 142 deletions
|
|
@ -496,11 +496,13 @@ fn test_highlighting_via_c_api() {
|
|||
.iter()
|
||||
.map(|h| h.as_bytes().as_ptr() as *const c_char)
|
||||
.collect::<Vec<_>>();
|
||||
let highlighter = c::ts_highlighter_new(
|
||||
&highlight_names[0] as *const *const c_char,
|
||||
&highlight_attrs[0] as *const *const c_char,
|
||||
highlights.len() as u32,
|
||||
);
|
||||
let highlighter = unsafe {
|
||||
c::ts_highlighter_new(
|
||||
&highlight_names[0] as *const *const c_char,
|
||||
&highlight_attrs[0] as *const *const c_char,
|
||||
highlights.len() as u32,
|
||||
)
|
||||
};
|
||||
|
||||
let source_code = c_string("<script>\nconst a = b('c');\nc.d();\n</script>");
|
||||
|
||||
|
|
@ -512,20 +514,22 @@ fn test_highlighting_via_c_api() {
|
|||
let highlights_query = fs::read_to_string(queries.join("highlights.scm")).unwrap();
|
||||
let injections_query = fs::read_to_string(queries.join("injections.scm")).unwrap();
|
||||
let locals_query = fs::read_to_string(queries.join("locals.scm")).unwrap();
|
||||
c::ts_highlighter_add_language(
|
||||
highlighter,
|
||||
lang_name.as_ptr(),
|
||||
js_scope.as_ptr(),
|
||||
js_injection_regex.as_ptr(),
|
||||
language,
|
||||
highlights_query.as_ptr() as *const c_char,
|
||||
injections_query.as_ptr() as *const c_char,
|
||||
locals_query.as_ptr() as *const c_char,
|
||||
highlights_query.len() as u32,
|
||||
injections_query.len() as u32,
|
||||
locals_query.len() as u32,
|
||||
false,
|
||||
);
|
||||
unsafe {
|
||||
c::ts_highlighter_add_language(
|
||||
highlighter,
|
||||
lang_name.as_ptr(),
|
||||
js_scope.as_ptr(),
|
||||
js_injection_regex.as_ptr(),
|
||||
language,
|
||||
highlights_query.as_ptr() as *const c_char,
|
||||
injections_query.as_ptr() as *const c_char,
|
||||
locals_query.as_ptr() as *const c_char,
|
||||
highlights_query.len() as u32,
|
||||
injections_query.len() as u32,
|
||||
locals_query.len() as u32,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
let html_scope = c_string("text.html.basic");
|
||||
let html_injection_regex = c_string("^html");
|
||||
|
|
@ -534,36 +538,40 @@ fn test_highlighting_via_c_api() {
|
|||
let queries = get_language_queries_path("html");
|
||||
let highlights_query = fs::read_to_string(queries.join("highlights.scm")).unwrap();
|
||||
let injections_query = fs::read_to_string(queries.join("injections.scm")).unwrap();
|
||||
c::ts_highlighter_add_language(
|
||||
highlighter,
|
||||
lang_name.as_ptr(),
|
||||
html_scope.as_ptr(),
|
||||
html_injection_regex.as_ptr(),
|
||||
language,
|
||||
highlights_query.as_ptr() as *const c_char,
|
||||
injections_query.as_ptr() as *const c_char,
|
||||
ptr::null(),
|
||||
highlights_query.len() as u32,
|
||||
injections_query.len() as u32,
|
||||
0,
|
||||
false,
|
||||
);
|
||||
unsafe {
|
||||
c::ts_highlighter_add_language(
|
||||
highlighter,
|
||||
lang_name.as_ptr(),
|
||||
html_scope.as_ptr(),
|
||||
html_injection_regex.as_ptr(),
|
||||
language,
|
||||
highlights_query.as_ptr() as *const c_char,
|
||||
injections_query.as_ptr() as *const c_char,
|
||||
ptr::null(),
|
||||
highlights_query.len() as u32,
|
||||
injections_query.len() as u32,
|
||||
0,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
let buffer = c::ts_highlight_buffer_new();
|
||||
|
||||
c::ts_highlighter_highlight(
|
||||
highlighter,
|
||||
html_scope.as_ptr(),
|
||||
source_code.as_ptr(),
|
||||
source_code.as_bytes().len() as u32,
|
||||
buffer,
|
||||
ptr::null_mut(),
|
||||
);
|
||||
unsafe {
|
||||
c::ts_highlighter_highlight(
|
||||
highlighter,
|
||||
html_scope.as_ptr(),
|
||||
source_code.as_ptr(),
|
||||
source_code.as_bytes().len() as u32,
|
||||
buffer,
|
||||
ptr::null_mut(),
|
||||
);
|
||||
}
|
||||
|
||||
let output_bytes = c::ts_highlight_buffer_content(buffer);
|
||||
let output_line_offsets = c::ts_highlight_buffer_line_offsets(buffer);
|
||||
let output_len = c::ts_highlight_buffer_len(buffer);
|
||||
let output_line_count = c::ts_highlight_buffer_line_count(buffer);
|
||||
let output_bytes = unsafe { c::ts_highlight_buffer_content(buffer) };
|
||||
let output_line_offsets = unsafe { c::ts_highlight_buffer_line_offsets(buffer) };
|
||||
let output_len = unsafe { c::ts_highlight_buffer_len(buffer) };
|
||||
let output_line_count = unsafe { c::ts_highlight_buffer_line_count(buffer) };
|
||||
|
||||
let output_bytes = unsafe { slice::from_raw_parts(output_bytes, output_len as usize) };
|
||||
let output_line_offsets =
|
||||
|
|
@ -589,8 +597,10 @@ fn test_highlighting_via_c_api() {
|
|||
]
|
||||
);
|
||||
|
||||
c::ts_highlighter_delete(highlighter);
|
||||
c::ts_highlight_buffer_delete(buffer);
|
||||
unsafe {
|
||||
c::ts_highlighter_delete(highlighter);
|
||||
c::ts_highlight_buffer_delete(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use std::{
|
|||
use tree_sitter::Point;
|
||||
use tree_sitter_tags::{c_lib as c, Error, TagsConfiguration, TagsContext};
|
||||
|
||||
const PYTHON_TAG_QUERY: &'static str = r#"
|
||||
const PYTHON_TAG_QUERY: &str = r#"
|
||||
(
|
||||
(function_definition
|
||||
name: (identifier) @name
|
||||
|
|
@ -39,7 +39,7 @@ const PYTHON_TAG_QUERY: &'static str = r#"
|
|||
attribute: (identifier) @name)) @reference.call
|
||||
"#;
|
||||
|
||||
const JS_TAG_QUERY: &'static str = r#"
|
||||
const JS_TAG_QUERY: &str = r#"
|
||||
(
|
||||
(comment)* @doc .
|
||||
(class_declaration
|
||||
|
|
@ -68,7 +68,7 @@ const JS_TAG_QUERY: &'static str = r#"
|
|||
function: (identifier) @name) @reference.call
|
||||
"#;
|
||||
|
||||
const RUBY_TAG_QUERY: &'static str = r#"
|
||||
const RUBY_TAG_QUERY: &str = r#"
|
||||
(method
|
||||
name: (_) @name) @definition.method
|
||||
|
||||
|
|
@ -359,25 +359,29 @@ fn test_tags_via_c_api() {
|
|||
);
|
||||
|
||||
let c_scope_name = CString::new(scope_name).unwrap();
|
||||
let result = c::ts_tagger_add_language(
|
||||
tagger,
|
||||
c_scope_name.as_ptr(),
|
||||
language,
|
||||
JS_TAG_QUERY.as_ptr(),
|
||||
ptr::null(),
|
||||
JS_TAG_QUERY.len() as u32,
|
||||
0,
|
||||
);
|
||||
let result = unsafe {
|
||||
c::ts_tagger_add_language(
|
||||
tagger,
|
||||
c_scope_name.as_ptr(),
|
||||
language,
|
||||
JS_TAG_QUERY.as_ptr(),
|
||||
ptr::null(),
|
||||
JS_TAG_QUERY.len() as u32,
|
||||
0,
|
||||
)
|
||||
};
|
||||
assert_eq!(result, c::TSTagsError::Ok);
|
||||
|
||||
let result = c::ts_tagger_tag(
|
||||
tagger,
|
||||
c_scope_name.as_ptr(),
|
||||
source_code.as_ptr(),
|
||||
source_code.len() as u32,
|
||||
buffer,
|
||||
ptr::null(),
|
||||
);
|
||||
let result = unsafe {
|
||||
c::ts_tagger_tag(
|
||||
tagger,
|
||||
c_scope_name.as_ptr(),
|
||||
source_code.as_ptr(),
|
||||
source_code.len() as u32,
|
||||
buffer,
|
||||
ptr::null(),
|
||||
)
|
||||
};
|
||||
assert_eq!(result, c::TSTagsError::Ok);
|
||||
let tags = unsafe {
|
||||
slice::from_raw_parts(
|
||||
|
|
@ -419,8 +423,10 @@ fn test_tags_via_c_api() {
|
|||
]
|
||||
);
|
||||
|
||||
c::ts_tags_buffer_delete(buffer);
|
||||
c::ts_tagger_delete(tagger);
|
||||
unsafe {
|
||||
c::ts_tags_buffer_delete(buffer);
|
||||
c::ts_tagger_delete(tagger);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,23 +32,27 @@ pub enum ErrorCode {
|
|||
InvalidLanguageName,
|
||||
}
|
||||
|
||||
/// Create a new [`TSHighlighter`] instance.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that the `highlight_names` and `attribute_strings` arrays are valid for
|
||||
/// the lifetime of the returned [`TSHighlighter`] instance, and are non-null.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ts_highlighter_new(
|
||||
pub unsafe extern "C" fn ts_highlighter_new(
|
||||
highlight_names: *const *const c_char,
|
||||
attribute_strings: *const *const c_char,
|
||||
highlight_count: u32,
|
||||
) -> *mut TSHighlighter {
|
||||
let highlight_names =
|
||||
unsafe { slice::from_raw_parts(highlight_names, highlight_count as usize) };
|
||||
let attribute_strings =
|
||||
unsafe { slice::from_raw_parts(attribute_strings, highlight_count as usize) };
|
||||
let highlight_names = slice::from_raw_parts(highlight_names, highlight_count as usize);
|
||||
let attribute_strings = slice::from_raw_parts(attribute_strings, highlight_count as usize);
|
||||
let highlight_names = highlight_names
|
||||
.into_iter()
|
||||
.map(|s| unsafe { CStr::from_ptr(*s).to_string_lossy().to_string() })
|
||||
.iter()
|
||||
.map(|s| CStr::from_ptr(*s).to_string_lossy().to_string())
|
||||
.collect::<Vec<_>>();
|
||||
let attribute_strings = attribute_strings
|
||||
.into_iter()
|
||||
.map(|s| unsafe { CStr::from_ptr(*s).to_bytes() })
|
||||
.iter()
|
||||
.map(|s| CStr::from_ptr(*s).to_bytes())
|
||||
.collect();
|
||||
let carriage_return_index = highlight_names.iter().position(|s| s == "carriage-return");
|
||||
Box::into_raw(Box::new(TSHighlighter {
|
||||
|
|
@ -59,8 +63,19 @@ pub extern "C" fn ts_highlighter_new(
|
|||
}))
|
||||
}
|
||||
|
||||
/// Add a language to a [`TSHighlighter`] instance.
|
||||
///
|
||||
/// Returns an [`ErrorCode`] indicating whether the language was added successfully or not.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `this` must be non-null and must be a valid pointer to a [`TSHighlighter`] instance
|
||||
/// created by [`ts_highlighter_new`].
|
||||
///
|
||||
/// The caller must ensure that any `*const c_char` (C-style string) parameters are valid for the lifetime of
|
||||
/// the [`TSHighlighter`] instance, and are non-null.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ts_highlighter_add_language(
|
||||
pub unsafe extern "C" fn ts_highlighter_add_language(
|
||||
this: *mut TSHighlighter,
|
||||
language_name: *const c_char,
|
||||
scope_name: *const c_char,
|
||||
|
|
@ -76,7 +91,7 @@ pub extern "C" fn ts_highlighter_add_language(
|
|||
) -> ErrorCode {
|
||||
let f = move || {
|
||||
let this = unwrap_mut_ptr(this);
|
||||
let scope_name = unsafe { CStr::from_ptr(scope_name) };
|
||||
let scope_name = CStr::from_ptr(scope_name);
|
||||
let scope_name = scope_name
|
||||
.to_str()
|
||||
.or(Err(ErrorCode::InvalidUtf8))?
|
||||
|
|
@ -84,35 +99,32 @@ pub extern "C" fn ts_highlighter_add_language(
|
|||
let injection_regex = if injection_regex.is_null() {
|
||||
None
|
||||
} else {
|
||||
let pattern = unsafe { CStr::from_ptr(injection_regex) };
|
||||
let pattern = CStr::from_ptr(injection_regex);
|
||||
let pattern = pattern.to_str().or(Err(ErrorCode::InvalidUtf8))?;
|
||||
Some(Regex::new(pattern).or(Err(ErrorCode::InvalidRegex))?)
|
||||
};
|
||||
|
||||
let highlight_query = unsafe {
|
||||
slice::from_raw_parts(highlight_query as *const u8, highlight_query_len as usize)
|
||||
};
|
||||
let highlight_query =
|
||||
slice::from_raw_parts(highlight_query as *const u8, highlight_query_len as usize);
|
||||
|
||||
let highlight_query = str::from_utf8(highlight_query).or(Err(ErrorCode::InvalidUtf8))?;
|
||||
|
||||
let injection_query = if injection_query_len > 0 {
|
||||
let query = unsafe {
|
||||
slice::from_raw_parts(injection_query as *const u8, injection_query_len as usize)
|
||||
};
|
||||
let query =
|
||||
slice::from_raw_parts(injection_query as *const u8, injection_query_len as usize);
|
||||
str::from_utf8(query).or(Err(ErrorCode::InvalidUtf8))?
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
let locals_query = if locals_query_len > 0 {
|
||||
let query = unsafe {
|
||||
slice::from_raw_parts(locals_query as *const u8, locals_query_len as usize)
|
||||
};
|
||||
let query = slice::from_raw_parts(locals_query as *const u8, locals_query_len as usize);
|
||||
str::from_utf8(query).or(Err(ErrorCode::InvalidUtf8))?
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
let lang = unsafe { CStr::from_ptr(language_name) }
|
||||
let lang = CStr::from_ptr(language_name)
|
||||
.to_str()
|
||||
.or(Err(ErrorCode::InvalidLanguageName))?;
|
||||
|
||||
|
|
@ -125,7 +137,7 @@ pub extern "C" fn ts_highlighter_add_language(
|
|||
apply_all_captures,
|
||||
)
|
||||
.or(Err(ErrorCode::InvalidQuery))?;
|
||||
config.configure(&this.highlight_names.as_slice());
|
||||
config.configure(this.highlight_names.as_slice());
|
||||
this.languages.insert(scope_name, (injection_regex, config));
|
||||
|
||||
Ok(())
|
||||
|
|
@ -145,42 +157,102 @@ pub extern "C" fn ts_highlight_buffer_new() -> *mut TSHighlightBuffer {
|
|||
}))
|
||||
}
|
||||
|
||||
/// Deletes a [`TSHighlighter`] instance.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `this` must be non-null and must be a valid pointer to a [`TSHighlighter`] instance
|
||||
/// created by [`ts_highlighter_new`].
|
||||
///
|
||||
/// It cannot be used after this function is called.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ts_highlighter_delete(this: *mut TSHighlighter) {
|
||||
drop(unsafe { Box::from_raw(this) })
|
||||
pub unsafe extern "C" fn ts_highlighter_delete(this: *mut TSHighlighter) {
|
||||
drop(Box::from_raw(this))
|
||||
}
|
||||
|
||||
/// Deletes a [`TSHighlightBuffer`] instance.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `this` must be non-null and must be a valid pointer to a [`TSHighlightBuffer`] instance
|
||||
/// created by [`ts_highlight_buffer_new`]
|
||||
///
|
||||
/// It cannot be used after this function is called.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ts_highlight_buffer_delete(this: *mut TSHighlightBuffer) {
|
||||
drop(unsafe { Box::from_raw(this) })
|
||||
pub unsafe extern "C" fn ts_highlight_buffer_delete(this: *mut TSHighlightBuffer) {
|
||||
drop(Box::from_raw(this))
|
||||
}
|
||||
|
||||
/// Get the HTML content of a [`TSHighlightBuffer`] instance as a raw pointer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `this` must be non-null and must be a valid pointer to a [`TSHighlightBuffer`] instance
|
||||
/// created by [`ts_highlight_buffer_new`].
|
||||
///
|
||||
/// The returned pointer, a C-style string, must not outlive the [`TSHighlightBuffer`] instance, else the
|
||||
/// data will point to garbage.
|
||||
///
|
||||
/// To get the length of the HTML content, use [`ts_highlight_buffer_len`].
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ts_highlight_buffer_content(this: *const TSHighlightBuffer) -> *const u8 {
|
||||
pub unsafe extern "C" fn ts_highlight_buffer_content(this: *const TSHighlightBuffer) -> *const u8 {
|
||||
let this = unwrap_ptr(this);
|
||||
this.renderer.html.as_slice().as_ptr()
|
||||
}
|
||||
|
||||
/// Get the line offsets of a [`TSHighlightBuffer`] instance as a C-style array.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `this` must be non-null and must be a valid pointer to a [`TSHighlightBuffer`] instance
|
||||
/// created by [`ts_highlight_buffer_new`].
|
||||
///
|
||||
/// The returned pointer, a C-style array of [`u32`]s, must not outlive the [`TSHighlightBuffer`] instance, else the
|
||||
/// data will point to garbage.
|
||||
///
|
||||
/// To get the length of the array, use [`ts_highlight_buffer_line_count`].
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ts_highlight_buffer_line_offsets(this: *const TSHighlightBuffer) -> *const u32 {
|
||||
pub unsafe extern "C" fn ts_highlight_buffer_line_offsets(
|
||||
this: *const TSHighlightBuffer,
|
||||
) -> *const u32 {
|
||||
let this = unwrap_ptr(this);
|
||||
this.renderer.line_offsets.as_slice().as_ptr()
|
||||
}
|
||||
|
||||
/// Get the length of the HTML content of a [`TSHighlightBuffer`] instance.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `this` must be non-null and must be a valid pointer to a [`TSHighlightBuffer`] instance
|
||||
/// created by [`ts_highlight_buffer_new`].
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ts_highlight_buffer_len(this: *const TSHighlightBuffer) -> u32 {
|
||||
pub unsafe extern "C" fn ts_highlight_buffer_len(this: *const TSHighlightBuffer) -> u32 {
|
||||
let this = unwrap_ptr(this);
|
||||
this.renderer.html.len() as u32
|
||||
}
|
||||
|
||||
/// Get the number of lines in a [`TSHighlightBuffer`] instance.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `this` must be non-null and must be a valid pointer to a [`TSHighlightBuffer`] instance
|
||||
/// created by [`ts_highlight_buffer_new`].
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ts_highlight_buffer_line_count(this: *const TSHighlightBuffer) -> u32 {
|
||||
pub unsafe extern "C" fn ts_highlight_buffer_line_count(this: *const TSHighlightBuffer) -> u32 {
|
||||
let this = unwrap_ptr(this);
|
||||
this.renderer.line_offsets.len() as u32
|
||||
}
|
||||
|
||||
/// Highlight a string of source code.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that `scope_name`, `source_code`, `output`, and `cancellation_flag` are valid for
|
||||
/// the lifetime of the [`TSHighlighter`] instance, and are non-null.
|
||||
///
|
||||
/// `this` must be a non-null pointer to a [`TSHighlighter`] instance created by [`ts_highlighter_new`]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ts_highlighter_highlight(
|
||||
pub unsafe extern "C" fn ts_highlighter_highlight(
|
||||
this: *const TSHighlighter,
|
||||
scope_name: *const c_char,
|
||||
source_code: *const c_char,
|
||||
|
|
@ -190,10 +262,9 @@ pub extern "C" fn ts_highlighter_highlight(
|
|||
) -> ErrorCode {
|
||||
let this = unwrap_ptr(this);
|
||||
let output = unwrap_mut_ptr(output);
|
||||
let scope_name = unwrap(unsafe { CStr::from_ptr(scope_name).to_str() });
|
||||
let source_code =
|
||||
unsafe { slice::from_raw_parts(source_code as *const u8, source_code_len as usize) };
|
||||
let cancellation_flag = unsafe { cancellation_flag.as_ref() };
|
||||
let scope_name = unwrap(CStr::from_ptr(scope_name).to_str());
|
||||
let source_code = slice::from_raw_parts(source_code as *const u8, source_code_len as usize);
|
||||
let cancellation_flag = cancellation_flag.as_ref();
|
||||
this.highlight(source_code, scope_name, output, cancellation_flag)
|
||||
}
|
||||
|
||||
|
|
@ -238,15 +309,8 @@ impl TSHighlighter {
|
|||
.renderer
|
||||
.render(highlights, source_code, &|s| self.attribute_strings[s.0]);
|
||||
match result {
|
||||
Err(Error::Cancelled) => {
|
||||
return ErrorCode::Timeout;
|
||||
}
|
||||
Err(Error::InvalidLanguage) => {
|
||||
return ErrorCode::InvalidLanguage;
|
||||
}
|
||||
Err(Error::Unknown) => {
|
||||
return ErrorCode::Timeout;
|
||||
}
|
||||
Err(Error::Cancelled) | Err(Error::Unknown) => ErrorCode::Timeout,
|
||||
Err(Error::InvalidLanguage) => ErrorCode::InvalidLanguage,
|
||||
Ok(()) => ErrorCode::Ok,
|
||||
}
|
||||
} else {
|
||||
|
|
@ -255,15 +319,15 @@ impl TSHighlighter {
|
|||
}
|
||||
}
|
||||
|
||||
fn unwrap_ptr<'a, T>(result: *const T) -> &'a T {
|
||||
unsafe { result.as_ref() }.unwrap_or_else(|| {
|
||||
unsafe fn unwrap_ptr<'a, T>(result: *const T) -> &'a T {
|
||||
result.as_ref().unwrap_or_else(|| {
|
||||
eprintln!("{}:{} - pointer must not be null", file!(), line!());
|
||||
abort();
|
||||
})
|
||||
}
|
||||
|
||||
fn unwrap_mut_ptr<'a, T>(result: *mut T) -> &'a mut T {
|
||||
unsafe { result.as_mut() }.unwrap_or_else(|| {
|
||||
unsafe fn unwrap_mut_ptr<'a, T>(result: *mut T) -> &'a mut T {
|
||||
result.as_mut().unwrap_or_else(|| {
|
||||
eprintln!("{}:{} - pointer must not be null", file!(), line!());
|
||||
abort();
|
||||
})
|
||||
|
|
|
|||
|
|
@ -66,13 +66,29 @@ pub extern "C" fn ts_tagger_new() -> *mut TSTagger {
|
|||
}))
|
||||
}
|
||||
|
||||
/// Delete a TSTagger.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `this` must be non-null and a valid pointer to a [`TSTagger`] instance.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ts_tagger_delete(this: *mut TSTagger) {
|
||||
drop(unsafe { Box::from_raw(this) })
|
||||
pub unsafe extern "C" fn ts_tagger_delete(this: *mut TSTagger) {
|
||||
drop(Box::from_raw(this))
|
||||
}
|
||||
|
||||
/// Add a language to a TSTagger.
|
||||
///
|
||||
/// Returns a [`TSTagsError`] indicating whether the operation was successful or not.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `this` must be non-null and a valid pointer to a [`TSTagger`] instance.
|
||||
/// `scope_name` must be non-null and a valid pointer to a null-terminated string.
|
||||
/// `tags_query` and `locals_query` must be non-null and valid pointers to strings.
|
||||
///
|
||||
/// The caller must ensure that the lengths of `tags_query` and `locals_query` are correct.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ts_tagger_add_language(
|
||||
pub unsafe extern "C" fn ts_tagger_add_language(
|
||||
this: *mut TSTagger,
|
||||
scope_name: *const c_char,
|
||||
language: Language,
|
||||
|
|
@ -82,10 +98,10 @@ pub extern "C" fn ts_tagger_add_language(
|
|||
locals_query_len: u32,
|
||||
) -> TSTagsError {
|
||||
let tagger = unwrap_mut_ptr(this);
|
||||
let scope_name = unsafe { unwrap(CStr::from_ptr(scope_name).to_str()) };
|
||||
let tags_query = unsafe { slice::from_raw_parts(tags_query, tags_query_len as usize) };
|
||||
let locals_query = if locals_query != std::ptr::null() {
|
||||
unsafe { slice::from_raw_parts(locals_query, locals_query_len as usize) }
|
||||
let scope_name = unwrap(CStr::from_ptr(scope_name).to_str());
|
||||
let tags_query = slice::from_raw_parts(tags_query, tags_query_len as usize);
|
||||
let locals_query = if !locals_query.is_null() {
|
||||
slice::from_raw_parts(locals_query, locals_query_len as usize)
|
||||
} else {
|
||||
&[]
|
||||
};
|
||||
|
|
@ -111,8 +127,19 @@ pub extern "C" fn ts_tagger_add_language(
|
|||
}
|
||||
}
|
||||
|
||||
/// Tags some source code.
|
||||
///
|
||||
/// Returns a [`TSTagsError`] indicating whether the operation was successful or not.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `this` must be a non-null valid pointer to a [`TSTagger`] instance.
|
||||
/// `scope_name` must be a non-null valid pointer to a null-terminated string.
|
||||
/// `source_code` must be a non-null valid pointer to a slice of bytes.
|
||||
/// `output` must be a non-null valid pointer to a [`TSTagsBuffer`] instance.
|
||||
/// `cancellation_flag` must be a non-null valid pointer to an [`AtomicUsize`] instance.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ts_tagger_tag(
|
||||
pub unsafe extern "C" fn ts_tagger_tag(
|
||||
this: *mut TSTagger,
|
||||
scope_name: *const c_char,
|
||||
source_code: *const u8,
|
||||
|
|
@ -122,14 +149,14 @@ pub extern "C" fn ts_tagger_tag(
|
|||
) -> TSTagsError {
|
||||
let tagger = unwrap_mut_ptr(this);
|
||||
let buffer = unwrap_mut_ptr(output);
|
||||
let scope_name = unsafe { unwrap(CStr::from_ptr(scope_name).to_str()) };
|
||||
let scope_name = unwrap(CStr::from_ptr(scope_name).to_str());
|
||||
|
||||
if let Some(config) = tagger.languages.get(scope_name) {
|
||||
shrink_and_clear(&mut buffer.tags, BUFFER_TAGS_RESERVE_CAPACITY);
|
||||
shrink_and_clear(&mut buffer.docs, BUFFER_DOCS_RESERVE_CAPACITY);
|
||||
|
||||
let source_code = unsafe { slice::from_raw_parts(source_code, source_code_len as usize) };
|
||||
let cancellation_flag = unsafe { cancellation_flag.as_ref() };
|
||||
let source_code = slice::from_raw_parts(source_code, source_code_len as usize);
|
||||
let cancellation_flag = cancellation_flag.as_ref();
|
||||
|
||||
let tags = match buffer
|
||||
.context
|
||||
|
|
@ -201,49 +228,108 @@ pub extern "C" fn ts_tags_buffer_new() -> *mut TSTagsBuffer {
|
|||
}))
|
||||
}
|
||||
|
||||
/// Delete a TSTagsBuffer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `this` must be non-null and a valid pointer to a [`TSTagsBuffer`] instance created by
|
||||
/// [`ts_tags_buffer_new`].
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ts_tags_buffer_delete(this: *mut TSTagsBuffer) {
|
||||
drop(unsafe { Box::from_raw(this) })
|
||||
pub unsafe extern "C" fn ts_tags_buffer_delete(this: *mut TSTagsBuffer) {
|
||||
drop(Box::from_raw(this))
|
||||
}
|
||||
|
||||
/// Get the tags from a TSTagsBuffer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `this` must be non-null and a valid pointer to a [`TSTagsBuffer`] instance created by
|
||||
/// [`ts_tags_buffer_new`].
|
||||
///
|
||||
/// The caller must ensure that the returned pointer is not used after the [`TSTagsBuffer`]
|
||||
/// is deleted with [`ts_tags_buffer_delete`], else the data will point to garbage.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ts_tags_buffer_tags(this: *const TSTagsBuffer) -> *const TSTag {
|
||||
pub unsafe extern "C" fn ts_tags_buffer_tags(this: *const TSTagsBuffer) -> *const TSTag {
|
||||
let buffer = unwrap_ptr(this);
|
||||
buffer.tags.as_ptr()
|
||||
}
|
||||
|
||||
/// Get the number of tags in a TSTagsBuffer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `this` must be non-null and a valid pointer to a [`TSTagsBuffer`] instance.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ts_tags_buffer_tags_len(this: *const TSTagsBuffer) -> u32 {
|
||||
pub unsafe extern "C" fn ts_tags_buffer_tags_len(this: *const TSTagsBuffer) -> u32 {
|
||||
let buffer = unwrap_ptr(this);
|
||||
buffer.tags.len() as u32
|
||||
}
|
||||
|
||||
/// Get the documentation strings from a TSTagsBuffer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `this` must be non-null and a valid pointer to a [`TSTagsBuffer`] instance created by
|
||||
/// [`ts_tags_buffer_new`].
|
||||
///
|
||||
/// The caller must ensure that the returned pointer is not used after the [`TSTagsBuffer`]
|
||||
/// is deleted with [`ts_tags_buffer_delete`], else the data will point to garbage.
|
||||
///
|
||||
/// The returned pointer points to a C-style string.
|
||||
/// To get the length of the string, use [`ts_tags_buffer_docs_len`].
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ts_tags_buffer_docs(this: *const TSTagsBuffer) -> *const c_char {
|
||||
pub unsafe extern "C" fn ts_tags_buffer_docs(this: *const TSTagsBuffer) -> *const c_char {
|
||||
let buffer = unwrap_ptr(this);
|
||||
buffer.docs.as_ptr() as *const c_char
|
||||
}
|
||||
|
||||
/// Get the length of the documentation strings in a TSTagsBuffer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `this` must be non-null and a valid pointer to a [`TSTagsBuffer`] instance created by
|
||||
/// [`ts_tags_buffer_new`].
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ts_tags_buffer_docs_len(this: *const TSTagsBuffer) -> u32 {
|
||||
pub unsafe extern "C" fn ts_tags_buffer_docs_len(this: *const TSTagsBuffer) -> u32 {
|
||||
let buffer = unwrap_ptr(this);
|
||||
buffer.docs.len() as u32
|
||||
}
|
||||
|
||||
/// Get whether or not a TSTagsBuffer contains any parse errors.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `this` must be non-null and a valid pointer to a [`TSTagsBuffer`] instance created by
|
||||
/// [`ts_tags_buffer_new`].
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ts_tags_buffer_found_parse_error(this: *const TSTagsBuffer) -> bool {
|
||||
pub unsafe extern "C" fn ts_tags_buffer_found_parse_error(this: *const TSTagsBuffer) -> bool {
|
||||
let buffer = unwrap_ptr(this);
|
||||
buffer.errors_present
|
||||
}
|
||||
|
||||
/// Get the syntax kinds for a given scope name.
|
||||
///
|
||||
/// Returns a pointer to a null-terminated array of null-terminated strings.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// `this` must be non-null and a valid pointer to a [`TSTagger`] instance created by
|
||||
/// [`ts_tagger_new`].
|
||||
/// `scope_name` must be non-null and a valid pointer to a null-terminated string.
|
||||
/// `len` must be non-null and a valid pointer to a `u32`.
|
||||
///
|
||||
/// The caller must ensure that the returned pointer is not used after the [`TSTagger`]
|
||||
/// is deleted with [`ts_tagger_delete`], else the data will point to garbage.
|
||||
///
|
||||
/// The returned pointer points to a C-style string array.
|
||||
#[no_mangle]
|
||||
pub extern "C" fn ts_tagger_syntax_kinds_for_scope_name(
|
||||
pub unsafe extern "C" fn ts_tagger_syntax_kinds_for_scope_name(
|
||||
this: *mut TSTagger,
|
||||
scope_name: *const c_char,
|
||||
len: *mut u32,
|
||||
) -> *const *const c_char {
|
||||
let tagger = unwrap_mut_ptr(this);
|
||||
let scope_name = unsafe { unwrap(CStr::from_ptr(scope_name).to_str()) };
|
||||
let scope_name = unwrap(CStr::from_ptr(scope_name).to_str());
|
||||
let len = unwrap_mut_ptr(len);
|
||||
|
||||
*len = 0;
|
||||
|
|
@ -254,15 +340,15 @@ pub extern "C" fn ts_tagger_syntax_kinds_for_scope_name(
|
|||
std::ptr::null()
|
||||
}
|
||||
|
||||
fn unwrap_ptr<'a, T>(result: *const T) -> &'a T {
|
||||
unsafe { result.as_ref() }.unwrap_or_else(|| {
|
||||
unsafe fn unwrap_ptr<'a, T>(result: *const T) -> &'a T {
|
||||
result.as_ref().unwrap_or_else(|| {
|
||||
eprintln!("{}:{} - pointer must not be null", file!(), line!());
|
||||
abort();
|
||||
})
|
||||
}
|
||||
|
||||
fn unwrap_mut_ptr<'a, T>(result: *mut T) -> &'a mut T {
|
||||
unsafe { result.as_mut() }.unwrap_or_else(|| {
|
||||
unsafe fn unwrap_mut_ptr<'a, T>(result: *mut T) -> &'a mut T {
|
||||
result.as_mut().unwrap_or_else(|| {
|
||||
eprintln!("{}:{} - pointer must not be null", file!(), line!());
|
||||
abort();
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue