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