Merge pull request #2538 from amaanq/unsafety

fix(safety): mark functions that potentially deref a raw pointer as unsafe
This commit is contained in:
Amaan Qureshi 2023-08-19 20:27:30 -04:00 committed by GitHub
commit 62823fc333
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 308 additions and 142 deletions

View file

@ -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]

View file

@ -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);
}
});
}

View file

@ -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();
})

View file

@ -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();
})