| name | ruby-prism-api |
| description | Comprehensive API reference for the ruby-prism Rust crate (v1.9.0) used in ruby-fast-cop. MUST consult when implementing new RuboCop cops, debugging Prism-related compilation errors (E0282, E0106, E0599), or working with AST node traversal. Covers node ownership model, BlockNode/CallNode relationship, type inference gotchas, Visit trait patterns, and all 148 node types with their accessors. |
ruby-prism Rust Crate API Reference (v1.9.0)
Critical Rules (Read First)
1. Node Ownership -- No Clone, No Copy
Node<'pr> does NOT implement Clone or Copy. All 148 variants are struct wrappers around raw pointers with PhantomData lifetime markers.
let saved = node.clone();
fn get_first<'a>(stmts: &StatementsNode<'a>) -> &Node<'a> {
let items: Vec<_> = stmts.body().iter().collect();
&items[0]
}
fn extract_call<'a>(node: &Node<'a>) -> Option<CallNode<'a>> {
node.as_call_node()
}
fn base_receiver_matches(node: &Node, pred: impl Fn(&Node) -> bool) -> bool {
match node {
Node::CallNode { .. } => {
let call = node.as_call_node().unwrap();
match call.receiver() {
Some(recv) => base_receiver_matches(&recv, pred),
None => pred(node),
}
}
_ => pred(node),
}
}
fn walk_chain(node: &Node) -> usize {
if let Some(call) = node.as_call_node() {
if let Some(recv) = call.receiver() {
return 1 + walk_chain(&recv);
}
}
1
}
2. BlockNode/CallNode Relationship
In Prism, BlockNode is a child of CallNode. This is opposite to other Ruby parsers.
let call = block_node.call();
let block: Option<Node> = call_node.block();
if call_node.block().is_some() { }
3. Type Inference Gotchas (E0282)
Prism's generic return types cause Rust type inference failures with closures:
let has_args = call.arguments().map_or(false, |args| args.arguments().iter().count() > 0);
let msg = call.message_loc().is_some_and(|loc| loc.as_slice() == b"foo");
let has_args = if let Some(args) = call.arguments() {
let arg_list: Vec<_> = args.arguments().iter().collect();
!arg_list.is_empty()
} else {
false
};
if let Some(msg_loc) = call.message_loc() {
let name = msg_loc.as_slice();
}
4. NodeList Iteration
NodeList.iter() yields owned Node<'pr> values. Collect to Vec for multi-pass or indexing:
let items: Vec<_> = stmts.body().iter().collect();
for node in stmts.body().iter() { }
5. Names Are Bytes
Node names return ConstantId, not &str:
let method_name = String::from_utf8_lossy(call.name().as_slice());
let method_str: &str = method_name.as_ref();
if method_str == "puts" { }
6. Location API
Locations provide byte offsets only -- no line/column:
let loc = node.location();
let start: usize = loc.start_offset();
let end: usize = loc.end_offset();
let bytes: &[u8] = loc.as_slice();
let source_text: &str = &ctx.source[start..end];
call.message_loc()
call.call_operator_loc()
call.opening_loc()
call.closing_loc()
def_node.name_loc()
let combined: Option<Location> = loc1.join(&loc2);
Visit Trait Pattern
use ruby_prism::Visit;
struct MyVisitor<'a> {
ctx: &'a CheckContext<'a>,
offenses: Vec<Offense>,
}
impl Visit<'_> for MyVisitor<'_> {
fn visit_call_node(&mut self, node: &ruby_prism::CallNode) {
ruby_prism::visit_call_node(self, node);
}
fn visit_def_node(&mut self, _node: &ruby_prism::DefNode) {
}
fn visit_block_node(&mut self, node: &ruby_prism::BlockNode) {
self.depth += 1;
ruby_prism::visit_block_node(self, node);
self.depth -= 1;
}
}
let mut visitor = MyVisitor { ctx: &ctx, offenses: vec![] };
visitor.visit(&parse_result.node());
Node Matching Patterns
match node {
Node::CallNode { .. } => {
let call = node.as_call_node().unwrap();
}
Node::IfNode { .. } => { }
_ => {}
}
if let Some(call) = node.as_call_node() {
}
if matches!(node, Node::TrueNode { .. } | Node::FalseNode { .. }) {
}
Detailed Node References
Anti-Patterns Checklist
Before submitting code, verify:
- No
block_node.call() -- use call_node.block() instead
- No
node.clone() expecting deep copy -- restructure to avoid
- No
&Node returned from locally-collected Vec
- No
.map_or() / .is_some_and() on Prism Optional returns with closures
- No loops with
node = next_node -- use recursion
- All NodeList access collected to Vec before indexing
- Names converted via
String::from_utf8_lossy(x.name().as_slice())