]> git.proxmox.com Git - rustc.git/blob - src/libsyntax_ext/assert.rs
New upstream version 1.41.1+dfsg1
[rustc.git] / src / libsyntax_ext / assert.rs
1 use errors::{Applicability, DiagnosticBuilder};
2
3 use rustc_parse::parser::Parser;
4 use syntax::ast::{self, *};
5 use syntax::token::{self, TokenKind};
6 use syntax::print::pprust;
7 use syntax::ptr::P;
8 use syntax::symbol::{sym, Symbol};
9 use syntax::tokenstream::{DelimSpan, TokenStream, TokenTree};
10 use syntax_expand::base::*;
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 tokens = custom_message.unwrap_or_else(|| {
30 TokenStream::from(TokenTree::token(
31 TokenKind::lit(token::Str, Symbol::intern(&format!(
32 "assertion failed: {}",
33 pprust::expr_to_string(&cond_expr).escape_debug()
34 )), None),
35 DUMMY_SP,
36 ))
37 });
38 let args = P(MacArgs::Delimited(DelimSpan::from_single(sp), MacDelimiter::Parenthesis, tokens));
39 let panic_call = Mac {
40 path: Path::from_ident(Ident::new(sym::panic, sp)),
41 args,
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 = parser.prev_span.shrink_to_hi();
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 }