diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 65aacb23bd768..08c49002b5579 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -1716,7 +1716,7 @@ macro_rules! tuple { () => (); ( $($name:ident,)+ ) => ( #[stable(feature = "rust1", since = "1.0.0")] - impl<$($name:Debug),*> Debug for ($($name,)*) where last_type!($($name,)+): ?Sized { + impl<$($name: Debug),*> Debug for ($($name,)*) where last_type!($($name,)+): ?Sized { #[allow(non_snake_case, unused_assignments, deprecated)] fn fmt(&self, f: &mut Formatter) -> Result { let mut builder = f.debug_tuple(""); diff --git a/src/libcore/tuple.rs b/src/libcore/tuple.rs index 4c5370194fecb..f151cdd77a0d6 100644 --- a/src/libcore/tuple.rs +++ b/src/libcore/tuple.rs @@ -22,7 +22,7 @@ macro_rules! tuple_impls { )+) => { $( #[stable(feature = "rust1", since = "1.0.0")] - impl<$($T:PartialEq),+> PartialEq for ($($T,)+) where last_type!($($T,)+): ?Sized { + impl<$($T: PartialEq),+> PartialEq for ($($T,)+) where last_type!($($T,)+): ?Sized { #[inline] fn eq(&self, other: &($($T,)+)) -> bool { $(self.$idx == other.$idx)&&+ @@ -34,10 +34,10 @@ macro_rules! tuple_impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl<$($T:Eq),+> Eq for ($($T,)+) where last_type!($($T,)+): ?Sized {} + impl<$($T: Eq),+> Eq for ($($T,)+) where last_type!($($T,)+): ?Sized {} #[stable(feature = "rust1", since = "1.0.0")] - impl<$($T:PartialOrd + PartialEq),+> PartialOrd for ($($T,)+) + impl<$($T: PartialOrd + PartialEq),+> PartialOrd for ($($T,)+) where last_type!($($T,)+): ?Sized { #[inline] fn partial_cmp(&self, other: &($($T,)+)) -> Option { @@ -62,7 +62,7 @@ macro_rules! tuple_impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl<$($T:Ord),+> Ord for ($($T,)+) where last_type!($($T,)+): ?Sized { + impl<$($T: Ord),+> Ord for ($($T,)+) where last_type!($($T,)+): ?Sized { #[inline] fn cmp(&self, other: &($($T,)+)) -> Ordering { lexical_cmp!($(self.$idx, other.$idx),+) @@ -70,7 +70,7 @@ macro_rules! tuple_impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl<$($T:Default),+> Default for ($($T,)+) { + impl<$($T: Default),+> Default for ($($T,)+) { #[inline] fn default() -> ($($T,)+) { ($({ let x: $T = Default::default(); x},)+) diff --git a/src/libserialize/serialize.rs b/src/libserialize/serialize.rs index 854ca4eb3b03b..fa60b11b1cb6b 100644 --- a/src/libserialize/serialize.rs +++ b/src/libserialize/serialize.rs @@ -679,7 +679,7 @@ macro_rules! count_idents { macro_rules! tuple { () => (); ( $($name:ident,)+ ) => ( - impl<$($name:Decodable),*> Decodable for ($($name,)*) { + impl<$($name: Decodable),*> Decodable for ($($name,)*) { #[allow(non_snake_case)] fn decode(d: &mut D) -> Result<($($name,)*), D::Error> { let len: usize = count_idents!($($name,)*); @@ -693,7 +693,7 @@ macro_rules! tuple { }) } } - impl<$($name:Encodable),*> Encodable for ($($name,)*) { + impl<$($name: Encodable),*> Encodable for ($($name,)*) { #[allow(non_snake_case)] fn encode(&self, s: &mut S) -> Result<(), S::Error> { let ($(ref $name,)*) = *self; diff --git a/src/libsyntax/ext/tt/quoted.rs b/src/libsyntax/ext/tt/quoted.rs index c55dfaba8f6b2..e44f6f0733a43 100644 --- a/src/libsyntax/ext/tt/quoted.rs +++ b/src/libsyntax/ext/tt/quoted.rs @@ -190,31 +190,51 @@ pub fn parse( // Given the parsed tree, if there is a metavar and we are expecting matchers, actually // parse out the matcher (i.e. in `$id:ident` this would parse the `:` and `ident`). match tree { - TokenTree::MetaVar(start_sp, ident) if expect_matchers => { - let span = match trees.next() { - Some(tokenstream::TokenTree::Token(span, token::Colon)) => match trees.next() { - Some(tokenstream::TokenTree::Token(end_sp, ref tok)) => match tok.ident() { - Some(kind) => { - let span = end_sp.with_lo(start_sp.lo()); - result.push(TokenTree::MetaVarDecl(span, ident, kind)); - continue; - } - _ => end_sp, - }, - tree => tree.as_ref() - .map(tokenstream::TokenTree::span) - .unwrap_or(span), - }, - tree => tree.as_ref() - .map(tokenstream::TokenTree::span) - .unwrap_or(start_sp), - }; - sess.missing_fragment_specifiers.borrow_mut().insert(span); - result.push(TokenTree::MetaVarDecl( - span, - ident, - keywords::Invalid.ident(), - )); + TokenTree::MetaVar(start_sp, ident) => { + if expect_matchers { + match parse_matcher(start_sp, ident, &mut trees) { + Ok((meta_var_decl, _, _)) => result.push(meta_var_decl), + Err(span) => { + sess.missing_fragment_specifiers.borrow_mut().insert(span); + result.push(TokenTree::MetaVarDecl( + span, + ident, + keywords::Invalid.ident(), + )); + } + } + } else { + const VALID_SPECIFIERS: &[&str] = &[ + "ident", "block", "stmt", "expr", "pat", "ty", "path", "meta", "tt", + "item", "vis", + ]; + match parse_matcher(start_sp, ident, &mut trees.clone()) { + Ok((TokenTree::MetaVarDecl(full_sp, _, kind), colon_sp, end_sp)) + if start_sp.hi() == colon_sp.lo() && colon_sp.hi() == end_sp.lo() + && VALID_SPECIFIERS.contains(&&*kind.name.as_str()) => + { + let specifier_sp = colon_sp.with_hi(end_sp.hi()); + sess.span_diagnostic + .struct_span_warn( + specifier_sp, + "macro expansion includes fragment specifier", + ) + .span_suggestion( + full_sp, + "to just use the macro argument, remove the fragment specifier", + format!("${}", ident), + ) + .span_suggestion( + specifier_sp, + "to suppress this warning, add a space after the colon", + format!(": {}", kind), + ) + .emit(); + } + _ => {} + } + result.push(tree) + } } // Not a metavar or no matchers allowed, so just return the tree @@ -224,6 +244,48 @@ pub fn parse( result } +/// Parses a `:`. Doesn't report error on its own. +/// +/// # Parameters +/// +/// - `start_sp` - span of metavariable name preceding the `:` +/// - `ident` - the name of metavariable +/// - `trees` - iterator over trees +/// +/// # Returns +/// +/// On success, returns a parsed `MetaVarDecl`, span of `:` and span of matcher's type +/// On error, returns a span for error. +fn parse_matcher( + start_sp: Span, + ident: ast::Ident, + trees: &mut I, +) -> Result<(TokenTree, Span, Span), Span> +where + I: Iterator, +{ + match trees.next() { + Some(tokenstream::TokenTree::Token(colon_sp, token::Colon)) => match trees.next() { + Some(tokenstream::TokenTree::Token(end_sp, ref tok)) => match tok.ident() { + Some(kind) => { + let span = end_sp.with_lo(start_sp.lo()); + Ok((TokenTree::MetaVarDecl(span, ident, kind), colon_sp, end_sp)) + } + _ => Err(end_sp), + }, + tree => { + let span = tree.as_ref() + .map(tokenstream::TokenTree::span) + .unwrap_or(colon_sp); + Err(span) + } + }, + tree => Err(tree.as_ref() + .map(tokenstream::TokenTree::span) + .unwrap_or(start_sp)), + } +} + /// Takes a `tokenstream::TokenTree` and returns a `self::TokenTree`. Specifically, this takes a /// generic `TokenTree`, such as is used in the rest of the compiler, and returns a `TokenTree` /// for use in parsing a macro. diff --git a/src/test/compile-fail/hygiene/globs.rs b/src/test/compile-fail/hygiene/globs.rs index 7ba217061c66e..a67f0d72e0366 100644 --- a/src/test/compile-fail/hygiene/globs.rs +++ b/src/test/compile-fail/hygiene/globs.rs @@ -47,7 +47,7 @@ macro n($i:ident) { } } - macro n($j:ident) { + macro n($j: ident) { mod test { use super::*; fn g() { diff --git a/src/test/compile-fail/hygiene/nested_macro_privacy.rs b/src/test/compile-fail/hygiene/nested_macro_privacy.rs index 6612359649c19..2ded2e81def70 100644 --- a/src/test/compile-fail/hygiene/nested_macro_privacy.rs +++ b/src/test/compile-fail/hygiene/nested_macro_privacy.rs @@ -14,7 +14,7 @@ macro n($foo:ident, $S:ident, $i:ident, $m:ident) { mod $foo { #[derive(Default)] pub struct $S { $i: u32 } - pub macro $m($e:expr) { $e.$i } + pub macro $m($e: expr) { $e.$i } } } diff --git a/src/test/ui/fragment_specifier_in_expansion.rs b/src/test/ui/fragment_specifier_in_expansion.rs new file mode 100644 index 0000000000000..2fa111a6874a3 --- /dev/null +++ b/src/test/ui/fragment_specifier_in_expansion.rs @@ -0,0 +1,30 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests the "fragment specifier in expansion" warning + +// must-compile-successfully + +#![allow(unused)] + +macro_rules! warn_me { + ( $thing:tt ) => { $thing:tt } + //~^ WARN fragment specifier +} + +macro_rules! with_space { + ( $thing:tt ) => { $thing: tt } +} + +macro_rules! unknown_specifier { + ( $thing:tt ) => { $thing:foobar } +} + +fn main() {} diff --git a/src/test/ui/fragment_specifier_in_expansion.stderr b/src/test/ui/fragment_specifier_in_expansion.stderr new file mode 100644 index 0000000000000..9eda3d5e5d9ae --- /dev/null +++ b/src/test/ui/fragment_specifier_in_expansion.stderr @@ -0,0 +1,14 @@ +warning: macro expansion includes fragment specifier + --> $DIR/fragment_specifier_in_expansion.rs:18:30 + | +18 | ( $thing:tt ) => { $thing:tt } + | ^^^ +help: to just use the macro argument, remove the fragment specifier + | +18 | ( $thing:tt ) => { $thing } + | ^^^^^^ +help: to suppress this warning, add a space after the colon + | +18 | ( $thing:tt ) => { $thing: tt } + | ^^^^ +