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<ComputedExpr, ConstEvalError> {
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<const N: usize, const M: usize>;
impl<const N: usize, const M: usize> Adder<N, M> {
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,
);
}