Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions rust/ruby-rbs/src/node/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ pub fn parse(rbs_code: &[u8]) -> Result<SignatureNode<'_>, String> {
unsafe {
let start_ptr = rbs_code.as_ptr() as *const i8;
let end_ptr = start_ptr.add(rbs_code.len());
let char_count = std::str::from_utf8(rbs_code)
.map(|s| s.chars().count())
.unwrap_or(rbs_code.len()) as i32;

let raw_rbs_string_value = rbs_string_new(start_ptr, end_ptr);

let encoding_ptr = &rbs_encodings[RBS_ENCODING_UTF_8 as usize] as *const rbs_encoding_t;
let parser = rbs_parser_new(raw_rbs_string_value, encoding_ptr, 0, rbs_code.len() as i32);
let parser = rbs_parser_new(raw_rbs_string_value, encoding_ptr, 0, char_count);

let mut signature: *mut rbs_signature_t = std::ptr::null_mut();
let result = rbs_parse_signature(parser, &mut signature);
Expand All @@ -34,7 +37,18 @@ pub fn parse(rbs_code: &[u8]) -> Result<SignatureNode<'_>, String> {
if result {
Ok(signature_node)
} else {
Err(String::from("Failed to parse RBS signature"))
let error_message = (*parser)
.error
.as_ref()
.filter(|error| !error.message.is_null())
.map(|error| {
std::ffi::CStr::from_ptr(error.message)
.to_string_lossy()
.into_owned()
})
.unwrap_or_else(|| String::from("Failed to parse RBS signature"));

Err(error_message)
}
}
}
Expand Down Expand Up @@ -267,6 +281,14 @@ impl SymbolNode<'_> {
mod tests {
use super::*;

#[test]
fn test_parse_error_contains_actual_message() {
let rbs_code = "class { end";
let result = parse(rbs_code.as_bytes());
let error_message = result.unwrap_err();
assert_eq!(error_message, "expected one of class/module/constant name");
}

#[test]
fn test_parse() {
let rbs_code = r#"type foo = "hello""#;
Expand Down
47 changes: 47 additions & 0 deletions rust/ruby-rbs/tests/sanity.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use std::path::Path;

use ruby_rbs::node::parse;

fn collect_rbs_files(dir: &Path) -> Vec<std::path::PathBuf> {
let mut files = Vec::new();

for entry in std::fs::read_dir(dir).unwrap() {
let entry = entry.unwrap();
let path = entry.path();

if path.is_dir() {
files.extend(collect_rbs_files(&path));
} else if path.extension().is_some_and(|ext| ext == "rbs") {
files.push(path);
}
}

files
}

#[test]
fn all_included_rbs_can_be_parsed() {
let repo_root = Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
let dirs = [repo_root.join("core"), repo_root.join("stdlib")];

let mut files: Vec<_> = dirs.iter().flat_map(|d| collect_rbs_files(d)).collect();
files.sort();
assert!(!files.is_empty());

let mut failures = Vec::new();

for file in &files {
let content = std::fs::read_to_string(file).unwrap();

if let Err(e) = parse(content.as_bytes()) {
failures.push(format!("{}: {}", file.display(), e));
}
}

assert!(
failures.is_empty(),
"Failed to parse {} RBS file(s):\n{}",
failures.len(),
failures.join("\n")
);
}