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) }
}
}
Note though that you can only test public functions that way.