Home > Back-end >  How to assert that code won't compile in a test?
How to assert that code won't compile in a test?

Time:01-03

I'm currently writing some unsafe code that requires some degree of manually managing lifetimes, and exposing those lifetimes to external users. I've been writing tests, and while it's pretty easy to write tests to verify the correctness of code that compiles (at least to the extent that the potential for undefined behavior allows), I've been looking to ensure certain types of unsound code can't compile.

Say I have the following contrived example:

pub struct MyBox<T> {
    ptr: *mut T,
}

impl<T> MyBox<T> {
    fn new(t: T) -> Self {
        MyBox {
            ptr: Box::into_raw(Box::new(t)),
        }
    }
    fn get(&self) -> &T {
        unsafe { &*self.ptr }
    }
}
impl<T> Drop for MyBox<T> {
    fn drop(&mut self) {
        unsafe { Box::from_raw(self.ptr) }
    }
}

fn test<'a>() -> &'a i32 {
    MyBox::new(7).get()
}

How can I ensure that the test function continues to fail to compile from rust's test framework?

Obviously, just throwing the erroneous code into a separate file and using the actual test function as glorified build script works, but that gets really boilerplatey and awkward, especially when there are more than just one or two bad examples I would like to test. If it helps, I don't care about the specifics of the error, just that an error exists.

CodePudding user response:

You can write it as a doctest with compile_fail attr so that you can write multiple tests that expect to fail at the same time in a comment.

/// ```compile_fail,E0515
/// use path_to::MyBox;
///
/// fn test<'a>() -> &'a i32 {
///     MyBox::new(7).get()
/// }
/// ```
fn _doc_test() {}

,E0515 indicates the error number you expect, which avoids any other errors that would make the test false negative. (This error number check will be ignored if cargo is not running as nightly)

Note that this requires your crate to be a lib instead of a bin, as bin is not currently supported by doctests. (issue #50784)

CodePudding user response:

You can write your test function in a doc-test with the compile_fail annotation:

pub struct MyBox<T> {
    ptr: *mut T,
}

/// ```compile_fail
/// fn test<'a>() -> &'a i32 {
///     playground::MyBox::new(7).get()
/// }
/// ```
impl<T> MyBox<T> {
    fn new(t: T) -> Self {
        MyBox {
            ptr: Box::into_raw(Box::new(t)),
        }
    }
    fn get(&self) -> &T {
        unsafe { &*self.ptr }
    }
}
impl<T> Drop for MyBox<T> {
    fn drop(&mut self) {
        unsafe { Box::from_raw(self.ptr) }
    }
}

Playground

Note though that you can only test public functions that way.

  • Related