1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
|
//! Check the examples in the reference manual
//!
//! Looks for bullet points and blockquotes
//! in sections with title starting "Examples":
//!
//! ```text
//! * `INPUT`: `OUTPUT`
//! * `INPUT` for struct: `OUTPUT`
//! * `INPUT` for structs: `OUTPUT`
//! * `INPUT` for enum: `OUTPUT`
//! * `INPUT` for enums: `OUTPUT`
//! * `INPUT` for enum variant: `OUTPUT`
//! * `INPUT` for enum variants: `OUTPUT`
//! * `INPUT` for `TYPE-OR-VARIANT`: `OUTPUT`
//! * `INPUT` for `FIELD` in `TYPE-OR-VARIANT`: `OUTPUT`
//! * `INPUT` for fields in `TYPE-OR-VARIANT`: `OUTPUT`
//! * `INPUT` for others: `OUTPUT`
//! * `INPUT` ...: ``OUTPUT``, ...
//! * `INPUT` ...: nothing
//! * `INPUT` ...: error, ``MESSAGE``
//! * `CONDITION`: true for SOMETHING, SOMETHING, and ...`
//! ```
//!
//! ("others" means not any of the preceding contexts.
//! Note that double backquotes are required for "error,",
//! which allows individual backquotes in the messages themselves.
//! The MESSAGE must then be a substring of the actual error.)
//!
//! In an example of a `CONDITION`,
//! `SOMETHING` can be any of the syntaxes accepted in `for ...`
//! (but not "others", obviously).
//! All the contexts for which it returns true must be listed.
//!
//! In "for" clauses you can also write
//! a leading `struct ` or `Enum::`,
//! and a trailing `;`, `(...);`, or `{...}`.
//!
//! Blockquotes ` ```rust ` are tested separately via rustdoc, so ignored here.
//!
//! Otherwise, they should come in pairs, with, in between,
//! ```text
//! <!--##examples-for-toplevels-concat TYPE TYPE...##-->
//! ```
//! (which shuld be followed by introductory text for the reader).
//! And then the first is expanded for each TYPE;
//! the results (concatenated) must match the 2nd block.
//!
//! Special directives
//!
//! * `<!--##examples-ignore##-->`:
//!
//! Ignore until next blank line
//!
//! * `<!--##examples-for FOO##-->`:
//!
//! In bullet point(s), use this as if "for FOO" was written
//! (ignoring any actual "for FOO")
//! Applies until end of section (or next such directive)
//!
//! Preceding a ` ```...``` ` quote
//!
//! * `<!--##examples-structs##-->`:
//!
//! The quote has the example structs
//!
use super::*;
mod conditions;
mod contexts;
mod for_toplevels_concat;
mod possibilities;
mod reference_extract;
use conditions::ConditionExample;
use contexts::{for_every_example_context, ContextExt as _, Limit};
use for_toplevels_concat::ForToplevelsConcatExample;
use possibilities::PossibilitiesExample;
const INPUT_FILE: &str = "doc/reference.md";
pub type DocLoc = usize;
/// Something that can be checked
pub trait Example {
fn print_checking(&self);
fn check(&self, out: &mut Errors, drivers: &[syn::DeriveInput]);
}
/// Allows errors to be aggregated
pub struct Errors {
ok: Result<(), ()>,
}
impl Errors {
fn new() -> Self {
Errors { ok: Ok(()) }
}
fn wrong(&mut self, loc: DocLoc, msg: impl Display) {
eprintln!("{INPUT_FILE}:{loc}: {msg}");
self.ok = Err(());
}
}
impl Drop for Errors {
fn drop(&mut self) {
if !std::thread::panicking() && !self.ok.is_ok() {
panic!("documentation examples check failed");
}
}
}
fn bail(loc: DocLoc, msg: impl Display) -> ! {
Errors::new().wrong(loc, msg);
panic!("Errors should have panicked already!");
}
#[test]
fn check_examples() {
macros::beta::Enabled::test_with_parsing(|| {
let mut errs = Errors::new();
let (structs, examples) = reference_extract::extract(&mut errs);
for example in &examples {
example.print_checking();
example.check(&mut errs, &structs);
}
eprintln!("checked {} examples", examples.len());
});
}
|