diff options
| author | bors <bors@rust-lang.org> | 2018-10-26 17:26:56 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2018-10-26 17:26:56 +0000 |
| commit | 3e6f30ec3e6bda159063fcd126dcb14725fef92d (patch) | |
| tree | b70731fbfb445b1531d9bec123ecebcffcf2a0db /src/libsyntax | |
| parent | bf962e2552cb60a75fec5bded24da071df7f8482 (diff) | |
| parent | eb2953022420517859c67c6e57ef59a14dd2fee8 (diff) | |
| download | rust-3e6f30ec3e6bda159063fcd126dcb14725fef92d.tar.gz rust-3e6f30ec3e6bda159063fcd126dcb14725fef92d.zip | |
Auto merge of #55382 - kennytm:rollup, r=kennytm
Rollup of 21 pull requests Successful merges: - #54816 (Don't try to promote already promoted out temporaries) - #54824 (Cleanup rustdoc tests with `@!has` and `@!matches`) - #54921 (Add line numbers option to rustdoc) - #55167 (Add a "cheap" mode for `compute_missing_ctors`.) - #55258 (Fix Rustdoc ICE when checking blanket impls) - #55264 (Compile the libstd we distribute with -Ccodegen-unit=1) - #55271 (Unimplement ExactSizeIterator for MIR traversing iterators) - #55292 (Macro diagnostics tweaks) - #55298 (Point at macro definition when no rules expect token) - #55301 (List allowed tokens after macro fragments) - #55302 (Extend the impl_stable_hash_for! macro for miri.) - #55325 (Fix link to macros chapter) - #55343 (rustbuild: fix remap-debuginfo when building a release) - #55346 (Shrink `Statement`.) - #55358 (Remove redundant clone (2)) - #55370 (Update mailmap for estebank) - #55375 (Typo fixes in configure_cmake comments) - #55378 (rustbuild: use configured linker to build boostrap) - #55379 (validity: assert that unions are non-empty) - #55383 (Use `SmallVec` for the queue in `coerce_unsized`.) - #55391 (bootstrap: clean up a few clippy findings)
Diffstat (limited to 'src/libsyntax')
| -rw-r--r-- | src/libsyntax/ext/base.rs | 18 | ||||
| -rw-r--r-- | src/libsyntax/ext/expand.rs | 35 | ||||
| -rw-r--r-- | src/libsyntax/ext/tt/macro_rules.rs | 173 | ||||
| -rw-r--r-- | src/libsyntax/ext/tt/transcribe.rs | 4 | ||||
| -rw-r--r-- | src/libsyntax/test.rs | 2 |
5 files changed, 165 insertions, 67 deletions
diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 5bf1a7dd663..1701c8da2c5 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -247,8 +247,13 @@ impl<F> AttrProcMacro for F /// Represents a thing that maps token trees to Macro Results pub trait TTMacroExpander { - fn expand<'cx>(&self, ecx: &'cx mut ExtCtxt, span: Span, input: TokenStream) - -> Box<dyn MacResult+'cx>; + fn expand<'cx>( + &self, + ecx: &'cx mut ExtCtxt, + span: Span, + input: TokenStream, + def_span: Option<Span>, + ) -> Box<dyn MacResult+'cx>; } pub type MacroExpanderFn = @@ -259,8 +264,13 @@ impl<F> TTMacroExpander for F where F: for<'cx> Fn(&'cx mut ExtCtxt, Span, &[tokenstream::TokenTree]) -> Box<dyn MacResult+'cx> { - fn expand<'cx>(&self, ecx: &'cx mut ExtCtxt, span: Span, input: TokenStream) - -> Box<dyn MacResult+'cx> { + fn expand<'cx>( + &self, + ecx: &'cx mut ExtCtxt, + span: Span, + input: TokenStream, + _def_span: Option<Span>, + ) -> Box<dyn MacResult+'cx> { struct AvoidInterpolatedIdents; impl Folder for AvoidInterpolatedIdents { diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 9e06384f5a8..33b651e1b38 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -764,7 +764,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { edition) { dummy_span } else { - kind.make_from(expander.expand(self.cx, span, mac.node.stream())) + kind.make_from(expander.expand(self.cx, span, mac.node.stream(), None)) } } @@ -785,7 +785,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> { edition) { dummy_span } else { - kind.make_from(expander.expand(self.cx, span, mac.node.stream())) + kind.make_from(expander.expand( + self.cx, + span, + mac.node.stream(), + def_info.map(|(_, s)| s), + )) } } @@ -1036,10 +1041,28 @@ impl<'a> Parser<'a> { // Avoid emitting backtrace info twice. let def_site_span = self.span.with_ctxt(SyntaxContext::empty()); let mut err = self.diagnostic().struct_span_err(def_site_span, &msg); - let msg = format!("caused by the macro expansion here; the usage \ - of `{}!` is likely invalid in {} context", - macro_path, kind_name); - err.span_note(span, &msg).emit(); + err.span_label(span, "caused by the macro expansion here"); + let msg = format!( + "the usage of `{}!` is likely invalid in {} context", + macro_path, + kind_name, + ); + err.note(&msg); + let semi_span = self.sess.source_map().next_point(span); + + let semi_full_span = semi_span.to(self.sess.source_map().next_point(semi_span)); + match self.sess.source_map().span_to_snippet(semi_full_span) { + Ok(ref snippet) if &snippet[..] != ";" && kind_name == "expression" => { + err.span_suggestion_with_applicability( + semi_span, + "you might be missing a semicolon here", + ";".to_owned(), + Applicability::MaybeIncorrect, + ); + } + _ => {} + } + err.emit(); } } } diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index 805aa9bef22..e9149054a55 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -50,7 +50,12 @@ pub struct ParserAnyMacro<'a> { impl<'a> ParserAnyMacro<'a> { pub fn make(mut self: Box<ParserAnyMacro<'a>>, kind: AstFragmentKind) -> AstFragment { let ParserAnyMacro { site_span, macro_ident, ref mut parser } = *self; - let fragment = panictry!(parser.parse_ast_fragment(kind, true)); + let fragment = panictry!(parser.parse_ast_fragment(kind, true).map_err(|mut e| { + if e.span.is_dummy() { // Get around lack of span in error (#30128) + e.set_span(site_span); + } + e + })); // We allow semicolons at the end of expressions -- e.g. the semicolon in // `macro_rules! m { () => { panic!(); } }` isn't parsed by `.parse_expr()`, @@ -74,16 +79,19 @@ struct MacroRulesMacroExpander { } impl TTMacroExpander for MacroRulesMacroExpander { - fn expand<'cx>(&self, - cx: &'cx mut ExtCtxt, - sp: Span, - input: TokenStream) - -> Box<dyn MacResult+'cx> { + fn expand<'cx>( + &self, + cx: &'cx mut ExtCtxt, + sp: Span, + input: TokenStream, + def_span: Option<Span>, + ) -> Box<dyn MacResult+'cx> { if !self.valid { return DummyResult::any(sp); } generic_extension(cx, sp, + def_span, self.name, input, &self.lhses, @@ -99,6 +107,7 @@ fn trace_macros_note(cx: &mut ExtCtxt, sp: Span, message: String) { /// Given `lhses` and `rhses`, this is the new macro we create fn generic_extension<'cx>(cx: &'cx mut ExtCtxt, sp: Span, + def_span: Option<Span>, name: ast::Ident, arg: TokenStream, lhses: &[quoted::TokenTree], @@ -133,8 +142,7 @@ fn generic_extension<'cx>(cx: &'cx mut ExtCtxt, // Replace all the tokens for the corresponding positions in the macro, to maintain // proper positions in error reporting, while maintaining the macro_backtrace. if rhs_spans.len() == tts.len() { - tts = tts.map_enumerated(|i, tt| { - let mut tt = tt.clone(); + tts = tts.map_enumerated(|i, mut tt| { let mut sp = rhs_spans[i]; sp = sp.with_ctxt(tt.span().ctxt()); tt.set_span(sp); @@ -178,7 +186,14 @@ fn generic_extension<'cx>(cx: &'cx mut ExtCtxt, } let best_fail_msg = parse_failure_msg(best_fail_tok.expect("ran no matchers")); - let mut err = cx.struct_span_err(best_fail_spot.substitute_dummy(sp), &best_fail_msg); + let span = best_fail_spot.substitute_dummy(sp); + let mut err = cx.struct_span_err(span, &best_fail_msg); + err.span_label(span, best_fail_msg); + if let Some(sp) = def_span { + if cx.source_map().span_to_filename(sp).is_real() && !sp.is_dummy() { + err.span_label(cx.source_map().def_span(sp), "when calling this macro"); + } + } // Check whether there's a missing comma in this macro call, like `println!("{}" a);` if let Some((arg, comma_span)) = arg.add_comma() { @@ -189,7 +204,7 @@ fn generic_extension<'cx>(cx: &'cx mut ExtCtxt, }; match TokenTree::parse(cx, lhs_tt, arg.clone()) { Success(_) => { - if comma_span == DUMMY_SP { + if comma_span.is_dummy() { err.note("you might be missing a comma"); } else { err.span_suggestion_short_with_applicability( @@ -792,15 +807,15 @@ fn check_matcher_core(sess: &ParseSess, if let TokenTree::MetaVarDecl(_, ref name, ref frag_spec) = *token { for next_token in &suffix_first.tokens { match is_in_follow(next_token, &frag_spec.as_str()) { - Err((msg, help)) => { + IsInFollow::Invalid(msg, help) => { sess.span_diagnostic.struct_span_err(next_token.span(), &msg) .help(help).emit(); // don't bother reporting every source of // conflict for a particular element of `last`. continue 'each_last; } - Ok(true) => {} - Ok(false) => { + IsInFollow::Yes => {} + IsInFollow::No(ref possible) => { let may_be = if last.tokens.len() == 1 && suffix_first.tokens.len() == 1 { @@ -809,15 +824,41 @@ fn check_matcher_core(sess: &ParseSess, "may be" }; - sess.span_diagnostic.span_err( - next_token.span(), + let sp = next_token.span(); + let mut err = sess.span_diagnostic.struct_span_err( + sp, &format!("`${name}:{frag}` {may_be} followed by `{next}`, which \ is not allowed for `{frag}` fragments", name=name, frag=frag_spec, next=quoted_tt_to_string(next_token), - may_be=may_be) + may_be=may_be), ); + err.span_label( + sp, + format!("not allowed after `{}` fragments", frag_spec), + ); + let msg = "allowed there are: "; + match &possible[..] { + &[] => {} + &[t] => { + err.note(&format!( + "only {} is allowed after `{}` fragments", + t, + frag_spec, + )); + } + ts => { + err.note(&format!( + "{}{} or {}", + msg, + ts[..ts.len() - 1].iter().map(|s| *s) + .collect::<Vec<_>>().join(", "), + ts[ts.len() - 1], + )); + } + } + err.emit(); } } } @@ -860,6 +901,12 @@ fn frag_can_be_followed_by_any(frag: &str) -> bool { } } +enum IsInFollow { + Yes, + No(Vec<&'static str>), + Invalid(String, &'static str), +} + /// True if `frag` can legally be followed by the token `tok`. For /// fragments that can consume an unbounded number of tokens, `tok` /// must be within a well-defined follow set. This is intended to @@ -868,81 +915,99 @@ fn frag_can_be_followed_by_any(frag: &str) -> bool { /// break macros that were relying on that binary operator as a /// separator. // when changing this do not forget to update doc/book/macros.md! -fn is_in_follow(tok: "ed::TokenTree, frag: &str) -> Result<bool, (String, &'static str)> { +fn is_in_follow(tok: "ed::TokenTree, frag: &str) -> IsInFollow { use self::quoted::TokenTree; if let TokenTree::Token(_, token::CloseDelim(_)) = *tok { // closing a token tree can never be matched by any fragment; // iow, we always require that `(` and `)` match, etc. - Ok(true) + IsInFollow::Yes } else { match frag { "item" => { // since items *must* be followed by either a `;` or a `}`, we can // accept anything after them - Ok(true) + IsInFollow::Yes }, "block" => { // anything can follow block, the braces provide an easy boundary to // maintain - Ok(true) + IsInFollow::Yes }, - "stmt" | "expr" => match *tok { - TokenTree::Token(_, ref tok) => match *tok { - FatArrow | Comma | Semi => Ok(true), - _ => Ok(false) - }, - _ => Ok(false), + "stmt" | "expr" => { + let tokens = vec!["`=>`", "`,`", "`;`"]; + match *tok { + TokenTree::Token(_, ref tok) => match *tok { + FatArrow | Comma | Semi => IsInFollow::Yes, + _ => IsInFollow::No(tokens), + }, + _ => IsInFollow::No(tokens), + } }, - "pat" => match *tok { - TokenTree::Token(_, ref tok) => match *tok { - FatArrow | Comma | Eq | BinOp(token::Or) => Ok(true), - Ident(i, false) if i.name == "if" || i.name == "in" => Ok(true), - _ => Ok(false) - }, - _ => Ok(false), + "pat" => { + let tokens = vec!["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"]; + match *tok { + TokenTree::Token(_, ref tok) => match *tok { + FatArrow | Comma | Eq | BinOp(token::Or) => IsInFollow::Yes, + Ident(i, false) if i.name == "if" || i.name == "in" => IsInFollow::Yes, + _ => IsInFollow::No(tokens), + }, + _ => IsInFollow::No(tokens), + } }, - "path" | "ty" => match *tok { - TokenTree::Token(_, ref tok) => match *tok { - OpenDelim(token::DelimToken::Brace) | OpenDelim(token::DelimToken::Bracket) | - Comma | FatArrow | Colon | Eq | Gt | BinOp(token::Shr) | Semi | - BinOp(token::Or) => Ok(true), - Ident(i, false) if i.name == "as" || i.name == "where" => Ok(true), - _ => Ok(false) - }, - TokenTree::MetaVarDecl(_, _, frag) if frag.name == "block" => Ok(true), - _ => Ok(false), + "path" | "ty" => { + let tokens = vec![ + "`{`", "`[`", "`=>`", "`,`", "`>`","`=`", "`:`", "`;`", "`|`", "`as`", + "`where`", + ]; + match *tok { + TokenTree::Token(_, ref tok) => match *tok { + OpenDelim(token::DelimToken::Brace) | + OpenDelim(token::DelimToken::Bracket) | + Comma | FatArrow | Colon | Eq | Gt | BinOp(token::Shr) | Semi | + BinOp(token::Or) => IsInFollow::Yes, + Ident(i, false) if i.name == "as" || i.name == "where" => IsInFollow::Yes, + _ => IsInFollow::No(tokens), + }, + TokenTree::MetaVarDecl(_, _, frag) if frag.name == "block" => IsInFollow::Yes, + _ => IsInFollow::No(tokens), + } }, "ident" | "lifetime" => { // being a single token, idents and lifetimes are harmless - Ok(true) + IsInFollow::Yes }, "literal" => { // literals may be of a single token, or two tokens (negative numbers) - Ok(true) + IsInFollow::Yes }, "meta" | "tt" => { // being either a single token or a delimited sequence, tt is // harmless - Ok(true) + IsInFollow::Yes }, "vis" => { // Explicitly disallow `priv`, on the off chance it comes back. + let tokens = vec!["`,`", "an ident", "a type"]; match *tok { TokenTree::Token(_, ref tok) => match *tok { - Comma => Ok(true), - Ident(i, is_raw) if is_raw || i.name != "priv" => Ok(true), - ref tok => Ok(tok.can_begin_type()) + Comma => IsInFollow::Yes, + Ident(i, is_raw) if is_raw || i.name != "priv" => IsInFollow::Yes, + ref tok => if tok.can_begin_type() { + IsInFollow::Yes + } else { + IsInFollow::No(tokens) + } }, TokenTree::MetaVarDecl(_, _, frag) if frag.name == "ident" || frag.name == "ty" - || frag.name == "path" => Ok(true), - _ => Ok(false) + || frag.name == "path" => IsInFollow::Yes, + _ => IsInFollow::No(tokens), } }, - "" => Ok(true), // keywords::Invalid - _ => Err((format!("invalid fragment specifier `{}`", frag), - VALID_FRAGMENT_NAMES_MSG)) + "" => IsInFollow::Yes, // keywords::Invalid + _ => IsInFollow::Invalid(format!("invalid fragment specifier `{}`", frag), + VALID_FRAGMENT_NAMES_MSG), } } } diff --git a/src/libsyntax/ext/tt/transcribe.rs b/src/libsyntax/ext/tt/transcribe.rs index ae486158fee..3d897d17e0b 100644 --- a/src/libsyntax/ext/tt/transcribe.rs +++ b/src/libsyntax/ext/tt/transcribe.rs @@ -219,9 +219,9 @@ impl Add for LockstepIterSize { LockstepIterSize::Unconstrained => other, LockstepIterSize::Contradiction(_) => self, LockstepIterSize::Constraint(l_len, ref l_id) => match other { - LockstepIterSize::Unconstrained => self.clone(), + LockstepIterSize::Unconstrained => self, LockstepIterSize::Contradiction(_) => other, - LockstepIterSize::Constraint(r_len, _) if l_len == r_len => self.clone(), + LockstepIterSize::Constraint(r_len, _) if l_len == r_len => self, LockstepIterSize::Constraint(r_len, r_id) => { let msg = format!("inconsistent lockstep iteration: \ '{}' has {} items, but '{}' has {}", diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 9f554a90afb..8ff4b0d025c 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -346,7 +346,7 @@ fn mk_main(cx: &mut TestCtxt) -> P<ast::Item> { test_runner.span = sp; - let test_main_path_expr = ecx.expr_path(test_runner.clone()); + let test_main_path_expr = ecx.expr_path(test_runner); let call_test_main = ecx.expr_call(sp, test_main_path_expr, vec![mk_tests_slice(cx)]); let call_test_main = ecx.stmt_expr(call_test_main); |
