]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_ext/assert.rs
Auto merge of #60740 - petrochenkov:kw, r=nnethercote
[rust.git] / src / libsyntax_ext / assert.rs
1 use errors::{Applicability, DiagnosticBuilder};
2
3 use syntax::ast::{self, *};
4 use syntax::source_map::Spanned;
5 use syntax::ext::base::*;
6 use syntax::ext::build::AstBuilder;
7 use syntax::parse::token;
8 use syntax::parse::parser::Parser;
9 use syntax::print::pprust;
10 use syntax::ptr::P;
11 use syntax::symbol::Symbol;
12 use syntax::tokenstream::{TokenStream, TokenTree};
13 use syntax_pos::{Span, DUMMY_SP};
14
15 pub fn expand_assert<'cx>(
16     cx: &'cx mut ExtCtxt<'_>,
17     sp: Span,
18     tts: &[TokenTree],
19 ) -> Box<dyn MacResult + 'cx> {
20     let Assert { cond_expr, custom_message } = match parse_assert(cx, sp, tts) {
21         Ok(assert) => assert,
22         Err(mut err) => {
23             err.emit();
24             return DummyResult::expr(sp);
25         }
26     };
27
28     let sp = sp.apply_mark(cx.current_expansion.mark);
29     let panic_call = Mac_ {
30         path: Path::from_ident(Ident::new(Symbol::intern("panic"), sp)),
31         tts: custom_message.unwrap_or_else(|| {
32             TokenStream::from(TokenTree::Token(
33                 DUMMY_SP,
34                 token::Literal(
35                     token::Lit::Str_(Name::intern(&format!(
36                         "assertion failed: {}",
37                         pprust::expr_to_string(&cond_expr).escape_debug()
38                     ))),
39                     None,
40                 ),
41             ))
42         }).into(),
43         delim: MacDelimiter::Parenthesis,
44     };
45     let if_expr = cx.expr_if(
46         sp,
47         cx.expr(sp, ExprKind::Unary(UnOp::Not, cond_expr)),
48         cx.expr(
49             sp,
50             ExprKind::Mac(Spanned {
51                 span: sp,
52                 node: panic_call,
53             }),
54         ),
55         None,
56     );
57     MacEager::expr(if_expr)
58 }
59
60 struct Assert {
61     cond_expr: P<ast::Expr>,
62     custom_message: Option<TokenStream>,
63 }
64
65 fn parse_assert<'a>(
66     cx: &mut ExtCtxt<'a>,
67     sp: Span,
68     tts: &[TokenTree]
69 ) -> Result<Assert, DiagnosticBuilder<'a>> {
70     let mut parser = cx.new_parser_from_tts(tts);
71
72     if parser.token == token::Eof {
73         let mut err = cx.struct_span_err(sp, "macro requires a boolean expression as an argument");
74         err.span_label(sp, "boolean expression required");
75         return Err(err);
76     }
77
78     let cond_expr = parser.parse_expr()?;
79
80     // Some crates use the `assert!` macro in the following form (note extra semicolon):
81     //
82     // assert!(
83     //     my_function();
84     // );
85     //
86     // Warn about semicolon and suggest removing it. Eventually, this should be turned into an
87     // error.
88     if parser.token == token::Semi {
89         let mut err = cx.struct_span_warn(sp, "macro requires an expression as an argument");
90         err.span_suggestion(
91             parser.span,
92             "try removing semicolon",
93             String::new(),
94             Applicability::MaybeIncorrect
95         );
96         err.note("this is going to be an error in the future");
97         err.emit();
98
99         parser.bump();
100     }
101
102     // Some crates use the `assert!` macro in the following form (note missing comma before
103     // message):
104     //
105     // assert!(true "error message");
106     //
107     // Parse this as an actual message, and suggest inserting a comma. Eventually, this should be
108     // turned into an error.
109     let custom_message = if let token::Literal(token::Lit::Str_(_), _) = parser.token {
110         let mut err = cx.struct_span_warn(parser.span, "unexpected string literal");
111         let comma_span = cx.source_map().next_point(parser.prev_span);
112         err.span_suggestion_short(
113             comma_span,
114             "try adding a comma",
115             ", ".to_string(),
116             Applicability::MaybeIncorrect
117         );
118         err.note("this is going to be an error in the future");
119         err.emit();
120
121         parse_custom_message(&mut parser)
122     } else if parser.eat(&token::Comma) {
123         parse_custom_message(&mut parser)
124     } else {
125         None
126     };
127
128     if parser.token != token::Eof {
129         parser.expect_one_of(&[], &[])?;
130         unreachable!();
131     }
132
133     Ok(Assert { cond_expr, custom_message })
134 }
135
136 fn parse_custom_message<'a>(parser: &mut Parser<'a>) -> Option<TokenStream> {
137     let ts = parser.parse_tokens();
138     if !ts.is_empty() {
139         Some(ts)
140     } else {
141         None
142     }
143 }