use base_db::fixture::WithFixture; use hir_def::{db::DefDatabase, expr::Literal}; use crate::{consteval::ComputedExpr, db::HirDatabase, test_db::TestDB}; use super::ConstEvalError; fn check_fail(ra_fixture: &str, error: ConstEvalError) { assert_eq!(eval_goal(ra_fixture), Err(error)); } fn check_number(ra_fixture: &str, answer: i128) { let r = eval_goal(ra_fixture).unwrap(); match r { ComputedExpr::Literal(Literal::Int(r, _)) => assert_eq!(r, answer), ComputedExpr::Literal(Literal::Uint(r, _)) => assert_eq!(r, answer as u128), x => panic!("Expected number but found {:?}", x), } } fn eval_goal(ra_fixture: &str) -> Result { let (db, file_id) = TestDB::with_single_file(ra_fixture); let module_id = db.module_for_file(file_id); let def_map = module_id.def_map(&db); let scope = &def_map[module_id.local_id].scope; let const_id = scope .declarations() .into_iter() .find_map(|x| match x { hir_def::ModuleDefId::ConstId(x) => { if db.const_data(x).name.as_ref()?.to_string() == "GOAL" { Some(x) } else { None } } _ => None, }) .unwrap(); db.const_eval(const_id) } #[test] fn add() { check_number(r#"const GOAL: usize = 2 + 2;"#, 4); } #[test] fn bit_op() { check_number(r#"const GOAL: u8 = !0 & !(!0 >> 1)"#, 128); check_number(r#"const GOAL: i8 = !0 & !(!0 >> 1)"#, 0); // FIXME: rustc evaluate this to -128 check_fail( r#"const GOAL: i8 = 1 << 7"#, ConstEvalError::Panic("attempt to run invalid arithmetic operation".to_string()), ); check_fail( r#"const GOAL: i8 = 1 << 8"#, ConstEvalError::Panic("attempt to run invalid arithmetic operation".to_string()), ); } #[test] fn locals() { check_number( r#" const GOAL: usize = { let a = 3 + 2; let b = a * a; b }; "#, 25, ); } #[test] fn consts() { check_number( r#" const F1: i32 = 1; const F3: i32 = 3 * F2; const F2: i32 = 2 * F1; const GOAL: i32 = F3; "#, 6, ); } #[test] fn const_loop() { check_fail( r#" const F1: i32 = 1 * F3; const F3: i32 = 3 * F2; const F2: i32 = 2 * F1; const GOAL: i32 = F3; "#, ConstEvalError::Loop, ); } #[test] fn const_impl_assoc() { check_number( r#" struct U5; impl U5 { const VAL: usize = 5; } const GOAL: usize = U5::VAL; "#, 5, ); } #[test] fn const_generic_subst() { // FIXME: this should evaluate to 5 check_fail( r#" struct Adder; impl Adder { const VAL: usize = N + M; } const GOAL: usize = Adder::<2, 3>::VAL; "#, ConstEvalError::NotSupported("const generic without substitution"), ); } #[test] fn const_trait_assoc() { // FIXME: this should evaluate to 0 check_fail( r#" struct U0; trait ToConst { const VAL: usize; } impl ToConst for U0 { const VAL: usize = 0; } const GOAL: usize = U0::VAL; "#, ConstEvalError::IncompleteExpr, ); }