From fed12fad7019051fc875a06d3699d660ea1e226a Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 19 Jun 2019 18:46:12 +0200 Subject: [PATCH 01/25] Remove mentions of removed `offset_to` method --- src/libcore/ptr/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcore/ptr/mod.rs b/src/libcore/ptr/mod.rs index 8f026a5b7d8d..337f3440b436 100644 --- a/src/libcore/ptr/mod.rs +++ b/src/libcore/ptr/mod.rs @@ -1534,7 +1534,7 @@ pub unsafe fn copy_to_nonoverlapping(self, dest: *mut T, count: usize) /// `usize::max_value()`. /// /// The offset is expressed in number of `T` elements, and not bytes. The value returned can be - /// used with the `offset` or `offset_to` methods. + /// used with the `offset` method. /// /// There are no guarantees whatsover that offsetting the pointer will not overflow or go /// beyond the allocation that the pointer points into. It is up to the caller to ensure that @@ -2335,7 +2335,7 @@ pub unsafe fn swap(self, with: *mut T) /// `usize::max_value()`. /// /// The offset is expressed in number of `T` elements, and not bytes. The value returned can be - /// used with the `offset` or `offset_to` methods. + /// used with the `offset` method. /// /// There are no guarantees whatsover that offsetting the pointer will not overflow or go /// beyond the allocation that the pointer points into. It is up to the caller to ensure that From c9c73f5d2550f00e9b952e65d05421721b73ee6d Mon Sep 17 00:00:00 2001 From: Philipp Oppermann Date: Wed, 17 Jul 2019 17:25:43 +0200 Subject: [PATCH 02/25] Refer to `add` method instead of `offset` The `align_offset` method returns an `usize`, so using `add` makes more sense than using `offset`, which takes an `isize`. --- src/libcore/ptr/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libcore/ptr/mod.rs b/src/libcore/ptr/mod.rs index 337f3440b436..e8c988b921ac 100644 --- a/src/libcore/ptr/mod.rs +++ b/src/libcore/ptr/mod.rs @@ -1534,7 +1534,7 @@ pub unsafe fn copy_to_nonoverlapping(self, dest: *mut T, count: usize) /// `usize::max_value()`. /// /// The offset is expressed in number of `T` elements, and not bytes. The value returned can be - /// used with the `offset` method. + /// used with the `add` method. /// /// There are no guarantees whatsover that offsetting the pointer will not overflow or go /// beyond the allocation that the pointer points into. It is up to the caller to ensure that @@ -2335,7 +2335,7 @@ pub unsafe fn swap(self, with: *mut T) /// `usize::max_value()`. /// /// The offset is expressed in number of `T` elements, and not bytes. The value returned can be - /// used with the `offset` method. + /// used with the `add` method. /// /// There are no guarantees whatsover that offsetting the pointer will not overflow or go /// beyond the allocation that the pointer points into. It is up to the caller to ensure that From 3dca17e62def01ee8c8621db7925d55f9275f6d9 Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 28 Jul 2019 13:33:51 +0100 Subject: [PATCH 03/25] Disallow duplicate lifetime parameters with legacy hygiene They were resolved with modern hygiene, making this just a strange way to shadow lifetimes. --- src/librustc/middle/resolve_lifetime.rs | 2 +- src/test/ui/hygiene/duplicate_lifetimes.rs | 19 +++++++++++++ .../ui/hygiene/duplicate_lifetimes.stderr | 27 +++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/hygiene/duplicate_lifetimes.rs create mode 100644 src/test/ui/hygiene/duplicate_lifetimes.stderr diff --git a/src/librustc/middle/resolve_lifetime.rs b/src/librustc/middle/resolve_lifetime.rs index e2b1b54cef39..f6c62d191fa6 100644 --- a/src/librustc/middle/resolve_lifetime.rs +++ b/src/librustc/middle/resolve_lifetime.rs @@ -2568,7 +2568,7 @@ fn check_lifetime_params( let lifetimes: Vec<_> = params .iter() .filter_map(|param| match param.kind { - GenericParamKind::Lifetime { .. } => Some((param, param.name)), + GenericParamKind::Lifetime { .. } => Some((param, param.name.modern())), _ => None, }) .collect(); diff --git a/src/test/ui/hygiene/duplicate_lifetimes.rs b/src/test/ui/hygiene/duplicate_lifetimes.rs new file mode 100644 index 000000000000..e7312b51dbcb --- /dev/null +++ b/src/test/ui/hygiene/duplicate_lifetimes.rs @@ -0,0 +1,19 @@ +// Ensure that lifetime parameter names are modernized before we check for +// duplicates. + +#![feature(decl_macro, rustc_attrs)] + +#[rustc_macro_transparency = "semitransparent"] +macro m($a:lifetime) { + fn g<$a, 'a>() {} //~ ERROR lifetime name `'a` declared twice +} + +#[rustc_macro_transparency = "transparent"] +macro n($a:lifetime) { + fn h<$a, 'a>() {} //~ ERROR lifetime name `'a` declared twice +} + +m!('a); +n!('a); + +fn main() {} diff --git a/src/test/ui/hygiene/duplicate_lifetimes.stderr b/src/test/ui/hygiene/duplicate_lifetimes.stderr new file mode 100644 index 000000000000..7aaea6ff24e3 --- /dev/null +++ b/src/test/ui/hygiene/duplicate_lifetimes.stderr @@ -0,0 +1,27 @@ +error[E0263]: lifetime name `'a` declared twice in the same scope + --> $DIR/duplicate_lifetimes.rs:8:14 + | +LL | fn g<$a, 'a>() {} + | ^^ declared twice +... +LL | m!('a); + | ------- + | | | + | | previous declaration here + | in this macro invocation + +error[E0263]: lifetime name `'a` declared twice in the same scope + --> $DIR/duplicate_lifetimes.rs:13:14 + | +LL | fn h<$a, 'a>() {} + | ^^ declared twice +... +LL | n!('a); + | ------- + | | | + | | previous declaration here + | in this macro invocation + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0263`. From 8876b3b9b0bf652cddf68d9ddcb5b5fa31d829ab Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Sun, 28 Jul 2019 13:34:03 +0100 Subject: [PATCH 04/25] Resolve const parameters with modern hygiene Declarations were already modernized, resulting in cases where a macro couldn't resolve it's own identifier. --- src/librustc_resolve/lib.rs | 26 ++++-- src/test/ui/hygiene/generic_params.rs | 104 ++++++++++++++++++++++ src/test/ui/hygiene/generic_params.stderr | 6 ++ src/test/ui/hygiene/ty_params.rs | 14 --- 4 files changed, 128 insertions(+), 22 deletions(-) create mode 100644 src/test/ui/hygiene/generic_params.rs create mode 100644 src/test/ui/hygiene/generic_params.stderr delete mode 100644 src/test/ui/hygiene/ty_params.rs diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index a5e498fa7564..8884d1cd27f7 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -872,8 +872,7 @@ fn visit_fn(&mut self, debug!("(resolving function) entering function"); let rib_kind = match function_kind { FnKind::ItemFn(..) => FnItemRibKind, - FnKind::Method(..) => AssocItemRibKind, - FnKind::Closure(_) => NormalRibKind, + FnKind::Method(..) | FnKind::Closure(_) => NormalRibKind, }; // Create a value rib for the function. @@ -2310,21 +2309,32 @@ fn resolve_ident_in_lexical_scope(&mut self, if ident.name == kw::Invalid { return Some(LexicalScopeBinding::Res(Res::Err)); } - ident.span = if ident.name == kw::SelfUpper { + let (general_span, modern_span) = if ident.name == kw::SelfUpper { // FIXME(jseyfried) improve `Self` hygiene - ident.span.with_ctxt(SyntaxContext::empty()) + let empty_span = ident.span.with_ctxt(SyntaxContext::empty()); + (empty_span, empty_span) } else if ns == TypeNS { - ident.span.modern() + let modern_span = ident.span.modern(); + (modern_span, modern_span) } else { - ident.span.modern_and_legacy() + (ident.span.modern_and_legacy(), ident.span.modern()) }; + ident.span = general_span; + let modern_ident = Ident { span: modern_span, ..ident }; // Walk backwards up the ribs in scope. let record_used = record_used_id.is_some(); let mut module = self.graph_root; for i in (0 .. self.ribs[ns].len()).rev() { debug!("walk rib\n{:?}", self.ribs[ns][i].bindings); - if let Some(res) = self.ribs[ns][i].bindings.get(&ident).cloned() { + // Use the rib kind to determine whether we are resolving parameters + // (modern hygiene) or local variables (legacy hygiene). + let rib_ident = if let AssocItemRibKind | ItemRibKind = self.ribs[ns][i].kind { + modern_ident + } else { + ident + }; + if let Some(res) = self.ribs[ns][i].bindings.get(&rib_ident).cloned() { // The ident resolves to a type parameter or local variable. return Some(LexicalScopeBinding::Res( self.validate_res_from_ribs(ns, i, res, record_used, path_span), @@ -2360,7 +2370,7 @@ fn resolve_ident_in_lexical_scope(&mut self, } } - ident.span = ident.span.modern(); + ident = modern_ident; let mut poisoned = None; loop { let opt_module = if let Some(node_id) = record_used_id { diff --git a/src/test/ui/hygiene/generic_params.rs b/src/test/ui/hygiene/generic_params.rs new file mode 100644 index 000000000000..9dc5adfce478 --- /dev/null +++ b/src/test/ui/hygiene/generic_params.rs @@ -0,0 +1,104 @@ +// Ensure that generic parameters always have modern hygiene. + +// check-pass +// ignore-pretty pretty-printing is unhygienic + +#![feature(decl_macro, rustc_attrs, const_generics)] + +mod type_params { + macro m($T:ident) { + fn f<$T: Clone, T: PartialEq>(t1: $T, t2: T) -> ($T, bool) { + (t1.clone(), t2 == t2) + } + } + + #[rustc_macro_transparency = "semitransparent"] + macro n($T:ident) { + fn g<$T: Clone>(t1: $T, t2: T) -> (T, $T) { + (t1.clone(), t2.clone()) + } + fn h(t1: $T, t2: T) -> (T, $T) { + (t1.clone(), t2.clone()) + } + } + + #[rustc_macro_transparency = "transparent"] + macro p($T:ident) { + fn j<$T: Clone>(t1: $T, t2: T) -> (T, $T) { + (t1.clone(), t2.clone()) + } + fn k(t1: $T, t2: T) -> (T, $T) { + (t1.clone(), t2.clone()) + } + } + + m!(T); + n!(T); + p!(T); +} + +mod lifetime_params { + macro m($a:lifetime) { + fn f<'b, 'c, $a: 'b, 'a: 'c>(t1: &$a(), t2: &'a ()) -> (&'b (), &'c ()) { + (t1, t2) + } + } + + #[rustc_macro_transparency = "semitransparent"] + macro n($a:lifetime) { + fn g<$a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) { + (t1, t2) + } + fn h<'a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) { + (t1, t2) + } + } + + #[rustc_macro_transparency = "transparent"] + macro p($a:lifetime) { + fn j<$a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) { + (t1, t2) + } + fn k<'a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) { + (t1, t2) + } + } + + m!('a); + n!('a); + p!('a); +} + +mod const_params { + macro m($C:ident) { + fn f(t1: [(); $C], t2: [(); C]) -> ([(); $C], [(); C]) { + (t1, t2) + } + } + + #[rustc_macro_transparency = "semitransparent"] + macro n($C:ident) { + fn g(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) { + (t1, t2) + } + fn h(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) { + (t1, t2) + } + } + + #[rustc_macro_transparency = "transparent"] + macro p($C:ident) { + fn j(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) { + (t1, t2) + } + fn k(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) { + (t1, t2) + } + } + + m!(C); + n!(C); + p!(C); +} + +fn main() {} diff --git a/src/test/ui/hygiene/generic_params.stderr b/src/test/ui/hygiene/generic_params.stderr new file mode 100644 index 000000000000..ecd228a5db5c --- /dev/null +++ b/src/test/ui/hygiene/generic_params.stderr @@ -0,0 +1,6 @@ +warning: the feature `const_generics` is incomplete and may cause the compiler to crash + --> $DIR/generic_params.rs:6:37 + | +LL | #![feature(decl_macro, rustc_attrs, const_generics)] + | ^^^^^^^^^^^^^^ + diff --git a/src/test/ui/hygiene/ty_params.rs b/src/test/ui/hygiene/ty_params.rs deleted file mode 100644 index b296bfe59888..000000000000 --- a/src/test/ui/hygiene/ty_params.rs +++ /dev/null @@ -1,14 +0,0 @@ -// check-pass -// ignore-pretty pretty-printing is unhygienic - -#![feature(decl_macro)] - -macro m($T:ident) { - fn f(t: T, t2: $T) -> (T, $T) { - (t, t2) - } -} - -m!(T); - -fn main() {} From dfad725be540137e0bc3022fe5341378e4690b9b Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Wed, 24 Jul 2019 10:26:32 +0200 Subject: [PATCH 05/25] Recover 'for ( $pat in $expr ) $block'. --- src/libsyntax/parse/diagnostics.rs | 44 +++++++++++++++++++ src/libsyntax/parse/parser.rs | 11 +++++ .../recover-for-loop-parens-around-head.rs | 15 +++++++ ...recover-for-loop-parens-around-head.stderr | 27 ++++++++++++ 4 files changed, 97 insertions(+) create mode 100644 src/test/ui/parser/recover-for-loop-parens-around-head.rs create mode 100644 src/test/ui/parser/recover-for-loop-parens-around-head.stderr diff --git a/src/libsyntax/parse/diagnostics.rs b/src/libsyntax/parse/diagnostics.rs index f4fc87506f35..e9dcfa81343d 100644 --- a/src/libsyntax/parse/diagnostics.rs +++ b/src/libsyntax/parse/diagnostics.rs @@ -923,6 +923,50 @@ pub fn unexpected_try_recover( } } + /// Recover a situation like `for ( $pat in $expr )` + /// and suggest writing `for $pat in $expr` instead. + /// + /// This should be called before parsing the `$block`. + crate fn recover_parens_around_for_head( + &mut self, + pat: P, + expr: &Expr, + begin_paren: Option, + ) -> P { + match (&self.token.kind, begin_paren) { + (token::CloseDelim(token::Paren), Some(begin_par_sp)) => { + self.bump(); + + let pat_str = self + .sess + .source_map() + // Remove the `(` from the span of the pattern: + .span_to_snippet(pat.span.trim_start(begin_par_sp).unwrap()) + .unwrap_or_else(|_| pprust::pat_to_string(&pat)); + + self.struct_span_err(self.prev_span, "unexpected closing `)`") + .span_label(begin_par_sp, "opening `(`") + .span_suggestion( + begin_par_sp.to(self.prev_span), + "remove parenthesis in `for` loop", + format!("{} in {}", pat_str, pprust::expr_to_string(&expr)), + // With e.g. `for (x) in y)` this would replace `(x) in y)` + // with `x) in y)` which is syntactically invalid. + // However, this is prevented before we get here. + Applicability::MachineApplicable, + ) + .emit(); + + // Unwrap `(pat)` into `pat` to avoid the `unused_parens` lint. + pat.and_then(|pat| match pat.node { + PatKind::Paren(pat) => pat, + _ => P(pat), + }) + } + _ => pat, + } + } + crate fn could_ascription_be_path(&self, node: &ast::ExprKind) -> bool { self.token.is_ident() && if let ast::ExprKind::Path(..) = node { true } else { false } && diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 8f8ed4111808..42030acf9df8 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3275,6 +3275,14 @@ fn parse_for_expr(&mut self, opt_label: Option