use llmcc_core::ir::HirId;
use llmcc_core::symbol::Symbol;
use llmcc_rust::{bind_symbols, build_llmcc_ir, collect_symbols, CompileCtxt, LangRust};
fn compile(
source: &str,
) -> (
&'static CompileCtxt<'static>,
llmcc_core::context::CompileUnit<'static>,
llmcc_rust::CollectionResult,
) {
let sources = vec![source.as_bytes().to_vec()];
let cc = Box::leak(Box::new(CompileCtxt::from_sources::<LangRust>(&sources)));
let unit = cc.compile_unit(0);
build_llmcc_ir::<LangRust>(cc).unwrap();
let globals = cc.create_globals();
let collection = collect_symbols(unit, globals);
bind_symbols(unit, globals);
(cc, unit, collection)
}
fn find_struct<'a>(
collection: &'a llmcc_rust::CollectionResult,
name: &str,
) -> &'a llmcc_rust::StructDescriptor {
collection
.structs
.iter()
.find(|desc| desc.name == name)
.unwrap()
}
fn find_function<'a>(
collection: &'a llmcc_rust::CollectionResult,
name: &str,
) -> &'a llmcc_rust::FunctionDescriptor {
collection
.functions
.iter()
.find(|desc| desc.name == name)
.unwrap()
}
fn find_enum<'a>(
collection: &'a llmcc_rust::CollectionResult,
name: &str,
) -> &'a llmcc_rust::EnumDescriptor {
collection
.enums
.iter()
.find(|desc| desc.name == name)
.unwrap()
}
fn struct_symbol(
unit: llmcc_core::context::CompileUnit<'static>,
collection: &llmcc_rust::CollectionResult,
name: &str,
) -> &'static Symbol {
let desc = find_struct(collection, name);
symbol(unit, desc.hir_id)
}
fn function_symbol(
unit: llmcc_core::context::CompileUnit<'static>,
collection: &llmcc_rust::CollectionResult,
name: &str,
) -> &'static Symbol {
let desc = find_function(collection, name);
symbol(unit, desc.hir_id)
}
fn enum_symbol(
unit: llmcc_core::context::CompileUnit<'static>,
collection: &llmcc_rust::CollectionResult,
name: &str,
) -> &'static Symbol {
let desc = find_enum(collection, name);
symbol(unit, desc.hir_id)
}
fn symbol(unit: llmcc_core::context::CompileUnit<'static>, hir_id: HirId) -> &'static Symbol {
unit.get_scope(hir_id).symbol().unwrap()
}
fn assert_depends_on(symbol: &Symbol, target: &Symbol) {
assert!(symbol.depends.borrow().iter().any(|id| *id == target.id));
}
fn assert_relation(dependent: &Symbol, dependency: &Symbol) {
assert_depends_on(dependent, dependency);
}
#[test]
fn let_with_explicit_type_creates_dependency() {
let source = r#"
struct ProjectGraph;
fn process_graph() {
let pg: ProjectGraph = ProjectGraph;
}
"#;
let (_, unit, collection) = compile(source);
let process_fn = function_symbol(unit, &collection, "process_graph");
let pg_struct = struct_symbol(unit, &collection, "ProjectGraph");
assert_relation(process_fn, pg_struct);
}
#[test]
fn multiple_let_declarations_with_different_types() {
let source = r#"
struct Config;
struct Logger;
fn initialize() {
let cfg: Config = Config;
let log: Logger = Logger;
}
"#;
let (_, unit, collection) = compile(source);
let init_fn = function_symbol(unit, &collection, "initialize");
let config_struct = struct_symbol(unit, &collection, "Config");
let logger_struct = struct_symbol(unit, &collection, "Logger");
assert_relation(init_fn, config_struct);
assert_relation(init_fn, logger_struct);
}
#[test]
fn let_without_type_annotation_still_works() {
let source = r#"
fn simple() {
let x = 5;
}
"#;
let (_, unit, collection) = compile(source);
let simple_fn = function_symbol(unit, &collection, "simple");
let _deps = simple_fn.depends.borrow();
}
#[test]
fn let_with_mutable_and_type_annotation() {
let source = r#"
struct Buffer;
fn allocate() {
let mut buf: Buffer = Buffer;
}
"#;
let (_, unit, collection) = compile(source);
let allocate_fn = function_symbol(unit, &collection, "allocate");
let buffer_struct = struct_symbol(unit, &collection, "Buffer");
assert_relation(allocate_fn, buffer_struct);
}
#[test]
fn nested_let_declarations_in_block() {
let source = r#"
struct Outer;
struct Inner;
fn nested() {
{
let outer: Outer = Outer;
{
let inner: Inner = Inner;
}
}
}
"#;
let (_, unit, collection) = compile(source);
let nested_fn = function_symbol(unit, &collection, "nested");
let outer_struct = struct_symbol(unit, &collection, "Outer");
let inner_struct = struct_symbol(unit, &collection, "Inner");
assert_relation(nested_fn, outer_struct);
assert_relation(nested_fn, inner_struct);
}
#[test]
fn let_with_reference_type() {
let source = r#"
struct Data;
fn borrow_data() {
let data_ref: &Data = &Data;
}
"#;
let (_, unit, collection) = compile(source);
let borrow_fn = function_symbol(unit, &collection, "borrow_data");
let data_struct = struct_symbol(unit, &collection, "Data");
assert_relation(borrow_fn, data_struct);
}
#[test]
fn let_in_function_parameter_scope() {
let source = r#"
struct State;
fn with_param(s: State) {
let local: State = s;
}
"#;
let (_, unit, collection) = compile(source);
let with_param_fn = function_symbol(unit, &collection, "with_param");
let state_struct = struct_symbol(unit, &collection, "State");
assert_relation(with_param_fn, state_struct);
}
#[test]
fn multiple_functions_each_with_let_types() {
let source = r#"
struct TypeA;
struct TypeB;
fn func_a() {
let a: TypeA = TypeA;
}
fn func_b() {
let b: TypeB = TypeB;
}
"#;
let (_, unit, collection) = compile(source);
let func_a = function_symbol(unit, &collection, "func_a");
let func_b = function_symbol(unit, &collection, "func_b");
let type_a = struct_symbol(unit, &collection, "TypeA");
let type_b = struct_symbol(unit, &collection, "TypeB");
assert_relation(func_a, type_a);
assert_relation(func_b, type_b);
}
#[test]
fn let_with_enum_type() {
let source = r#"
enum Status {
Active,
Inactive,
}
fn check_status() {
let s: Status = Status::Active;
}
"#;
let (_, unit, collection) = compile(source);
let check_fn = function_symbol(unit, &collection, "check_status");
let status_enum = enum_symbol(unit, &collection, "Status");
assert_relation(check_fn, status_enum);
}
#[test]
fn let_in_nested_scope_from_match() {
let source = r#"
struct Result;
fn process() {
match Some(0) {
Some(_) => {
let res: Result = Result;
}
None => {}
}
}
"#;
let (_, unit, collection) = compile(source);
let process_fn = function_symbol(unit, &collection, "process");
let result_struct = struct_symbol(unit, &collection, "Result");
assert_relation(process_fn, result_struct);
}
#[test]
fn let_without_explicit_type_struct_constructor() {
let source = r#"
struct Config;
impl Config {
fn new() -> Config {
Config
}
}
fn setup() {
let cfg = Config::new();
}
"#;
let (_, unit, collection) = compile(source);
let setup_fn = function_symbol(unit, &collection, "setup");
let config_struct = struct_symbol(unit, &collection, "Config");
assert_relation(setup_fn, config_struct);
}
#[test]
fn let_without_explicit_type_method_call() {
let source = r#"
struct Builder;
impl Builder {
fn new() -> Builder {
Builder
}
}
fn create() {
let builder = Builder::new();
}
"#;
let (_, unit, collection) = compile(source);
let builder_struct = struct_symbol(unit, &collection, "Builder");
let create_fn = function_symbol(unit, &collection, "create");
assert_relation(create_fn, builder_struct);
}
#[test]
fn multiple_let_without_types_from_different_sources() {
let source = r#"
struct Factory;
impl Factory {
fn new() -> Factory {
Factory
}
}
fn work() {
let factory = Factory::new();
}
"#;
let (_, unit, collection) = compile(source);
let work_fn = function_symbol(unit, &collection, "work");
let factory_struct = struct_symbol(unit, &collection, "Factory");
assert_relation(work_fn, factory_struct);
}
#[test]
fn let_inferred_type_from_function_return() {
let source = r#"
struct Value;
fn create_value() -> Value {
Value
}
fn use_value() {
let v = create_value();
}
"#;
let (_, unit, collection) = compile(source);
let use_value_fn = function_symbol(unit, &collection, "use_value");
let create_value_fn = function_symbol(unit, &collection, "create_value");
assert_relation(use_value_fn, create_value_fn);
}
#[test]
fn let_with_tuple_initializer() {
let source = r#"
struct A;
struct B;
impl A {
fn new() -> A {
A
}
}
impl B {
fn create() -> B {
B
}
}
fn combine() {
let a = A::new();
let b = B::create();
}
"#;
let (_, unit, collection) = compile(source);
let combine_fn = function_symbol(unit, &collection, "combine");
let struct_a = struct_symbol(unit, &collection, "A");
let struct_b = struct_symbol(unit, &collection, "B");
assert_relation(combine_fn, struct_a);
assert_relation(combine_fn, struct_b);
}
#[test]
fn let_with_array_of_struct() {
let source = r#"
struct Item;
impl Item {
fn default() -> Item {
Item
}
}
fn items() {
let list = Item::default();
}
"#;
let (_, unit, collection) = compile(source);
let items_fn = function_symbol(unit, &collection, "items");
let item_struct = struct_symbol(unit, &collection, "Item");
assert_relation(items_fn, item_struct);
}
#[test]
fn let_with_method_chain() {
let source = r#"
struct Builder;
impl Builder {
fn new() -> Builder {
Builder
}
fn configure(self) -> Builder {
self
}
}
fn setup() {
let configured = Builder::new().configure();
}
"#;
let (_, unit, collection) = compile(source);
let setup_fn = function_symbol(unit, &collection, "setup");
let builder_struct = struct_symbol(unit, &collection, "Builder");
assert_relation(setup_fn, builder_struct);
}