Lines
100 %
Functions
Branches
//! Extra LET / LET* / DEFVAR / DEFPARAMETER paths beyond the
//! basic runtime-binding tests.
use super::common::{compile_and_validate, compile_expect_error, wrap_with_runtime_ratio};
#[test]
fn let_with_constant_bindings() {
compile_and_validate("(let ((x 1) (y 2)) (+ x y))");
}
fn let_with_multiple_runtime_bindings_in_parallel() {
compile_and_validate(&wrap_with_runtime_ratio(
"(let ((a (+ X 1)) (b (+ X 2))) (+ a b))",
));
fn let_star_sequential_bindings() {
compile_and_validate(&wrap_with_runtime_ratio("(let* ((a X) (b (+ a 1))) b)"));
fn let_missing_body_errors() {
let err = compile_expect_error("(let ((x 1)))");
assert!(err.contains("LET"), "got: {err}");
fn let_star_missing_body_errors() {
let err = compile_expect_error("(let* ((x 1)))");
assert!(err.contains("LET*"), "got: {err}");
fn defvar_constant_init() {
compile_and_validate("(defvar my-var 42) my-var");
fn defvar_with_doc_string() {
compile_and_validate("(defvar pi 314/100 \"approximation\") pi");
fn defparameter_constant_init() {
compile_and_validate("(defparameter the-param 99) the-param");
fn defvar_without_init_compiles() {
compile_and_validate("(defvar my-var)");
fn defparameter_missing_init_errors() {
let err = compile_expect_error("(defparameter the-param)");
assert!(err.contains("DEFPARAMETER"), "got: {err}");
fn defun_with_optional_param() {
compile_and_validate(
"(defun greet (name &optional (suffix \"!\")) (upcase-string name)) (greet \"hi\")",
);
fn defun_with_rest_param() {
compile_and_validate("(defun sum (&rest nums) 0) (sum 1 2 3)");
fn defun_with_keyword_param() {
compile_and_validate("(defun config (&key (level 0)) level) (config :level 5)");
fn defun_with_doc_string() {
compile_and_validate("(defun square (x) \"compute x squared\" (* x x)) (square 3)");
fn nested_let_star() {
"(let* ((a X)) (let* ((b (+ a 1))) (let* ((c (* b 2))) c)))",
/// Regression (AFL): the setf-accumulator runtime-type walk (`binding::infer`)
/// sliced a binder body via `&elems[2..]` / `&elems[3..]` and panicked when a
/// nested binder form was too short to have a body — a malformed `(do)` /
/// `(dolist)` / `(let)` inside an accumulator body. The walk is best-effort, so
/// such a form must surface a structured compile error, never a slice panic.
fn short_nested_binder_in_accumulator_body_does_not_panic() {
for src in [
"(let ((acc 0)) (setf acc (cons 1 acc)) (do))",
"(let ((acc 0)) (setf acc (cons 1 acc)) (dolist))",
"(let ((acc 0)) (setf acc (cons 1 acc)) (let))",
"(let ((r nil)) (setf r (cons 1 r)) (do ((i 0)) ))",
] {
// Must return an Err (caught by `compile_expect_error`) rather than
// panicking — the assertion is simply that the call returns.
let _ = compile_expect_error(src);