incomplete/
lib.rs

1//! This crate provides `incomplete!()`, a compile-time checked version of `unimplemented!()`.
2//!
3//! Motivation for and discussion around this macro can be found in the
4//! [rust-internals](https://internals.rust-lang.org/t/compile-time-checked-version-of-unimplemented/4837)
5//! thread, as well as the [RFC issue](https://github.com/rust-lang/rfcs/issues/1911). Some of it
6//! is repeated below.
7//!
8//! We all know and love the `unimplemented!()` macro for keeping track of things we haven't yet
9//! gotten around to. However, it is only checked at runtime if a certain code path is encountered.
10//! This is fine in many cases, such as for corner-cases you don't expect to have to deal with yet.
11//! But when writing new code from scratch (especially when porting), refactoring large amounts of
12//! code, or building comprehensive new features, you often have segments of code that you haven't
13//! implemented yet, **but you know you'll have to**.
14//!
15//! The basic motivation for this macro is to introduce a construct to indicate that a piece of
16//! code *needs* to be filled in (i.e., the program should not compile while it is present), but
17//! that the developer wants to postpone coding up until later.
18//!
19//! As an example, consider a refactor that requires replacing all `Foo`s and `Bar`s with `Baz`s.
20//! Most of the conversions are straightforward, but some require deeper changes (like `Baz`
21//! requiring some additional values that aren't readily available in a particular segment of the
22//! code). You may want to first finish translating all the `Foo`s and `Bar`s, and only *after*
23//! that deal with the corner cases.
24//!
25//! Normally in this case, the developer might add a `// TODO` comment, or an `unimplemented!()`
26//! statement. However, this approach has some drawbacks. In particular, these both still let the
27//! code compile when present. Thus, after the "first pass", the developer must *remember* to also
28//! grep for all the `unimplemented()`s and `// TODO`s (*and* filter out any that aren't relevant
29//! to the refactor in question).
30//!
31//! The macro implemented by this crate provides a way to tell the compiler "don't let the code
32//! compile while this is unimplemented", while still running type checks and the borrow checker so
33//! you can fully complete other segments of code in the meantime.
34//!
35//! Ideally, this macro would have its own compiler lint so that the resulting error directly said
36//! something along the lines of "required code segment not completed". However, until RFC issue
37//! 1911 sees some progress, this crate hacks around the issue by "abusing" another lint and making
38//! it a fatal warning. The result works mostly as desired: it will only error out after all
39//! compiler passes complete successfully, and can be evaluated either as a statement or as an
40//! expression.
41//!
42//! # Example
43//!
44//! ```rust,ignore
45//! #[macro_use] extern crate incomplete;
46//! fn foo() -> bool {
47//!     incomplete!()
48//! }
49//! fn main() {
50//!     foo();
51//! }
52//! ```
53//!
54//! produces
55//!
56//! ```text
57//!  error: value assigned to `incomplete` is never read
58//!  --> src/main.rs:5:5
59//!   |
60//! 5 |     incomplete!()
61//!   |     ^^^^^^^^^^^^^
62//!   |
63//! ```
64//!
65//! # Notes
66//!
67//! As of the time of writing, the stable compiler will only produce a *warning*, not an *error*,
68//! when `incomplete!()` is used. This is because it doesn't correctly interpret the use of
69//! compiler directives on statements. On nightly this has been fixed.
70//!
71//! Also, this macro abuses an existing Rust compiler lint behind the scenes. Since the compiler
72//! insists on telling you *where* every lint warning-turned-error originates from, you will
73//! unfortunately also get a message along the lines of
74//!
75//! ```text
76//! note: lint level defined here
77//!  --> src/main.rs:5:13
78//!   |
79//! 5 |     let x = incomplete!(bool);
80//!   |             ^^^^^^^^^^^^^^^^^
81//!   = note: this error originates in a macro outside of the current crate
82//! ```
83
84#![deny(missing_docs)]
85
86/// Indicate that a segment of code must be filled out before compilation is allowed to succeed.
87///
88/// # Examples
89///
90/// ```rust,ignore
91/// #[macro_use] extern crate incomplete;
92///
93/// fn main() {
94///     let x = incomplete!(bool);
95///     if x == true {
96///         println!("profit");
97///     }
98/// }
99/// ```
100///
101/// would produce
102///
103/// ```text
104/// error: value assigned to `incomplete` is never read
105///  --> src/main.rs:5:13
106///   |
107/// 5 |     let x = incomplete!(bool);
108///   |             ^^^^^^^^^^^^^^^^^
109///   |
110/// ```
111///
112/// You can leave the type argument out if the compiler can already infer the type.
113/// ```
114#[macro_export]
115macro_rules! incomplete {
116    ($ty:ty) => {{
117        #[allow(unused_variables)]
118        let mut incomplete = 42;
119        if false {
120            #[forbid(unused_assignments)]
121            {incomplete = 1;}
122        }
123        unsafe { ::std::mem::uninitialized::<$ty>() }
124    }};
125    () => { incomplete!(_) }
126}