use llvmkit_asmparser::file_loc::FileLoc;
use llvmkit_asmparser::parse_error::ParseError;
use llvmkit_asmparser::parser;
use llvmkit_ir::{AnyTypeEnum, Module};
const MINIMAL: &str = include_str!("fixtures/facade_minimal.ll");
#[test]
fn parse_assembly_string_round_trips_module() {
parser::parse_assembly_string(MINIMAL, |module, _parsed| {
let printed = format!("{module}");
assert!(printed.contains("target triple = \"x86_64-pc-linux-gnu\""));
assert!(printed.contains("define i32 @main()"));
assert!(printed.contains("ret i32 0"));
})
.expect("facade parse succeeds");
}
#[test]
fn parse_assembly_file_reads_file() {
parser::parse_assembly_file(
concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/fixtures/facade_minimal.ll"
),
|module, _parsed| {
let printed = format!("{module}");
assert!(printed.contains("define i32 @main()"));
assert!(printed.contains("ret i32 0"));
},
)
.expect("facade file parse succeeds");
}
#[test]
fn parse_type_at_beginning_reports_read_count() {
Module::with_new::<_, _, _>("facade_type_prefix", |module| {
let (ty, consumed) = parser::parse_type_at_beginning(b"i32, rest", &module, None)
.expect("type prefix parses");
assert_eq!(consumed, 3);
assert!(matches!(AnyTypeEnum::from(ty), AnyTypeEnum::Int(t) if t.bit_width() == 32));
});
}
#[test]
fn parse_type_requires_end() {
Module::with_new::<_, _, _>("facade_type_eof", |module| {
let err = parser::parse_type(b"i32 trailing", &module, None)
.expect_err("trailing token rejected");
match err {
ParseError::Expected { expected, .. } => assert_eq!(expected, "end of string"),
other => panic!("unexpected error variant: {other:?}"),
}
});
}
#[test]
fn parse_target_extension_type() {
Module::with_new::<_, _, _>("facade_target_ext_type", |module| {
let ty = parser::parse_type(b"target(\"aarch64.svcount\")", &module, None)
.expect("target extension type parses");
assert_eq!(format!("{ty}"), "target(\"aarch64.svcount\")");
assert!(matches!(
AnyTypeEnum::from(ty),
AnyTypeEnum::TargetExt(t) if t.name() == "aarch64.svcount"
));
let with_params = parser::parse_type(b"target(\"spirv.Image\", i32, 7)", &module, None)
.expect("target extension type with parameters parses");
assert_eq!(format!("{with_params}"), "target(\"spirv.Image\", i32, 7)");
});
}
#[test]
fn parse_target_extension_rejects_type_after_integer_param() {
Module::with_new::<_, _, _>("facade_target_ext_bad_param_order", |module| {
let err = parser::parse_type(b"target(\"spirv.Image\", 7, i32)", &module, None)
.expect_err("type parameter after integer parameter is malformed");
match err {
ParseError::Expected { expected, .. } => assert_eq!(expected, "target extension type"),
other => panic!("unexpected error variant: {other:?}"),
}
});
}
#[test]
fn parse_constant_value_uses_slot_mapping() {
Module::with_new::<_, _, _>("facade_constant", |module| {
let i32_ty = module.i32_type().as_type();
let constant =
parser::parse_constant_value(b"42", &module, i32_ty, None).expect("constant parses");
assert_eq!(constant.ty(), i32_ty);
});
}
#[test]
fn parser_context_records_function_block_instruction_locations() {
parser::parse_assembly_with_context(MINIMAL.as_bytes(), |_module, _parsed, context| {
assert!(context.function_at(FileLoc::new(2, 0)).is_some());
assert!(context.block_at(FileLoc::new(3, 0)).is_some());
assert!(context.instruction_at(FileLoc::new(4, 2)).is_some());
})
.expect("context parse succeeds");
}