cargo_what/lib.rs
1//! Tells you what type things are.
2//!
3//! This crate provides the `what!` macro. This is functionally similar to the
4//! [`todo!`] macro, except that it also tells you type information.
5//!
6//! ``` rust
7//! # use cargo_what::what;
8//! # use std::error::Error;
9//! fn hello() -> Result<(), Box<dyn Error>> {
10//! what!()
11//! }
12//! ```
13//!
14//! Just like [`todo!`], `what!` passes all type-checks and makes it easy to
15//! write/build/test unfinished code. If it ever ends up in a compiled program,
16//! attempted to execute a `what!` will panic.
17//!
18//! The fun part happens when you run `cargo what`.
19//!
20//! ``` bash
21//! $ cargo what
22//! hole: expecting `std::result::Result<(), Box<dyn std::error::Error>>`
23//! --> src/hello.rs
24//! |
25//! 2 | what!()
26//! | ^^^^^^^
27//! ```
28//!
29//! Unfortunately, custom diagnostics aren't really available to Rust libraries,
30//! requiring the extra command. `cargo what` can be installed with `cargo`:
31//!
32//! ``` bash
33//! $ cargo install cargo-what
34//! ```
35//!
36//! `cargo what` wraps `cargo build` to show the type-info of any `what!`s
37//! you have in your code.
38//!
39//! `what!` also accepts arguments and shows their types, which can be useful
40//! for reducing the "unused variable" noise.
41//!
42//! ``` rust
43//! # use cargo_what::what;
44//! fn hello(a: usize, b: usize) -> usize {
45//! let c = a..b;
46//! what!(a, b, c)
47//! }
48//! ```
49//!
50//! And with `cargo what`:
51//!
52//! ```bash
53//! $ cargo what
54//! hole: expecting `usize`
55//! --> src/hello.rs
56//! |
57//! 3 | what!(a, b, c)
58//! | ^^^^^^^^^^^^^^
59//! |
60//! = note: a is `usize`
61//! = note: b is `usize`
62//! = note: c is `std::ops::Range<usize>`
63//! ```
64//!
65//! Emacs keybindings left as an exercise to the reader.
66//!
67//! [`todo!`]: https://doc.rust-lang.org/std/macro.todo.html
68//!
69
70// we need this for token pasting
71#[doc(hidden)]
72pub use paste as __paste;
73
74
75/// This is the core `what!` macro.
76///
77/// It behaves similarly to [`todo!`], passes all type-checks,
78/// and panics if executed.
79///
80/// ```rust
81/// # use cargo_what::what;
82/// # use std::error::Error;
83/// fn hello() -> Result<(), Box<dyn Error>> {
84/// what!()
85/// }
86/// ```
87///
88/// One difference from [`todo!`] is that `what!` also accepts
89/// arbitrary arguments, which can help reduce "unused variable"
90/// noise.
91///
92/// ``` rust
93/// # use cargo_what::what;
94/// fn hello(a: usize, b: usize) -> usize {
95/// let c = a..b;
96/// what!(a, b, c)
97/// }
98/// ```
99///
100/// See the [crate-level documentation](/cargo_what) for more info
101/// on how you can show the type-info of `what!`s in a program.
102///
103/// [`todo!`]: https://doc.rust-lang.org/std/macro.todo.html
104///
105
106// cargo build => todo
107#[cfg(not(cargo_what_query))]
108#[macro_export]
109macro_rules! what {
110 ($($args:expr),* $(,)*) => {
111 ({
112 $(
113 let _ = $args;
114 )*
115 todo!()
116 })
117 };
118}
119
120// cargo what => query type info
121#[cfg(cargo_what_query)]
122#[macro_export]
123macro_rules! what {
124 (@[$($n:expr)*] $(,)*) => {};
125 (@[$($n:expr)*] $arg:ident, $($args:tt)*) => {
126 // we can use ident directly as a special case
127 $crate::__paste::paste! {
128 trait [<What_ $arg>] {};
129 let _: &dyn [<What_ $arg>] = &$arg;
130 }
131 what!(@[$($n)* 0] $($args)*);
132 };
133 (@[$($n:expr)*] $arg:expr, $($args:tt)*) => {
134 $crate::__paste::paste! {
135 trait [<What_ $($n)*>] {};
136 // yes you need these parens (tt munching?)
137 let _: &dyn [<What_ $($n)*>] = &($arg);
138 }
139 what!(@[$($n)* 0] $($args)*);
140 };
141 // actual macro
142 ($($args:tt)*) => {
143 ({
144 todo!();
145
146 #[allow(unreachable_code)]
147 {
148 what!(@[0] $($args)*,);
149
150 trait What {};
151 match true {
152 true => {
153 let what;
154 let _: &dyn What = &what;
155 what
156 }
157 false => {
158 struct WhatTrait {};
159 impl What for WhatTrait {};
160 WhatTrait{}
161 }
162 }
163 }
164 })
165 };
166}