use controlled_option::ControlledOption;
use libc::c_char;
use stack_graphs::arena::Handle;
use stack_graphs::c::sg_file_handle;
use stack_graphs::c::sg_node;
use stack_graphs::c::sg_node_handle;
use stack_graphs::c::sg_node_id;
use stack_graphs::c::sg_node_kind;
use stack_graphs::c::sg_node_source_info;
use stack_graphs::c::sg_nodes;
use stack_graphs::c::sg_source_info;
use stack_graphs::c::sg_span;
use stack_graphs::c::sg_stack_graph;
use stack_graphs::c::sg_stack_graph_add_files;
use stack_graphs::c::sg_stack_graph_add_source_infos;
use stack_graphs::c::sg_stack_graph_add_strings;
use stack_graphs::c::sg_stack_graph_add_symbols;
use stack_graphs::c::sg_stack_graph_free;
use stack_graphs::c::sg_stack_graph_get_or_create_nodes;
use stack_graphs::c::sg_stack_graph_new;
use stack_graphs::c::sg_stack_graph_nodes;
use stack_graphs::c::sg_stack_graph_source_infos;
use stack_graphs::c::sg_string_handle;
use stack_graphs::c::sg_symbol_handle;
use stack_graphs::c::SG_JUMP_TO_NODE_HANDLE;
use stack_graphs::c::SG_JUMP_TO_NODE_ID;
use stack_graphs::c::SG_NULL_HANDLE;
use stack_graphs::c::SG_ROOT_NODE_HANDLE;
use stack_graphs::c::SG_ROOT_NODE_ID;
use stack_graphs::graph::Node;
use stack_graphs::graph::NodeID;
fn node_id(file: sg_file_handle, local_id: u32) -> NodeID {
NodeID::new_in_file(unsafe { std::mem::transmute(file) }, local_id)
}
fn add_file(graph: *mut sg_stack_graph, filename: &str) -> sg_file_handle {
let lengths = [filename.len()];
let mut handles: [sg_file_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_add_files(
graph,
1,
filename.as_bytes().as_ptr() as *const c_char,
lengths.as_ptr(),
handles.as_mut_ptr(),
);
assert!(handles[0] != SG_NULL_HANDLE);
handles[0]
}
fn add_symbol(graph: *mut sg_stack_graph, symbol: &str) -> sg_symbol_handle {
let lengths = [symbol.len()];
let mut handles: [sg_symbol_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_add_symbols(
graph,
1,
symbol.as_bytes().as_ptr() as *const c_char,
lengths.as_ptr(),
handles.as_mut_ptr(),
);
assert!(handles[0] != SG_NULL_HANDLE);
handles[0]
}
fn add_string(graph: *mut sg_stack_graph, string: &str) -> sg_string_handle {
let lengths = [string.len()];
let mut handles: [sg_string_handle; 1] = [0; 1];
sg_stack_graph_add_strings(
graph,
1,
string.as_bytes().as_ptr() as *const c_char,
lengths.as_ptr(),
handles.as_mut_ptr(),
);
assert!(handles[0] != 0);
handles[0]
}
#[test]
#[allow(unused_assignments)]
fn verify_null_node_representation() {
let bytes = [0x55u8; std::mem::size_of::<Handle<Node>>()];
let mut rust: ControlledOption<Handle<Node>> = unsafe { std::mem::transmute(bytes) };
rust = ControlledOption::none();
let c: sg_node_handle = unsafe { std::mem::transmute(rust) };
assert_eq!(c, SG_NULL_HANDLE);
}
fn jump_to_node() -> sg_node {
sg_node {
kind: sg_node_kind::SG_NODE_KIND_JUMP_TO,
id: sg_node_id {
file: SG_NULL_HANDLE,
local_id: SG_JUMP_TO_NODE_ID,
},
symbol: SG_NULL_HANDLE,
is_endpoint: false,
scope: sg_node_id::default(),
}
}
fn root_node() -> sg_node {
sg_node {
kind: sg_node_kind::SG_NODE_KIND_ROOT,
id: sg_node_id {
file: SG_NULL_HANDLE,
local_id: SG_ROOT_NODE_ID,
},
symbol: SG_NULL_HANDLE,
is_endpoint: false,
scope: sg_node_id::default(),
}
}
fn get_node(arena: &sg_nodes, handle: sg_node_handle) -> &Node {
assert!(handle != SG_NULL_HANDLE);
let slice = unsafe { std::slice::from_raw_parts(arena.nodes as *const Node, arena.count) };
&slice[handle as usize]
}
#[test]
fn cannot_add_singleton_nodes() {
let graph = sg_stack_graph_new();
let nodes = [root_node(), jump_to_node()];
let mut handles: [sg_node_handle; 2] = [SG_NULL_HANDLE; 2];
sg_stack_graph_get_or_create_nodes(graph, nodes.len(), nodes.as_ptr(), handles.as_mut_ptr());
assert!(handles.iter().all(|h| *h == SG_NULL_HANDLE));
sg_stack_graph_free(graph);
}
#[test]
fn can_dereference_singleton_nodes() {
let graph = sg_stack_graph_new();
let node_arena = sg_stack_graph_nodes(graph);
assert!(get_node(&node_arena, SG_ROOT_NODE_HANDLE).is_root());
assert!(get_node(&node_arena, SG_JUMP_TO_NODE_HANDLE).is_jump_to());
sg_stack_graph_free(graph);
}
#[test]
fn singleton_nodes_have_correct_ids() {
let graph = sg_stack_graph_new();
let arena = sg_stack_graph_nodes(graph);
let slice = unsafe { std::slice::from_raw_parts(arena.nodes, arena.count) };
let root = &slice[SG_ROOT_NODE_HANDLE as usize];
assert!(root.id.file == SG_NULL_HANDLE);
assert!(root.id.local_id == SG_ROOT_NODE_ID);
let jump_to = &slice[SG_JUMP_TO_NODE_HANDLE as usize];
assert!(jump_to.id.file == SG_NULL_HANDLE);
assert!(jump_to.id.local_id == SG_JUMP_TO_NODE_ID);
sg_stack_graph_free(graph);
}
fn drop_scopes(file: sg_file_handle, local_id: u32) -> sg_node {
sg_node {
kind: sg_node_kind::SG_NODE_KIND_DROP_SCOPES,
id: sg_node_id { file, local_id },
symbol: SG_NULL_HANDLE,
is_endpoint: false,
scope: sg_node_id::default(),
}
}
#[test]
fn can_add_drop_scopes_node() {
let graph = sg_stack_graph_new();
let file = add_file(graph, "test.py");
let nodes = [drop_scopes(file, 42)];
let mut handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(graph, nodes.len(), nodes.as_ptr(), handles.as_mut_ptr());
let node_arena = sg_stack_graph_nodes(graph);
let node = get_node(&node_arena, handles[0]);
assert!(matches!(node, Node::DropScopes(_)));
assert!(node.id() == node_id(file, 42));
let mut new_handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(
graph,
nodes.len(),
nodes.as_ptr(),
new_handles.as_mut_ptr(),
);
assert!(handles == new_handles);
sg_stack_graph_free(graph);
}
#[test]
fn drop_scopes_cannot_have_symbol() {
let graph = sg_stack_graph_new();
let file = add_file(graph, "test.py");
let symbol = add_symbol(graph, "a");
let mut nodes = [drop_scopes(file, 42)];
nodes[0].symbol = symbol;
let mut handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(graph, nodes.len(), nodes.as_ptr(), handles.as_mut_ptr());
assert!(handles[0] == SG_NULL_HANDLE);
sg_stack_graph_free(graph);
}
#[test]
fn drop_scopes_cannot_have_scope() {
let graph = sg_stack_graph_new();
let file = add_file(graph, "test.py");
let mut nodes = [drop_scopes(file, 42)];
nodes[0].scope = sg_node_id {
file: SG_NULL_HANDLE,
local_id: SG_JUMP_TO_NODE_ID,
};
let mut handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(graph, nodes.len(), nodes.as_ptr(), handles.as_mut_ptr());
assert!(handles[0] == SG_NULL_HANDLE);
sg_stack_graph_free(graph);
}
fn exported_scope(file: sg_file_handle, local_id: u32) -> sg_node {
sg_node {
kind: sg_node_kind::SG_NODE_KIND_SCOPE,
id: sg_node_id { file, local_id },
symbol: SG_NULL_HANDLE,
is_endpoint: true,
scope: sg_node_id::default(),
}
}
#[test]
fn can_add_exported_scope_node() {
let graph = sg_stack_graph_new();
let file = add_file(graph, "test.py");
let nodes = [exported_scope(file, 42)];
let mut handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(graph, nodes.len(), nodes.as_ptr(), handles.as_mut_ptr());
let node_arena = sg_stack_graph_nodes(graph);
let node = get_node(&node_arena, handles[0]);
assert!(matches!(node, Node::Scope(_)));
assert!(node.is_exported_scope());
assert!(node.id() == node_id(file, 42));
let mut new_handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(
graph,
nodes.len(),
nodes.as_ptr(),
new_handles.as_mut_ptr(),
);
assert!(handles == new_handles);
sg_stack_graph_free(graph);
}
#[test]
fn exported_scope_cannot_have_symbol() {
let graph = sg_stack_graph_new();
let file = add_file(graph, "test.py");
let symbol = add_symbol(graph, "a");
let mut nodes = [exported_scope(file, 42)];
nodes[0].symbol = symbol;
let mut handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(graph, nodes.len(), nodes.as_ptr(), handles.as_mut_ptr());
assert!(handles[0] == SG_NULL_HANDLE);
sg_stack_graph_free(graph);
}
#[test]
fn exported_scope_cannot_have_scope() {
let graph = sg_stack_graph_new();
let file = add_file(graph, "test.py");
let mut nodes = [exported_scope(file, 42)];
nodes[0].scope = sg_node_id {
file: SG_NULL_HANDLE,
local_id: SG_JUMP_TO_NODE_ID,
};
let mut handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(graph, nodes.len(), nodes.as_ptr(), handles.as_mut_ptr());
assert!(handles[0] == SG_NULL_HANDLE);
sg_stack_graph_free(graph);
}
fn internal_scope(file: sg_file_handle, local_id: u32) -> sg_node {
sg_node {
kind: sg_node_kind::SG_NODE_KIND_SCOPE,
id: sg_node_id { file, local_id },
symbol: SG_NULL_HANDLE,
is_endpoint: false,
scope: sg_node_id::default(),
}
}
#[test]
fn can_add_internal_scope_node() {
let graph = sg_stack_graph_new();
let file = add_file(graph, "test.py");
let nodes = [internal_scope(file, 42)];
let mut handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(graph, nodes.len(), nodes.as_ptr(), handles.as_mut_ptr());
let node_arena = sg_stack_graph_nodes(graph);
let node = get_node(&node_arena, handles[0]);
assert!(matches!(node, Node::Scope(_)));
assert!(!node.is_exported_scope());
assert!(node.id() == node_id(file, 42));
let mut new_handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(
graph,
nodes.len(),
nodes.as_ptr(),
new_handles.as_mut_ptr(),
);
assert!(handles == new_handles);
sg_stack_graph_free(graph);
}
#[test]
fn internal_scope_cannot_have_symbol() {
let graph = sg_stack_graph_new();
let file = add_file(graph, "test.py");
let symbol = add_symbol(graph, "a");
let mut nodes = [internal_scope(file, 42)];
nodes[0].symbol = symbol;
let mut handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(graph, nodes.len(), nodes.as_ptr(), handles.as_mut_ptr());
assert!(handles[0] == SG_NULL_HANDLE);
sg_stack_graph_free(graph);
}
#[test]
fn internal_scope_cannot_have_scope() {
let graph = sg_stack_graph_new();
let file = add_file(graph, "test.py");
let mut nodes = [internal_scope(file, 42)];
nodes[0].scope = sg_node_id {
file: SG_NULL_HANDLE,
local_id: SG_JUMP_TO_NODE_ID,
};
let mut handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(graph, nodes.len(), nodes.as_ptr(), handles.as_mut_ptr());
assert!(handles[0] == SG_NULL_HANDLE);
sg_stack_graph_free(graph);
}
fn pop_scoped_symbol(file: sg_file_handle, local_id: u32, symbol: sg_symbol_handle) -> sg_node {
sg_node {
kind: sg_node_kind::SG_NODE_KIND_POP_SCOPED_SYMBOL,
id: sg_node_id { file, local_id },
symbol,
is_endpoint: true,
scope: sg_node_id::default(),
}
}
#[test]
fn can_add_pop_scoped_symbol_node() {
let graph = sg_stack_graph_new();
let file = add_file(graph, "test.py");
let symbol = add_symbol(graph, "a");
let nodes = [pop_scoped_symbol(file, 42, symbol)];
let mut handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(graph, nodes.len(), nodes.as_ptr(), handles.as_mut_ptr());
let node_arena = sg_stack_graph_nodes(graph);
let node = get_node(&node_arena, handles[0]);
assert!(matches!(node, Node::PopScopedSymbol(_)));
assert!(node.id() == node_id(file, 42));
assert!(node.symbol().unwrap().as_usize() == symbol as usize);
assert!(node.is_definition());
let mut new_handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(
graph,
nodes.len(),
nodes.as_ptr(),
new_handles.as_mut_ptr(),
);
assert!(handles == new_handles);
sg_stack_graph_free(graph);
}
#[test]
fn pop_scoped_symbol_must_have_symbol() {
let graph = sg_stack_graph_new();
let file = add_file(graph, "test.py");
let nodes = [pop_scoped_symbol(file, 42, SG_NULL_HANDLE)];
let mut handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(graph, nodes.len(), nodes.as_ptr(), handles.as_mut_ptr());
assert!(handles[0] == SG_NULL_HANDLE);
sg_stack_graph_free(graph);
}
#[test]
fn pop_scoped_symbol_cannot_have_scope() {
let graph = sg_stack_graph_new();
let file = add_file(graph, "test.py");
let symbol = add_symbol(graph, "a");
let mut nodes = [pop_scoped_symbol(file, 42, symbol)];
nodes[0].scope = sg_node_id {
file: SG_NULL_HANDLE,
local_id: SG_JUMP_TO_NODE_ID,
};
let mut handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(graph, nodes.len(), nodes.as_ptr(), handles.as_mut_ptr());
assert!(handles[0] == SG_NULL_HANDLE);
sg_stack_graph_free(graph);
}
fn pop_symbol(file: sg_file_handle, local_id: u32, symbol: sg_symbol_handle) -> sg_node {
sg_node {
kind: sg_node_kind::SG_NODE_KIND_POP_SYMBOL,
id: sg_node_id { file, local_id },
symbol,
is_endpoint: true,
scope: sg_node_id::default(),
}
}
#[test]
fn can_add_pop_symbol_node() {
let graph = sg_stack_graph_new();
let file = add_file(graph, "test.py");
let symbol = add_symbol(graph, "a");
let nodes = [pop_symbol(file, 42, symbol)];
let mut handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(graph, nodes.len(), nodes.as_ptr(), handles.as_mut_ptr());
let node_arena = sg_stack_graph_nodes(graph);
let node = get_node(&node_arena, handles[0]);
assert!(matches!(node, Node::PopSymbol(_)));
assert!(node.id() == node_id(file, 42));
assert!(node.symbol().unwrap().as_usize() == symbol as usize);
assert!(node.is_definition());
let mut new_handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(
graph,
nodes.len(),
nodes.as_ptr(),
new_handles.as_mut_ptr(),
);
assert!(handles == new_handles);
sg_stack_graph_free(graph);
}
#[test]
fn pop_symbol_must_have_symbol() {
let graph = sg_stack_graph_new();
let file = add_file(graph, "test.py");
let nodes = [pop_symbol(file, 42, SG_NULL_HANDLE)];
let mut handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(graph, nodes.len(), nodes.as_ptr(), handles.as_mut_ptr());
assert!(handles[0] == SG_NULL_HANDLE);
sg_stack_graph_free(graph);
}
#[test]
fn pop_symbol_cannot_have_scope() {
let graph = sg_stack_graph_new();
let file = add_file(graph, "test.py");
let symbol = add_symbol(graph, "a");
let mut nodes = [pop_symbol(file, 42, symbol)];
nodes[0].scope = sg_node_id {
file: SG_NULL_HANDLE,
local_id: SG_JUMP_TO_NODE_ID,
};
let mut handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(graph, nodes.len(), nodes.as_ptr(), handles.as_mut_ptr());
assert!(handles[0] == SG_NULL_HANDLE);
sg_stack_graph_free(graph);
}
fn push_scoped_symbol(
file: sg_file_handle,
local_id: u32,
symbol: sg_symbol_handle,
scope_file: sg_file_handle,
scope_id: u32,
) -> sg_node {
let scope = sg_node_id {
file: scope_file,
local_id: scope_id,
};
sg_node {
kind: sg_node_kind::SG_NODE_KIND_PUSH_SCOPED_SYMBOL,
id: sg_node_id { file, local_id },
symbol,
is_endpoint: true,
scope,
}
}
#[test]
fn can_add_push_scoped_symbol_node() {
let graph = sg_stack_graph_new();
let file = add_file(graph, "test.py");
let symbol = add_symbol(graph, "a");
let nodes = [push_scoped_symbol(file, 42, symbol, 0, SG_JUMP_TO_NODE_ID)];
let mut handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(graph, nodes.len(), nodes.as_ptr(), handles.as_mut_ptr());
let node_arena = sg_stack_graph_nodes(graph);
let node = get_node(&node_arena, handles[0]);
assert!(matches!(node, Node::PushScopedSymbol(_)));
assert!(node.id() == node_id(file, 42));
assert!(node.symbol().unwrap().as_usize() == symbol as usize);
assert!(node.scope().unwrap().is_jump_to());
assert!(node.is_reference());
let mut new_handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(
graph,
nodes.len(),
nodes.as_ptr(),
new_handles.as_mut_ptr(),
);
assert!(handles == new_handles);
sg_stack_graph_free(graph);
}
#[test]
fn push_scoped_symbol_must_have_symbol() {
let graph = sg_stack_graph_new();
let file = add_file(graph, "test.py");
let nodes = [push_scoped_symbol(
file,
42,
SG_NULL_HANDLE,
SG_NULL_HANDLE,
SG_JUMP_TO_NODE_ID,
)];
let mut handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(graph, nodes.len(), nodes.as_ptr(), handles.as_mut_ptr());
assert!(handles[0] == SG_NULL_HANDLE);
sg_stack_graph_free(graph);
}
#[test]
fn push_scoped_symbol_must_have_scope() {
let graph = sg_stack_graph_new();
let file = add_file(graph, "test.py");
let symbol = add_symbol(graph, "a");
let nodes = [push_scoped_symbol(
file,
42,
symbol,
SG_NULL_HANDLE,
SG_NULL_HANDLE,
)];
let mut handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(graph, nodes.len(), nodes.as_ptr(), handles.as_mut_ptr());
assert!(handles[0] == SG_NULL_HANDLE);
sg_stack_graph_free(graph);
}
fn push_symbol(file: sg_file_handle, local_id: u32, symbol: sg_symbol_handle) -> sg_node {
sg_node {
kind: sg_node_kind::SG_NODE_KIND_PUSH_SYMBOL,
id: sg_node_id { file, local_id },
symbol,
is_endpoint: true,
scope: sg_node_id::default(),
}
}
#[test]
fn can_add_push_symbol_node() {
let graph = sg_stack_graph_new();
let file = add_file(graph, "test.py");
let symbol = add_symbol(graph, "a");
let nodes = [push_symbol(file, 42, symbol)];
let mut handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(graph, nodes.len(), nodes.as_ptr(), handles.as_mut_ptr());
let node_arena = sg_stack_graph_nodes(graph);
let node = get_node(&node_arena, handles[0]);
assert!(matches!(node, Node::PushSymbol(_)));
assert!(node.id() == node_id(file, 42));
assert!(node.symbol().unwrap().as_usize() == symbol as usize);
assert!(node.is_reference());
let mut new_handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(
graph,
nodes.len(),
nodes.as_ptr(),
new_handles.as_mut_ptr(),
);
assert!(handles == new_handles);
sg_stack_graph_free(graph);
}
#[test]
fn push_symbol_must_have_symbol() {
let graph = sg_stack_graph_new();
let file = add_file(graph, "test.py");
let nodes = [push_symbol(file, 42, SG_NULL_HANDLE)];
let mut handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(graph, nodes.len(), nodes.as_ptr(), handles.as_mut_ptr());
assert!(handles[0] == SG_NULL_HANDLE);
sg_stack_graph_free(graph);
}
#[test]
fn push_symbol_cannot_have_scope() {
let graph = sg_stack_graph_new();
let file = add_file(graph, "test.py");
let symbol = add_symbol(graph, "a");
let mut nodes = [push_symbol(file, 42, symbol)];
nodes[0].scope = sg_node_id {
file: SG_NULL_HANDLE,
local_id: SG_JUMP_TO_NODE_ID,
};
let mut handles: [sg_node_handle; 1] = [SG_NULL_HANDLE; 1];
sg_stack_graph_get_or_create_nodes(graph, nodes.len(), nodes.as_ptr(), handles.as_mut_ptr());
assert!(handles[0] == SG_NULL_HANDLE);
sg_stack_graph_free(graph);
}
fn get_source_info(graph: *const sg_stack_graph, node: sg_node_handle) -> Option<sg_source_info> {
let infos = sg_stack_graph_source_infos(graph);
let infos = unsafe { std::slice::from_raw_parts(infos.infos, infos.count) };
infos.get(node as usize).copied()
}
#[test]
fn can_create_source_info() {
let graph = sg_stack_graph_new();
let file = add_file(graph, "test.py");
let nodes = [
exported_scope(file, 0),
exported_scope(file, 1),
exported_scope(file, 2),
];
let mut handles: [sg_node_handle; 3] = [0; 3];
sg_stack_graph_get_or_create_nodes(graph, nodes.len(), nodes.as_ptr(), handles.as_mut_ptr());
let syntax_type = add_string(graph, "function");
let containing_line = add_string(graph, "def foo():");
let fully_qualified_name = add_string(graph, "bar.foo");
let mut infos = [sg_node_source_info {
node: handles[1],
source_info: sg_source_info {
span: sg_span::default(),
syntax_type,
containing_line,
definiens_span: sg_span::default(),
fully_qualified_name,
},
}];
infos[0].source_info.span.start.line = 17;
infos[0].source_info.span.end.column.utf8_offset = 23;
sg_stack_graph_add_source_infos(graph, infos.len(), infos.as_ptr());
assert!(get_source_info(graph, handles[0]).is_some());
assert!(get_source_info(graph, handles[1]).is_some());
assert!(get_source_info(graph, handles[2]).is_none());
let actual = get_source_info(graph, handles[1]).unwrap();
assert_eq!(actual.syntax_type, syntax_type);
assert_eq!(actual.containing_line, containing_line);
assert_eq!(actual.span.start.line, 17);
assert_eq!(actual.span.end.column.utf8_offset, 23);
sg_stack_graph_free(graph);
}