combine_proc_macro/
diagnostic.rs

1//! Utilities to generate diagnostic error messages.
2
3use crate::input::Token;
4use combine::ParseError;
5use combine::stream::StreamOnce;
6use proc_macro2::{TokenTree, TokenStream};
7use std::convert::TryFrom;
8use std::fmt;
9
10const DEFAULT_MAX_TRAILING: usize = 50;
11
12/// Incomplete is used in a `#[proc_macro]` to check that all tokens in the Input
13/// have been parsed completely or otherwise provide a printable diagnostic-friendly
14/// representation of remaining tokens.
15///
16/// ```rust,ignore
17/// let (ast, trailing) = match parse() {
18///     Ok(ok) => ok,
19///     Err(err) => panic!("error parsing in `my_example` macro: {:#?}", err),
20/// };
21/// if let Ok(diagnostic) = Incomplete::from_stream(trailing) {
22///     panic!("unexpected tokens at end of input:\n\n{}", diagnostic);
23/// }
24/// ```
25#[derive(Debug)]
26pub struct Incomplete {
27    trailing: Vec<TokenTree>,
28
29    // The maximum number of
30    max_trailing: usize,
31}
32
33impl Incomplete {
34    pub fn from_stream<I>(mut input: I) -> Option<Incomplete>
35    where
36        I: StreamOnce<Item = Token>,
37        I::Error: ParseError<I::Item, I::Range, I::Position>,
38    {
39        let mut trailing = Vec::new();
40        while let Ok(tok) = input.uncons() {
41            trailing.extend(TokenTree::try_from(tok).into_iter());
42            if trailing.len() > DEFAULT_MAX_TRAILING {
43                break;
44            }
45        }
46        if trailing.len() > 0 {
47            Some(Incomplete {
48                trailing,
49                max_trailing: DEFAULT_MAX_TRAILING,
50            })
51        } else {
52            None
53        }
54    }
55}
56
57impl fmt::Display for Incomplete {
58    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
59        let total = self.trailing.len();
60        let mut stream = TokenStream::new();
61        stream.extend(self.trailing.iter().take(self.max_trailing).cloned());
62        if total > self.max_trailing {
63            write!(f, "{} [and {} more ...]", stream, total - self.max_trailing)
64        } else {
65            write!(f, "{}", stream)
66        }
67    }
68}