From eed140792d2a32e665e8fc8fb7863aa1da01ab8e Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Fri, 28 Dec 2018 14:15:55 -0700 Subject: [PATCH 01/23] Update std/lib.rs docs to reflect Rust 2018 usage --- src/libstd/lib.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index aa4278a879981..5a33f3e7a37df 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -7,11 +7,9 @@ //! primitives](#primitives), [standard macros](#macros), [I/O] and //! [multithreading], among [many other things][other]. //! -//! `std` is available to all Rust crates by default, just as if each one -//! contained an `extern crate std;` import at the [crate root]. Therefore the +//! `std` is available to all Rust crates by default. Therefore the //! standard library can be accessed in [`use`] statements through the path -//! `std`, as in [`use std::env`], or in expressions through the absolute path -//! `::std`, as in [`::std::env::args`]. +//! `std`, as in [`use std::env`]. //! //! # How to read this documentation //! From 13dc584db39e5f36755b7b051a908277f5e5505f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Fri, 18 Jan 2019 12:35:14 +0100 Subject: [PATCH 02/23] Merge visitors in AST validation --- src/librustc_passes/ast_validation.rs | 255 +++++++++++--------------- 1 file changed, 111 insertions(+), 144 deletions(-) diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 3d0e46d998622..20f63b142997c 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -20,9 +20,79 @@ use errors::Applicability; struct AstValidator<'a> { session: &'a Session, + + // Used to ban nested `impl Trait`, e.g., `impl Into`. + // Nested `impl Trait` _is_ allowed in associated type position, + // e.g `impl Iterator` + outer_impl_trait: Option, + + // Used to ban `impl Trait` in path projections like `::Item` + // or `Foo::Bar` + is_impl_trait_banned: bool, } impl<'a> AstValidator<'a> { + fn with_banned_impl_trait(&mut self, f: F) + where F: FnOnce(&mut Self) + { + let old_is_impl_trait_banned = self.is_impl_trait_banned; + self.is_impl_trait_banned = true; + f(self); + self.is_impl_trait_banned = old_is_impl_trait_banned; + } + + fn with_impl_trait(&mut self, outer_impl_trait: Option, f: F) + where F: FnOnce(&mut Self) + { + let old_outer_impl_trait = self.outer_impl_trait; + self.outer_impl_trait = outer_impl_trait; + f(self); + self.outer_impl_trait = old_outer_impl_trait; + } + + // Mirrors visit::walk_ty, but tracks relevant state + fn walk_ty(&mut self, t: &'a Ty) { + match t.node { + TyKind::ImplTrait(..) => { + self.with_impl_trait(Some(t.span), |this| visit::walk_ty(this, t)) + } + TyKind::Path(ref qself, ref path) => { + // We allow these: + // - `Option` + // - `option::Option` + // - `option::Option::Foo + // + // But not these: + // - `::Foo` + // - `option::Option::Foo`. + // + // To implement this, we disallow `impl Trait` from `qself` + // (for cases like `::Foo>`) + // but we allow `impl Trait` in `GenericArgs` + // iff there are no more PathSegments. + if let Some(ref qself) = *qself { + // `impl Trait` in `qself` is always illegal + self.with_banned_impl_trait(|this| this.visit_ty(&qself.ty)); + } + + // Note that there should be a call to visit_path here, + // so if any logic is added to process `Path`s a call to it should be + // added both in visit_path and here. This code mirrors visit::walk_path. + for (i, segment) in path.segments.iter().enumerate() { + // Allow `impl Trait` iff we're on the final path segment + if i == path.segments.len() - 1 { + self.visit_path_segment(path.span, segment); + } else { + self.with_banned_impl_trait(|this| { + this.visit_path_segment(path.span, segment) + }); + } + } + } + _ => visit::walk_ty(self, t), + } + } + fn err_handler(&self) -> &errors::Handler { &self.session.diagnostic() } @@ -267,6 +337,19 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.no_questions_in_bounds(bounds, "trait object types", false); } TyKind::ImplTrait(_, ref bounds) => { + if self.is_impl_trait_banned { + struct_span_err!(self.session, ty.span, E0667, + "`impl Trait` is not allowed in path parameters").emit(); + } + + if let Some(outer_impl_trait) = self.outer_impl_trait { + struct_span_err!(self.session, ty.span, E0666, + "nested `impl Trait` is not allowed") + .span_label(outer_impl_trait, "outer `impl Trait`") + .span_label(ty.span, "nested `impl Trait` here") + .emit(); + + } if !bounds.iter() .any(|b| if let GenericBound::Trait(..) = *b { true } else { false }) { self.err_handler().span_err(ty.span, "at least one trait must be specified"); @@ -275,7 +358,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { _ => {} } - visit::walk_ty(self, ty) + self.walk_ty(ty) } fn visit_label(&mut self, label: &'a Label) { @@ -414,6 +497,28 @@ impl<'a> Visitor<'a> for AstValidator<'a> { visit::walk_foreign_item(self, fi) } + // Mirrors visit::walk_generic_args, but tracks relevant state + fn visit_generic_args(&mut self, _: Span, generic_args: &'a GenericArgs) { + match *generic_args { + GenericArgs::AngleBracketed(ref data) => { + walk_list!(self, visit_generic_arg, &data.args); + // Type bindings such as `Item=impl Debug` in `Iterator` + // are allowed to contain nested `impl Trait`. + self.with_impl_trait(None, |this| { + walk_list!(this, visit_assoc_type_binding, &data.bindings); + }); + } + GenericArgs::Parenthesized(ref data) => { + walk_list!(self, visit_ty, &data.inputs); + if let Some(ref type_) = data.output { + // `-> Foo` syntax is essentially an associated type binding, + // so it is also allowed to contain nested `impl Trait`. + self.with_impl_trait(None, |this| visit::walk_ty(this, type_)); + } + } + } + } + fn visit_generics(&mut self, generics: &'a Generics) { let mut seen_non_lifetime_param = false; let mut seen_default = None; @@ -490,148 +595,10 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } } -// Bans nested `impl Trait`, e.g., `impl Into`. -// Nested `impl Trait` _is_ allowed in associated type position, -// e.g `impl Iterator` -struct NestedImplTraitVisitor<'a> { - session: &'a Session, - outer_impl_trait: Option, -} - -impl<'a> NestedImplTraitVisitor<'a> { - fn with_impl_trait(&mut self, outer_impl_trait: Option, f: F) - where F: FnOnce(&mut NestedImplTraitVisitor<'a>) - { - let old_outer_impl_trait = self.outer_impl_trait; - self.outer_impl_trait = outer_impl_trait; - f(self); - self.outer_impl_trait = old_outer_impl_trait; - } -} - - -impl<'a> Visitor<'a> for NestedImplTraitVisitor<'a> { - fn visit_ty(&mut self, t: &'a Ty) { - if let TyKind::ImplTrait(..) = t.node { - if let Some(outer_impl_trait) = self.outer_impl_trait { - struct_span_err!(self.session, t.span, E0666, - "nested `impl Trait` is not allowed") - .span_label(outer_impl_trait, "outer `impl Trait`") - .span_label(t.span, "nested `impl Trait` here") - .emit(); - - } - self.with_impl_trait(Some(t.span), |this| visit::walk_ty(this, t)); - } else { - visit::walk_ty(self, t); - } - } - fn visit_generic_args(&mut self, _: Span, generic_args: &'a GenericArgs) { - match *generic_args { - GenericArgs::AngleBracketed(ref data) => { - for arg in &data.args { - self.visit_generic_arg(arg) - } - for type_binding in &data.bindings { - // Type bindings such as `Item=impl Debug` in `Iterator` - // are allowed to contain nested `impl Trait`. - self.with_impl_trait(None, |this| visit::walk_ty(this, &type_binding.ty)); - } - } - GenericArgs::Parenthesized(ref data) => { - for type_ in &data.inputs { - self.visit_ty(type_); - } - if let Some(ref type_) = data.output { - // `-> Foo` syntax is essentially an associated type binding, - // so it is also allowed to contain nested `impl Trait`. - self.with_impl_trait(None, |this| visit::walk_ty(this, type_)); - } - } - } - } - - fn visit_mac(&mut self, _mac: &Spanned) { - // covered in AstValidator - } -} - -// Bans `impl Trait` in path projections like `::Item` or `Foo::Bar`. -struct ImplTraitProjectionVisitor<'a> { - session: &'a Session, - is_banned: bool, -} - -impl<'a> ImplTraitProjectionVisitor<'a> { - fn with_ban(&mut self, f: F) - where F: FnOnce(&mut ImplTraitProjectionVisitor<'a>) - { - let old_is_banned = self.is_banned; - self.is_banned = true; - f(self); - self.is_banned = old_is_banned; - } -} - -impl<'a> Visitor<'a> for ImplTraitProjectionVisitor<'a> { - fn visit_ty(&mut self, t: &'a Ty) { - match t.node { - TyKind::ImplTrait(..) => { - if self.is_banned { - struct_span_err!(self.session, t.span, E0667, - "`impl Trait` is not allowed in path parameters").emit(); - } - } - TyKind::Path(ref qself, ref path) => { - // We allow these: - // - `Option` - // - `option::Option` - // - `option::Option::Foo - // - // But not these: - // - `::Foo` - // - `option::Option::Foo`. - // - // To implement this, we disallow `impl Trait` from `qself` - // (for cases like `::Foo>`) - // but we allow `impl Trait` in `GenericArgs` - // iff there are no more PathSegments. - if let Some(ref qself) = *qself { - // `impl Trait` in `qself` is always illegal - self.with_ban(|this| this.visit_ty(&qself.ty)); - } - - for (i, segment) in path.segments.iter().enumerate() { - // Allow `impl Trait` iff we're on the final path segment - if i == path.segments.len() - 1 { - visit::walk_path_segment(self, path.span, segment); - } else { - self.with_ban(|this| - visit::walk_path_segment(this, path.span, segment)); - } - } - } - _ => visit::walk_ty(self, t), - } - } - - fn visit_mac(&mut self, _mac: &Spanned) { - // covered in AstValidator - } -} - pub fn check_crate(session: &Session, krate: &Crate) { - visit::walk_crate( - &mut NestedImplTraitVisitor { - session, - outer_impl_trait: None, - }, krate); - - visit::walk_crate( - &mut ImplTraitProjectionVisitor { - session, - is_banned: false, - }, krate); - - visit::walk_crate(&mut AstValidator { session }, krate) + visit::walk_crate(&mut AstValidator { + session, + outer_impl_trait: None, + is_impl_trait_banned: false, + }, krate) } From a5d4aeddc8e2ecf1279f2138788777a29fedc3c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Sat, 19 Jan 2019 04:29:26 +0100 Subject: [PATCH 03/23] Address some comments --- src/librustc_passes/ast_validation.rs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 20f63b142997c..d1a3d7c1f81e0 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -6,6 +6,7 @@ // This pass is supposed to perform only simple checks not requiring name resolution // or type checking or some other kind of complex analysis. +use std::mem; use rustc::lint; use rustc::session::Session; use syntax::ast::*; @@ -32,22 +33,16 @@ struct AstValidator<'a> { } impl<'a> AstValidator<'a> { - fn with_banned_impl_trait(&mut self, f: F) - where F: FnOnce(&mut Self) - { - let old_is_impl_trait_banned = self.is_impl_trait_banned; - self.is_impl_trait_banned = true; + fn with_banned_impl_trait(&mut self, f: impl FnOnce(&mut Self)) { + let old = mem::replace(&mut self.is_impl_trait_banned, true); f(self); - self.is_impl_trait_banned = old_is_impl_trait_banned; + self.is_impl_trait_banned = old; } - fn with_impl_trait(&mut self, outer_impl_trait: Option, f: F) - where F: FnOnce(&mut Self) - { - let old_outer_impl_trait = self.outer_impl_trait; - self.outer_impl_trait = outer_impl_trait; + fn with_impl_trait(&mut self, outer_impl_trait: Option, f: impl FnOnce(&mut Self)) { + let old = mem::replace(&mut self.outer_impl_trait, outer_impl_trait); f(self); - self.outer_impl_trait = old_outer_impl_trait; + self.outer_impl_trait = old; } // Mirrors visit::walk_ty, but tracks relevant state From b1f169fe7a19cf10f70ee2aa2513276185c70e9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 20 Jan 2019 00:37:06 -0800 Subject: [PATCH 04/23] Recover from parse errors in struct literal fields Attempt to recover from parse errors while parsing a struct's literal fields by skipping tokens until a comma or the closing brace is found. This allows errors in other fields to be reported. --- src/libsyntax/parse/parser.rs | 47 ++++++++++++++--- src/test/ui/issues/issue-52496.rs | 13 +++++ src/test/ui/issues/issue-52496.stderr | 50 +++++++++++++++++++ .../ui/parser/removed-syntax-with-1.stderr | 4 +- .../ui/parser/removed-syntax-with-2.stderr | 4 +- .../parser/struct-field-numeric-shorthand.rs | 7 ++- .../struct-field-numeric-shorthand.stderr | 22 ++++++-- 7 files changed, 132 insertions(+), 15 deletions(-) create mode 100644 src/test/ui/issues/issue-52496.rs create mode 100644 src/test/ui/issues/issue-52496.stderr diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 7e15b23127655..9b20937cf933b 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -100,6 +100,7 @@ pub enum PathStyle { enum SemiColonMode { Break, Ignore, + Comma, } #[derive(Clone, Copy, PartialEq, Debug)] @@ -2656,18 +2657,37 @@ impl<'a> Parser<'a> { break; } + let mut recovery_field = None; + if let token::Ident(ident, _) = self.token { + if !self.token.is_reserved_ident() { + let mut ident = ident.clone(); + ident.span = self.span; + recovery_field = Some(ast::Field { + ident, + span: self.span, + expr: self.mk_expr(self.span, ExprKind::Err, ThinVec::new()), + is_shorthand: true, + attrs: ThinVec::new(), + }); + } + } match self.parse_field() { Ok(f) => fields.push(f), Err(mut e) => { e.span_label(struct_sp, "while parsing this struct"); e.emit(); + if let Some(f) = recovery_field { + fields.push(f); + } // If the next token is a comma, then try to parse // what comes next as additional fields, rather than // bailing out until next `}`. if self.token != token::Comma { - self.recover_stmt(); - break; + self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore); + if self.token != token::Comma { + break; + } } } } @@ -2676,9 +2696,10 @@ impl<'a> Parser<'a> { &[token::CloseDelim(token::Brace)]) { Ok(()) => {} Err(mut e) => { + e.span_label(struct_sp, "while parsing this struct"); e.emit(); - self.recover_stmt(); - break; + self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore); + self.eat(&token::Comma); } } } @@ -4538,13 +4559,13 @@ impl<'a> Parser<'a> { token::CloseDelim(token::DelimToken::Brace) => { if brace_depth == 0 { debug!("recover_stmt_ return - close delim {:?}", self.token); - return; + break; } brace_depth -= 1; self.bump(); if in_block && bracket_depth == 0 && brace_depth == 0 { debug!("recover_stmt_ return - block end {:?}", self.token); - return; + break; } } token::CloseDelim(token::DelimToken::Bracket) => { @@ -4556,7 +4577,7 @@ impl<'a> Parser<'a> { } token::Eof => { debug!("recover_stmt_ return - Eof"); - return; + break; } token::Semi => { self.bump(); @@ -4564,7 +4585,17 @@ impl<'a> Parser<'a> { brace_depth == 0 && bracket_depth == 0 { debug!("recover_stmt_ return - Semi"); - return; + break; + } + } + token::Comma => { + if break_on_semi == SemiColonMode::Comma && + brace_depth == 0 && + bracket_depth == 0 { + debug!("recover_stmt_ return - Semi"); + break; + } else { + self.bump(); } } _ => { diff --git a/src/test/ui/issues/issue-52496.rs b/src/test/ui/issues/issue-52496.rs new file mode 100644 index 0000000000000..d2636b7ecb3f8 --- /dev/null +++ b/src/test/ui/issues/issue-52496.rs @@ -0,0 +1,13 @@ +struct Foo { bar: f64, baz: i64, bat: i64 } + +fn main() { + let _ = Foo { bar: .5, baz: 42 }; + //~^ ERROR expected expression + //~| ERROR missing field `bat` in initializer of `Foo` + let bar = 1.5f32; + let _ = Foo { bar.into(), bat: -1, . }; + //~^ ERROR expected one of + //~| ERROR mismatched types + //~| ERROR missing field `baz` in initializer of `Foo` + //~| ERROR expected identifier, found `.` +} diff --git a/src/test/ui/issues/issue-52496.stderr b/src/test/ui/issues/issue-52496.stderr new file mode 100644 index 0000000000000..c98de6ffbed40 --- /dev/null +++ b/src/test/ui/issues/issue-52496.stderr @@ -0,0 +1,50 @@ +error: expected expression, found `.` + --> $DIR/issue-52496.rs:4:24 + | +LL | let _ = Foo { bar: .5, baz: 42 }; + | --- ^ expected expression + | | + | while parsing this struct + +error: expected one of `,` or `}`, found `.` + --> $DIR/issue-52496.rs:8:22 + | +LL | let _ = Foo { bar.into(), bat: -1, . }; + | --- ^ expected one of `,` or `}` here + | | + | while parsing this struct + +error: expected identifier, found `.` + --> $DIR/issue-52496.rs:8:40 + | +LL | let _ = Foo { bar.into(), bat: -1, . }; + | --- ^ expected identifier + | | + | while parsing this struct + +error[E0063]: missing field `bat` in initializer of `Foo` + --> $DIR/issue-52496.rs:4:13 + | +LL | let _ = Foo { bar: .5, baz: 42 }; + | ^^^ missing `bat` + +error[E0308]: mismatched types + --> $DIR/issue-52496.rs:8:19 + | +LL | let _ = Foo { bar.into(), bat: -1, . }; + | ^^^ expected f64, found f32 +help: you can cast an `f32` to `f64` in a lossless way + | +LL | let _ = Foo { bar: bar.into().into(), bat: -1, . }; + | ^^^^^^^^^^^^^^^ + +error[E0063]: missing field `baz` in initializer of `Foo` + --> $DIR/issue-52496.rs:8:13 + | +LL | let _ = Foo { bar.into(), bat: -1, . }; + | ^^^ missing `baz` + +error: aborting due to 6 previous errors + +Some errors occurred: E0063, E0308. +For more information about an error, try `rustc --explain E0063`. diff --git a/src/test/ui/parser/removed-syntax-with-1.stderr b/src/test/ui/parser/removed-syntax-with-1.stderr index 77ed4fcea517c..b5956ad339db8 100644 --- a/src/test/ui/parser/removed-syntax-with-1.stderr +++ b/src/test/ui/parser/removed-syntax-with-1.stderr @@ -2,7 +2,9 @@ error: expected one of `,`, `.`, `?`, `}`, or an operator, found `with` --> $DIR/removed-syntax-with-1.rs:8:25 | LL | let b = S { foo: () with a }; - | ^^^^ expected one of `,`, `.`, `?`, `}`, or an operator here + | - ^^^^ expected one of `,`, `.`, `?`, `}`, or an operator here + | | + | while parsing this struct error[E0063]: missing field `bar` in initializer of `main::S` --> $DIR/removed-syntax-with-1.rs:8:13 diff --git a/src/test/ui/parser/removed-syntax-with-2.stderr b/src/test/ui/parser/removed-syntax-with-2.stderr index 5642d2f45ffce..ee7560017a675 100644 --- a/src/test/ui/parser/removed-syntax-with-2.stderr +++ b/src/test/ui/parser/removed-syntax-with-2.stderr @@ -2,7 +2,9 @@ error: expected one of `,` or `}`, found `a` --> $DIR/removed-syntax-with-2.rs:8:31 | LL | let b = S { foo: (), with a }; - | ^ expected one of `,` or `}` here + | - ^ expected one of `,` or `}` here + | | + | while parsing this struct error[E0425]: cannot find value `with` in this scope --> $DIR/removed-syntax-with-2.rs:8:26 diff --git a/src/test/ui/parser/struct-field-numeric-shorthand.rs b/src/test/ui/parser/struct-field-numeric-shorthand.rs index 914588f51e1e3..58c40b3d96a49 100644 --- a/src/test/ui/parser/struct-field-numeric-shorthand.rs +++ b/src/test/ui/parser/struct-field-numeric-shorthand.rs @@ -1,6 +1,9 @@ struct Rgb(u8, u8, u8); fn main() { - let _ = Rgb { 0, 1, 2 }; //~ ERROR expected identifier, found `0` - //~| ERROR missing fields `0`, `1`, `2` in initializer of `Rgb` + let _ = Rgb { 0, 1, 2 }; + //~^ ERROR expected identifier, found `0` + //~| ERROR expected identifier, found `1` + //~| ERROR expected identifier, found `2` + //~| ERROR missing fields `0`, `1`, `2` in initializer of `Rgb` } diff --git a/src/test/ui/parser/struct-field-numeric-shorthand.stderr b/src/test/ui/parser/struct-field-numeric-shorthand.stderr index f5dc226934ec6..cfb1f82014754 100644 --- a/src/test/ui/parser/struct-field-numeric-shorthand.stderr +++ b/src/test/ui/parser/struct-field-numeric-shorthand.stderr @@ -1,17 +1,33 @@ error: expected identifier, found `0` --> $DIR/struct-field-numeric-shorthand.rs:4:19 | -LL | let _ = Rgb { 0, 1, 2 }; //~ ERROR expected identifier, found `0` +LL | let _ = Rgb { 0, 1, 2 }; | --- ^ expected identifier | | | while parsing this struct +error: expected identifier, found `1` + --> $DIR/struct-field-numeric-shorthand.rs:4:22 + | +LL | let _ = Rgb { 0, 1, 2 }; + | --- ^ expected identifier + | | + | while parsing this struct + +error: expected identifier, found `2` + --> $DIR/struct-field-numeric-shorthand.rs:4:25 + | +LL | let _ = Rgb { 0, 1, 2 }; + | --- ^ expected identifier + | | + | while parsing this struct + error[E0063]: missing fields `0`, `1`, `2` in initializer of `Rgb` --> $DIR/struct-field-numeric-shorthand.rs:4:13 | -LL | let _ = Rgb { 0, 1, 2 }; //~ ERROR expected identifier, found `0` +LL | let _ = Rgb { 0, 1, 2 }; | ^^^ missing `0`, `1`, `2` -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0063`. From acbda76f23b8945fd8f45332352269044ecbf2ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 20 Jan 2019 01:49:04 -0800 Subject: [PATCH 05/23] Recover with suggestion from writing `.42` instead of `0.42` --- src/libsyntax/parse/parser.rs | 26 +++++++++++++++++++++++ src/test/ui/issues/issue-52496.rs | 3 ++- src/test/ui/issues/issue-52496.stderr | 30 +++++++++++++++++---------- 3 files changed, 47 insertions(+), 12 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 9b20937cf933b..43a263b8a6b0e 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1989,6 +1989,32 @@ impl<'a> Parser<'a> { result.unwrap() } + token::Dot if self.look_ahead(1, |t| match t { + token::Literal(parse::token::Lit::Integer(_) , None) => true, + _ => false, + }) => { // recover from `let x = .4;` + let lo = self.span; + self.bump(); + if let token::Literal( + parse::token::Lit::Integer(val), + None + ) = self.token { + self.bump(); + let sp = lo.to(self.prev_span); + let mut err = self.diagnostic() + .struct_span_err(sp, "numeric float literals must have a significant"); + err.span_suggestion_with_applicability( + sp, + "numeric float literals must have a significant", + format!("0.{}", val), + Applicability::MachineApplicable, + ); + err.emit(); + return Ok(ast::LitKind::Float(val, ast::FloatTy::F32)); + } else { + unreachable!(); + }; + } _ => { return self.unexpected_last(&self.token); } }; diff --git a/src/test/ui/issues/issue-52496.rs b/src/test/ui/issues/issue-52496.rs index d2636b7ecb3f8..2e79079267540 100644 --- a/src/test/ui/issues/issue-52496.rs +++ b/src/test/ui/issues/issue-52496.rs @@ -2,8 +2,9 @@ struct Foo { bar: f64, baz: i64, bat: i64 } fn main() { let _ = Foo { bar: .5, baz: 42 }; - //~^ ERROR expected expression + //~^ ERROR numeric float literals must have a significant //~| ERROR missing field `bat` in initializer of `Foo` + //~| ERROR mismatched types let bar = 1.5f32; let _ = Foo { bar.into(), bat: -1, . }; //~^ ERROR expected one of diff --git a/src/test/ui/issues/issue-52496.stderr b/src/test/ui/issues/issue-52496.stderr index c98de6ffbed40..3c8056316603b 100644 --- a/src/test/ui/issues/issue-52496.stderr +++ b/src/test/ui/issues/issue-52496.stderr @@ -1,13 +1,11 @@ -error: expected expression, found `.` +error: numeric float literals must have a significant --> $DIR/issue-52496.rs:4:24 | LL | let _ = Foo { bar: .5, baz: 42 }; - | --- ^ expected expression - | | - | while parsing this struct + | ^^ help: numeric float literals must have a significant: `0.5` error: expected one of `,` or `}`, found `.` - --> $DIR/issue-52496.rs:8:22 + --> $DIR/issue-52496.rs:9:22 | LL | let _ = Foo { bar.into(), bat: -1, . }; | --- ^ expected one of `,` or `}` here @@ -15,13 +13,23 @@ LL | let _ = Foo { bar.into(), bat: -1, . }; | while parsing this struct error: expected identifier, found `.` - --> $DIR/issue-52496.rs:8:40 + --> $DIR/issue-52496.rs:9:40 | LL | let _ = Foo { bar.into(), bat: -1, . }; | --- ^ expected identifier | | | while parsing this struct +error[E0308]: mismatched types + --> $DIR/issue-52496.rs:4:24 + | +LL | let _ = Foo { bar: .5, baz: 42 }; + | ^^ expected f64, found f32 +help: change the type of the numeric literal from `f32` to `f64` + | +LL | let _ = Foo { bar: .5f64, baz: 42 }; + | ^^^^^ + error[E0063]: missing field `bat` in initializer of `Foo` --> $DIR/issue-52496.rs:4:13 | @@ -29,22 +37,22 @@ LL | let _ = Foo { bar: .5, baz: 42 }; | ^^^ missing `bat` error[E0308]: mismatched types - --> $DIR/issue-52496.rs:8:19 + --> $DIR/issue-52496.rs:9:19 | LL | let _ = Foo { bar.into(), bat: -1, . }; | ^^^ expected f64, found f32 help: you can cast an `f32` to `f64` in a lossless way | -LL | let _ = Foo { bar: bar.into().into(), bat: -1, . }; - | ^^^^^^^^^^^^^^^ +LL | let _ = Foo { bar.into().into(), bat: -1, . }; + | ^^^^^^^^^^ error[E0063]: missing field `baz` in initializer of `Foo` - --> $DIR/issue-52496.rs:8:13 + --> $DIR/issue-52496.rs:9:13 | LL | let _ = Foo { bar.into(), bat: -1, . }; | ^^^ missing `baz` -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors Some errors occurred: E0063, E0308. For more information about an error, try `rustc --explain E0063`. From e387597a8f789ab6e37e6ce1bf67c8c45d4827c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 20 Jan 2019 13:59:35 -0800 Subject: [PATCH 06/23] Reword message for incorrect float literal --- src/libsyntax/parse/parser.rs | 4 ++-- src/test/ui/issues/issue-52496.rs | 2 +- src/test/ui/issues/issue-52496.stderr | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 43a263b8a6b0e..fe6fa5e97d721 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2002,10 +2002,10 @@ impl<'a> Parser<'a> { self.bump(); let sp = lo.to(self.prev_span); let mut err = self.diagnostic() - .struct_span_err(sp, "numeric float literals must have a significant"); + .struct_span_err(sp, "float literals must have an integer part"); err.span_suggestion_with_applicability( sp, - "numeric float literals must have a significant", + "must have an integer part", format!("0.{}", val), Applicability::MachineApplicable, ); diff --git a/src/test/ui/issues/issue-52496.rs b/src/test/ui/issues/issue-52496.rs index 2e79079267540..e734e9bc51332 100644 --- a/src/test/ui/issues/issue-52496.rs +++ b/src/test/ui/issues/issue-52496.rs @@ -2,7 +2,7 @@ struct Foo { bar: f64, baz: i64, bat: i64 } fn main() { let _ = Foo { bar: .5, baz: 42 }; - //~^ ERROR numeric float literals must have a significant + //~^ ERROR float literals must have an integer part //~| ERROR missing field `bat` in initializer of `Foo` //~| ERROR mismatched types let bar = 1.5f32; diff --git a/src/test/ui/issues/issue-52496.stderr b/src/test/ui/issues/issue-52496.stderr index 3c8056316603b..e69b9b7c87f40 100644 --- a/src/test/ui/issues/issue-52496.stderr +++ b/src/test/ui/issues/issue-52496.stderr @@ -1,8 +1,8 @@ -error: numeric float literals must have a significant +error: float literals must have an integer part --> $DIR/issue-52496.rs:4:24 | LL | let _ = Foo { bar: .5, baz: 42 }; - | ^^ help: numeric float literals must have a significant: `0.5` + | ^^ help: must have an integer part: `0.5` error: expected one of `,` or `}`, found `.` --> $DIR/issue-52496.rs:9:22 From 15bad8bbfd3125b1e94d04f274910e24d0bb63eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 20 Jan 2019 14:25:53 -0800 Subject: [PATCH 07/23] Extend incorrect float literal recovery to account for suffixes --- src/libsyntax/parse/parser.rs | 20 +++++++-- src/test/ui/issues/issue-52496.rs | 1 - src/test/ui/issues/issue-52496.stderr | 20 +++------ .../ui/suggestions/recover-invalid-float.rs | 11 +++++ .../suggestions/recover-invalid-float.stderr | 42 +++++++++++++++++++ 5 files changed, 74 insertions(+), 20 deletions(-) create mode 100644 src/test/ui/suggestions/recover-invalid-float.rs create mode 100644 src/test/ui/suggestions/recover-invalid-float.stderr diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index fe6fa5e97d721..038d949d24aa6 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1990,15 +1990,23 @@ impl<'a> Parser<'a> { result.unwrap() } token::Dot if self.look_ahead(1, |t| match t { - token::Literal(parse::token::Lit::Integer(_) , None) => true, + token::Literal(parse::token::Lit::Integer(_) , _) => true, _ => false, }) => { // recover from `let x = .4;` let lo = self.span; self.bump(); if let token::Literal( parse::token::Lit::Integer(val), - None + suffix, ) = self.token { + let suffix = suffix.and_then(|s| { + let s = s.as_str().get(); + if ["f32", "f64"].contains(&s) { + Some(s) + } else { + None + } + }).unwrap_or(""); self.bump(); let sp = lo.to(self.prev_span); let mut err = self.diagnostic() @@ -2006,11 +2014,15 @@ impl<'a> Parser<'a> { err.span_suggestion_with_applicability( sp, "must have an integer part", - format!("0.{}", val), + format!("0.{}{}", val, suffix), Applicability::MachineApplicable, ); err.emit(); - return Ok(ast::LitKind::Float(val, ast::FloatTy::F32)); + return Ok(match suffix { + "f32" => ast::LitKind::Float(val, ast::FloatTy::F32), + "f64" => ast::LitKind::Float(val, ast::FloatTy::F64), + _ => ast::LitKind::FloatUnsuffixed(val), + }); } else { unreachable!(); }; diff --git a/src/test/ui/issues/issue-52496.rs b/src/test/ui/issues/issue-52496.rs index e734e9bc51332..e9ffeaf6c8901 100644 --- a/src/test/ui/issues/issue-52496.rs +++ b/src/test/ui/issues/issue-52496.rs @@ -4,7 +4,6 @@ fn main() { let _ = Foo { bar: .5, baz: 42 }; //~^ ERROR float literals must have an integer part //~| ERROR missing field `bat` in initializer of `Foo` - //~| ERROR mismatched types let bar = 1.5f32; let _ = Foo { bar.into(), bat: -1, . }; //~^ ERROR expected one of diff --git a/src/test/ui/issues/issue-52496.stderr b/src/test/ui/issues/issue-52496.stderr index e69b9b7c87f40..12fe7e7fc1f05 100644 --- a/src/test/ui/issues/issue-52496.stderr +++ b/src/test/ui/issues/issue-52496.stderr @@ -5,7 +5,7 @@ LL | let _ = Foo { bar: .5, baz: 42 }; | ^^ help: must have an integer part: `0.5` error: expected one of `,` or `}`, found `.` - --> $DIR/issue-52496.rs:9:22 + --> $DIR/issue-52496.rs:8:22 | LL | let _ = Foo { bar.into(), bat: -1, . }; | --- ^ expected one of `,` or `}` here @@ -13,23 +13,13 @@ LL | let _ = Foo { bar.into(), bat: -1, . }; | while parsing this struct error: expected identifier, found `.` - --> $DIR/issue-52496.rs:9:40 + --> $DIR/issue-52496.rs:8:40 | LL | let _ = Foo { bar.into(), bat: -1, . }; | --- ^ expected identifier | | | while parsing this struct -error[E0308]: mismatched types - --> $DIR/issue-52496.rs:4:24 - | -LL | let _ = Foo { bar: .5, baz: 42 }; - | ^^ expected f64, found f32 -help: change the type of the numeric literal from `f32` to `f64` - | -LL | let _ = Foo { bar: .5f64, baz: 42 }; - | ^^^^^ - error[E0063]: missing field `bat` in initializer of `Foo` --> $DIR/issue-52496.rs:4:13 | @@ -37,7 +27,7 @@ LL | let _ = Foo { bar: .5, baz: 42 }; | ^^^ missing `bat` error[E0308]: mismatched types - --> $DIR/issue-52496.rs:9:19 + --> $DIR/issue-52496.rs:8:19 | LL | let _ = Foo { bar.into(), bat: -1, . }; | ^^^ expected f64, found f32 @@ -47,12 +37,12 @@ LL | let _ = Foo { bar.into().into(), bat: -1, . }; | ^^^^^^^^^^ error[E0063]: missing field `baz` in initializer of `Foo` - --> $DIR/issue-52496.rs:9:13 + --> $DIR/issue-52496.rs:8:13 | LL | let _ = Foo { bar.into(), bat: -1, . }; | ^^^ missing `baz` -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors Some errors occurred: E0063, E0308. For more information about an error, try `rustc --explain E0063`. diff --git a/src/test/ui/suggestions/recover-invalid-float.rs b/src/test/ui/suggestions/recover-invalid-float.rs new file mode 100644 index 0000000000000..506ef8900b881 --- /dev/null +++ b/src/test/ui/suggestions/recover-invalid-float.rs @@ -0,0 +1,11 @@ +fn main() { + let _: usize = .3; + //~^ ERROR float literals must have an integer part + //~| ERROR mismatched types + let _: usize = .42f32; + //~^ ERROR float literals must have an integer part + //~| ERROR mismatched types + let _: usize = .5f64; + //~^ ERROR float literals must have an integer part + //~| ERROR mismatched types +} diff --git a/src/test/ui/suggestions/recover-invalid-float.stderr b/src/test/ui/suggestions/recover-invalid-float.stderr new file mode 100644 index 0000000000000..c464676b444cc --- /dev/null +++ b/src/test/ui/suggestions/recover-invalid-float.stderr @@ -0,0 +1,42 @@ +error: float literals must have an integer part + --> $DIR/recover-invalid-float.rs:2:20 + | +LL | let _: usize = .3; + | ^^ help: must have an integer part: `0.3` + +error: float literals must have an integer part + --> $DIR/recover-invalid-float.rs:5:20 + | +LL | let _: usize = .42f32; + | ^^^^^^ help: must have an integer part: `0.42f32` + +error: float literals must have an integer part + --> $DIR/recover-invalid-float.rs:8:20 + | +LL | let _: usize = .5f64; + | ^^^^^ help: must have an integer part: `0.5f64` + +error[E0308]: mismatched types + --> $DIR/recover-invalid-float.rs:2:20 + | +LL | let _: usize = .3; + | ^^ expected usize, found floating-point number + | + = note: expected type `usize` + found type `{float}` + +error[E0308]: mismatched types + --> $DIR/recover-invalid-float.rs:5:20 + | +LL | let _: usize = .42f32; + | ^^^^^^ expected usize, found f32 + +error[E0308]: mismatched types + --> $DIR/recover-invalid-float.rs:8:20 + | +LL | let _: usize = .5f64; + | ^^^^^ expected usize, found f64 + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0308`. From defa61f3fb2612358b57c206c5e16da2751e6deb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 20 Jan 2019 15:16:36 -0800 Subject: [PATCH 08/23] Tweak field parse error recovery --- src/libsyntax/parse/parser.rs | 25 +++++-------------- src/test/ui/issues/issue-52496.rs | 3 +-- src/test/ui/issues/issue-52496.stderr | 19 +++----------- src/test/ui/parser/removed-syntax-with-1.rs | 2 +- .../ui/parser/removed-syntax-with-1.stderr | 4 +-- src/test/ui/parser/removed-syntax-with-2.rs | 3 +-- .../ui/parser/removed-syntax-with-2.stderr | 19 ++++---------- 7 files changed, 20 insertions(+), 55 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 038d949d24aa6..a2d3595b47206 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2695,28 +2695,12 @@ impl<'a> Parser<'a> { break; } - let mut recovery_field = None; - if let token::Ident(ident, _) = self.token { - if !self.token.is_reserved_ident() { - let mut ident = ident.clone(); - ident.span = self.span; - recovery_field = Some(ast::Field { - ident, - span: self.span, - expr: self.mk_expr(self.span, ExprKind::Err, ThinVec::new()), - is_shorthand: true, - attrs: ThinVec::new(), - }); - } - } + let mut parsed_field = None; match self.parse_field() { - Ok(f) => fields.push(f), + Ok(f) => parsed_field = Some(f), Err(mut e) => { e.span_label(struct_sp, "while parsing this struct"); e.emit(); - if let Some(f) = recovery_field { - fields.push(f); - } // If the next token is a comma, then try to parse // what comes next as additional fields, rather than @@ -2732,7 +2716,10 @@ impl<'a> Parser<'a> { match self.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Brace)]) { - Ok(()) => {} + Ok(()) => if let Some(f) = parsed_field { + // only include the field if there's no parse error + fields.push(f); + } Err(mut e) => { e.span_label(struct_sp, "while parsing this struct"); e.emit(); diff --git a/src/test/ui/issues/issue-52496.rs b/src/test/ui/issues/issue-52496.rs index e9ffeaf6c8901..4e9453653735a 100644 --- a/src/test/ui/issues/issue-52496.rs +++ b/src/test/ui/issues/issue-52496.rs @@ -7,7 +7,6 @@ fn main() { let bar = 1.5f32; let _ = Foo { bar.into(), bat: -1, . }; //~^ ERROR expected one of - //~| ERROR mismatched types - //~| ERROR missing field `baz` in initializer of `Foo` + //~| ERROR missing fields `bar`, `baz` in initializer of `Foo` //~| ERROR expected identifier, found `.` } diff --git a/src/test/ui/issues/issue-52496.stderr b/src/test/ui/issues/issue-52496.stderr index 12fe7e7fc1f05..43009a15bd49a 100644 --- a/src/test/ui/issues/issue-52496.stderr +++ b/src/test/ui/issues/issue-52496.stderr @@ -26,23 +26,12 @@ error[E0063]: missing field `bat` in initializer of `Foo` LL | let _ = Foo { bar: .5, baz: 42 }; | ^^^ missing `bat` -error[E0308]: mismatched types - --> $DIR/issue-52496.rs:8:19 - | -LL | let _ = Foo { bar.into(), bat: -1, . }; - | ^^^ expected f64, found f32 -help: you can cast an `f32` to `f64` in a lossless way - | -LL | let _ = Foo { bar.into().into(), bat: -1, . }; - | ^^^^^^^^^^ - -error[E0063]: missing field `baz` in initializer of `Foo` +error[E0063]: missing fields `bar`, `baz` in initializer of `Foo` --> $DIR/issue-52496.rs:8:13 | LL | let _ = Foo { bar.into(), bat: -1, . }; - | ^^^ missing `baz` + | ^^^ missing `bar`, `baz` -error: aborting due to 6 previous errors +error: aborting due to 5 previous errors -Some errors occurred: E0063, E0308. -For more information about an error, try `rustc --explain E0063`. +For more information about this error, try `rustc --explain E0063`. diff --git a/src/test/ui/parser/removed-syntax-with-1.rs b/src/test/ui/parser/removed-syntax-with-1.rs index 57cbe8d5be655..add024ea3907f 100644 --- a/src/test/ui/parser/removed-syntax-with-1.rs +++ b/src/test/ui/parser/removed-syntax-with-1.rs @@ -7,5 +7,5 @@ fn main() { let a = S { foo: (), bar: () }; let b = S { foo: () with a }; //~^ ERROR expected one of `,`, `.`, `?`, `}`, or an operator, found `with` - //~| ERROR missing field `bar` in initializer of `main::S` + //~| ERROR missing fields `bar`, `foo` in initializer of `main::S` } diff --git a/src/test/ui/parser/removed-syntax-with-1.stderr b/src/test/ui/parser/removed-syntax-with-1.stderr index b5956ad339db8..aae29efa85e54 100644 --- a/src/test/ui/parser/removed-syntax-with-1.stderr +++ b/src/test/ui/parser/removed-syntax-with-1.stderr @@ -6,11 +6,11 @@ LL | let b = S { foo: () with a }; | | | while parsing this struct -error[E0063]: missing field `bar` in initializer of `main::S` +error[E0063]: missing fields `bar`, `foo` in initializer of `main::S` --> $DIR/removed-syntax-with-1.rs:8:13 | LL | let b = S { foo: () with a }; - | ^ missing `bar` + | ^ missing `bar`, `foo` error: aborting due to 2 previous errors diff --git a/src/test/ui/parser/removed-syntax-with-2.rs b/src/test/ui/parser/removed-syntax-with-2.rs index 11db391c5489a..f666da49696ba 100644 --- a/src/test/ui/parser/removed-syntax-with-2.rs +++ b/src/test/ui/parser/removed-syntax-with-2.rs @@ -7,6 +7,5 @@ fn main() { let a = S { foo: (), bar: () }; let b = S { foo: (), with a }; //~^ ERROR expected one of `,` or `}`, found `a` - //~| ERROR cannot find value `with` in this scope - //~| ERROR struct `main::S` has no field named `with` + //~| ERROR missing field `bar` in initializer of `main::S` } diff --git a/src/test/ui/parser/removed-syntax-with-2.stderr b/src/test/ui/parser/removed-syntax-with-2.stderr index ee7560017a675..7717b49d3a2c7 100644 --- a/src/test/ui/parser/removed-syntax-with-2.stderr +++ b/src/test/ui/parser/removed-syntax-with-2.stderr @@ -6,21 +6,12 @@ LL | let b = S { foo: (), with a }; | | | while parsing this struct -error[E0425]: cannot find value `with` in this scope - --> $DIR/removed-syntax-with-2.rs:8:26 +error[E0063]: missing field `bar` in initializer of `main::S` + --> $DIR/removed-syntax-with-2.rs:8:13 | LL | let b = S { foo: (), with a }; - | ^^^^ not found in this scope + | ^ missing `bar` -error[E0560]: struct `main::S` has no field named `with` - --> $DIR/removed-syntax-with-2.rs:8:26 - | -LL | let b = S { foo: (), with a }; - | ^^^^ `main::S` does not have this field - | - = note: available fields are: `foo`, `bar` - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors occurred: E0425, E0560. -For more information about an error, try `rustc --explain E0425`. +For more information about this error, try `rustc --explain E0063`. From e33f7f7de1df090f890063296608dca65be55ac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 20 Jan 2019 18:42:10 -0800 Subject: [PATCH 09/23] Explain type mismatch cause pointing to return type when it is `impl Trait` --- src/librustc_typeck/check/coercion.rs | 29 +++++++++++++++---- src/test/ui/impl-trait/equality.stderr | 5 +++- ...type-err-cause-on-impl-trait-return.stderr | 14 +++++++-- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index dd63b4f20fa55..e3aae21584c21 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -1199,7 +1199,6 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E> (self.final_ty.unwrap_or(self.expected_ty), expression_ty) }; - let reason_label = "expected because of this statement"; let mut db; match cause.code { ObligationCauseCode::ReturnNoExpression => { @@ -1244,9 +1243,19 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E> // as prior return coercions would not be relevant (#57664). let parent_id = fcx.tcx.hir().get_parent_node(blk_id); let parent = fcx.tcx.hir().get(fcx.tcx.hir().get_parent_node(parent_id)); - if fcx.get_node_fn_decl(parent).is_some() && !pointing_at_return_type { + if let (Some((fn_decl, _, _)), false) = ( + fcx.get_node_fn_decl(parent), + pointing_at_return_type, + ) { if let Some(sp) = fcx.ret_coercion_span.borrow().as_ref() { - db.span_label(*sp, reason_label); + db.span_label( + fn_decl.output.span(), + "expected because this return type...", + ); + db.span_label(*sp, format!( + "...is found to be `{}` here", + fcx.resolve_type_vars_with_obligations(expected), + )); } } } @@ -1254,16 +1263,26 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E> db = fcx.report_mismatched_types(cause, expected, found, err); let _id = fcx.tcx.hir().get_parent_node(_id); let mut pointing_at_return_type = false; + let mut return_sp = None; if let Some((fn_decl, can_suggest)) = fcx.get_fn_decl(_id) { pointing_at_return_type = fcx.suggest_missing_return_type( &mut db, &fn_decl, expected, found, can_suggest); + if !pointing_at_return_type { + return_sp = Some(fn_decl.output.span()); // `impl Trait` return type + } } if let (Some(sp), false) = ( fcx.ret_coercion_span.borrow().as_ref(), pointing_at_return_type, ) { - if !sp.overlaps(cause.span) { - db.span_label(*sp, reason_label); + if let Some(return_sp) = return_sp { + db.span_label(return_sp, "expected because this return type..."); + db.span_label( *sp, format!( + "...is found to be `{}` here", + fcx.resolve_type_vars_with_obligations(expected), + )); + } else if !sp.overlaps(cause.span) { + db.span_label(*sp, "expected because of this statement"); } } } diff --git a/src/test/ui/impl-trait/equality.stderr b/src/test/ui/impl-trait/equality.stderr index 57bd70de7c68f..6cd9d07748c27 100644 --- a/src/test/ui/impl-trait/equality.stderr +++ b/src/test/ui/impl-trait/equality.stderr @@ -1,8 +1,11 @@ error[E0308]: mismatched types --> $DIR/equality.rs:15:5 | +LL | fn two(x: bool) -> impl Foo { + | -------- expected because this return type... +LL | if x { LL | return 1_i32; - | ----- expected because of this statement + | ----- ...is found to be `i32` here LL | } LL | 0_u32 | ^^^^^ expected i32, found u32 diff --git a/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr b/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr index 62da0787b02a9..5ebe00e624fc1 100644 --- a/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr +++ b/src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr @@ -1,8 +1,11 @@ error[E0308]: mismatched types --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:5:5 | +LL | fn foo() -> impl std::fmt::Display { + | ---------------------- expected because this return type... +LL | if false { LL | return 0i32; - | ---- expected because of this statement + | ---- ...is found to be `i32` here LL | } LL | 1u32 | ^^^^ expected i32, found u32 @@ -13,8 +16,11 @@ LL | 1u32 error[E0308]: mismatched types --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:13:16 | +LL | fn bar() -> impl std::fmt::Display { + | ---------------------- expected because this return type... +LL | if false { LL | return 0i32; - | ---- expected because of this statement + | ---- ...is found to be `i32` here LL | } else { LL | return 1u32; | ^^^^ expected i32, found u32 @@ -25,10 +31,12 @@ LL | return 1u32; error[E0308]: mismatched types --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:19:5 | +LL | fn baz() -> impl std::fmt::Display { + | ---------------------- expected because this return type... LL | / if false { LL | | //~^ ERROR mismatched types LL | | return 0i32; - | | ---- expected because of this statement + | | ---- ...is found to be `i32` here LL | | } else { LL | | 1u32 LL | | } From 45a95b512c6fb491518d6a3f4b667d6dd82cd56b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 20 Jan 2019 19:37:38 -0800 Subject: [PATCH 10/23] Use structured suggestion in stead of notes --- src/librustc_typeck/check/method/suggest.rs | 14 ++++++++++++-- src/libsyntax/parse/parser.rs | 13 ++++++++++--- src/test/ui/auto-ref-slice-plus-ref.stderr | 3 +-- src/test/ui/block-result/issue-3563.stderr | 4 +--- src/test/ui/empty/empty-struct-braces-expr.stderr | 10 ++++------ src/test/ui/issues/issue-23217.stderr | 5 ++--- src/test/ui/issues/issue-28344.stderr | 6 ++---- src/test/ui/issues/issue-28971.stderr | 7 ++++--- .../result-deref-err.stderr | 3 +-- src/test/ui/parser/issue-17718-const-mut.rs | 2 +- src/test/ui/parser/issue-17718-const-mut.stderr | 6 +++--- src/test/ui/suggestions/suggest-methods.stderr | 12 +++--------- 12 files changed, 44 insertions(+), 41 deletions(-) diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 23bcd88d6afb5..f71a163cee261 100644 --- a/src/librustc_typeck/check/method/suggest.rs +++ b/src/librustc_typeck/check/method/suggest.rs @@ -304,7 +304,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { ); if let Some(suggestion) = suggestion { // enum variant - err.help(&format!("did you mean `{}`?", suggestion)); + err.span_suggestion_with_applicability( + item_name.span, + "did you mean", + suggestion.to_string(), + Applicability::MaybeIncorrect, + ); } err } @@ -440,7 +445,12 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } if let Some(lev_candidate) = lev_candidate { - err.help(&format!("did you mean `{}`?", lev_candidate.ident)); + err.span_suggestion_with_applicability( + span, + "did you mean", + lev_candidate.ident.to_string(), + Applicability::MaybeIncorrect, + ); } err.emit(); } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 7e15b23127655..d4a3411f463d0 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -7271,9 +7271,16 @@ impl<'a> Parser<'a> { // CONST ITEM if self.eat_keyword(keywords::Mut) { let prev_span = self.prev_span; - self.diagnostic().struct_span_err(prev_span, "const globals cannot be mutable") - .help("did you mean to declare a static?") - .emit(); + let mut err = self.diagnostic() + .struct_span_err(prev_span, "const globals cannot be mutable"); + err.span_label(prev_span, "cannot be mutable"); + err.span_suggestion_with_applicability( + const_span, + "you might want to declare a static instead", + "static".to_owned(), + Applicability::MaybeIncorrect, + ); + err.emit(); } let (ident, item_, extra_attrs) = self.parse_item_const(None)?; let prev_span = self.prev_span; diff --git a/src/test/ui/auto-ref-slice-plus-ref.stderr b/src/test/ui/auto-ref-slice-plus-ref.stderr index ab57fec0e7337..356e24d18a78f 100644 --- a/src/test/ui/auto-ref-slice-plus-ref.stderr +++ b/src/test/ui/auto-ref-slice-plus-ref.stderr @@ -2,12 +2,11 @@ error[E0599]: no method named `test_mut` found for type `std::vec::Vec<{integer} --> $DIR/auto-ref-slice-plus-ref.rs:7:7 | LL | a.test_mut(); //~ ERROR no method named `test_mut` found - | ^^^^^^^^ + | ^^^^^^^^ help: did you mean: `get_mut` | = help: items from traits can only be used if the trait is implemented and in scope = note: the following trait defines an item `test_mut`, perhaps you need to implement it: candidate #1: `MyIter` - = help: did you mean `get_mut`? error[E0599]: no method named `test` found for type `std::vec::Vec<{integer}>` in the current scope --> $DIR/auto-ref-slice-plus-ref.rs:8:7 diff --git a/src/test/ui/block-result/issue-3563.stderr b/src/test/ui/block-result/issue-3563.stderr index 7f386630de590..a6346a5233f4c 100644 --- a/src/test/ui/block-result/issue-3563.stderr +++ b/src/test/ui/block-result/issue-3563.stderr @@ -2,9 +2,7 @@ error[E0599]: no method named `b` found for type `&Self` in the current scope --> $DIR/issue-3563.rs:3:17 | LL | || self.b() - | ^ - | - = help: did you mean `a`? + | ^ help: did you mean: `a` error: aborting due to previous error diff --git a/src/test/ui/empty/empty-struct-braces-expr.stderr b/src/test/ui/empty/empty-struct-braces-expr.stderr index e595e0ccb9293..19844503a4804 100644 --- a/src/test/ui/empty/empty-struct-braces-expr.stderr +++ b/src/test/ui/empty/empty-struct-braces-expr.stderr @@ -51,20 +51,18 @@ error[E0599]: no variant named `Empty3` found for type `empty_struct::XE` in the | LL | let xe3 = XE::Empty3; //~ ERROR no variant named `Empty3` found for type | ----^^^^^^ - | | + | | | + | | help: did you mean: `XEmpty3` | variant not found in `empty_struct::XE` - | - = help: did you mean `XEmpty3`? error[E0599]: no variant named `Empty3` found for type `empty_struct::XE` in the current scope --> $DIR/empty-struct-braces-expr.rs:23:19 | LL | let xe3 = XE::Empty3(); //~ ERROR no variant named `Empty3` found for type | ----^^^^^^ - | | + | | | + | | help: did you mean: `XEmpty3` | variant not found in `empty_struct::XE` - | - = help: did you mean `XEmpty3`? error: aborting due to 8 previous errors diff --git a/src/test/ui/issues/issue-23217.stderr b/src/test/ui/issues/issue-23217.stderr index 208d0cc499a80..9cad002036fff 100644 --- a/src/test/ui/issues/issue-23217.stderr +++ b/src/test/ui/issues/issue-23217.stderr @@ -5,10 +5,9 @@ LL | pub enum SomeEnum { | ----------------- variant `A` not found here LL | B = SomeEnum::A, | ----------^ - | | + | | | + | | help: did you mean: `B` | variant not found in `SomeEnum` - | - = help: did you mean `B`? error: aborting due to previous error diff --git a/src/test/ui/issues/issue-28344.stderr b/src/test/ui/issues/issue-28344.stderr index 146ebad6ce175..b6f520c644b32 100644 --- a/src/test/ui/issues/issue-28344.stderr +++ b/src/test/ui/issues/issue-28344.stderr @@ -11,8 +11,7 @@ LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8); | --------^^^^^ | | | function or associated item not found in `dyn std::ops::BitXor<_>` - | - = help: did you mean `bitxor`? + | help: did you mean: `bitxor` error[E0191]: the value of the associated type `Output` (from the trait `std::ops::BitXor`) must be specified --> $DIR/issue-28344.rs:8:13 @@ -27,8 +26,7 @@ LL | let g = BitXor::bitor; | --------^^^^^ | | | function or associated item not found in `dyn std::ops::BitXor<_>` - | - = help: did you mean `bitxor`? + | help: did you mean: `bitxor` error: aborting due to 4 previous errors diff --git a/src/test/ui/issues/issue-28971.stderr b/src/test/ui/issues/issue-28971.stderr index d5dbd5f64885c..77d0b53ad216b 100644 --- a/src/test/ui/issues/issue-28971.stderr +++ b/src/test/ui/issues/issue-28971.stderr @@ -5,9 +5,10 @@ LL | enum Foo { | -------- variant `Baz` not found here ... LL | Foo::Baz(..) => (), - | -----^^^---- variant not found in `Foo` - | - = help: did you mean `Bar`? + | -----^^^---- + | | | + | | help: did you mean: `Bar` + | variant not found in `Foo` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-50264-inner-deref-trait/result-deref-err.stderr b/src/test/ui/issues/issue-50264-inner-deref-trait/result-deref-err.stderr index 99c4a5b03b320..96d6814b0fe93 100644 --- a/src/test/ui/issues/issue-50264-inner-deref-trait/result-deref-err.stderr +++ b/src/test/ui/issues/issue-50264-inner-deref-trait/result-deref-err.stderr @@ -2,11 +2,10 @@ error[E0599]: no method named `deref_err` found for type `std::result::Result<_, --> $DIR/result-deref-err.rs:4:28 | LL | let _result = &Err(41).deref_err(); - | ^^^^^^^^^ + | ^^^^^^^^^ help: did you mean: `deref_ok` | = note: the method `deref_err` exists but the following trait bounds were not satisfied: `{integer} : std::ops::Deref` - = help: did you mean `deref_ok`? error: aborting due to previous error diff --git a/src/test/ui/parser/issue-17718-const-mut.rs b/src/test/ui/parser/issue-17718-const-mut.rs index 4e74516d6b6fb..795a8c7631d9a 100644 --- a/src/test/ui/parser/issue-17718-const-mut.rs +++ b/src/test/ui/parser/issue-17718-const-mut.rs @@ -1,6 +1,6 @@ const mut //~ ERROR: const globals cannot be mutable -//~^ HELP did you mean to declare a static? +//~^^ HELP you might want to declare a static instead FOO: usize = 3; fn main() { diff --git a/src/test/ui/parser/issue-17718-const-mut.stderr b/src/test/ui/parser/issue-17718-const-mut.stderr index 29a65ebe41889..19f9fe19ef5ab 100644 --- a/src/test/ui/parser/issue-17718-const-mut.stderr +++ b/src/test/ui/parser/issue-17718-const-mut.stderr @@ -1,10 +1,10 @@ error: const globals cannot be mutable --> $DIR/issue-17718-const-mut.rs:2:1 | +LL | const + | ----- help: you might want to declare a static instead: `static` LL | mut //~ ERROR: const globals cannot be mutable - | ^^^ - | - = help: did you mean to declare a static? + | ^^^ cannot be mutable error: aborting due to previous error diff --git a/src/test/ui/suggestions/suggest-methods.stderr b/src/test/ui/suggestions/suggest-methods.stderr index 39d96a943a18a..b7727cf03a4e7 100644 --- a/src/test/ui/suggestions/suggest-methods.stderr +++ b/src/test/ui/suggestions/suggest-methods.stderr @@ -5,25 +5,19 @@ LL | struct Foo; | ----------- method `bat` not found for this ... LL | f.bat(1.0); //~ ERROR no method named - | ^^^ - | - = help: did you mean `bar`? + | ^^^ help: did you mean: `bar` error[E0599]: no method named `is_emtpy` found for type `std::string::String` in the current scope --> $DIR/suggest-methods.rs:21:15 | LL | let _ = s.is_emtpy(); //~ ERROR no method named - | ^^^^^^^^ - | - = help: did you mean `is_empty`? + | ^^^^^^^^ help: did you mean: `is_empty` error[E0599]: no method named `count_eos` found for type `u32` in the current scope --> $DIR/suggest-methods.rs:25:19 | LL | let _ = 63u32.count_eos(); //~ ERROR no method named - | ^^^^^^^^^ - | - = help: did you mean `count_zeros`? + | ^^^^^^^^^ help: did you mean: `count_zeros` error[E0599]: no method named `count_o` found for type `u32` in the current scope --> $DIR/suggest-methods.rs:28:19 From 6c399d155c6307563a2022fe98bc2e596af1cfc4 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 21 Jan 2019 19:42:06 +0100 Subject: [PATCH 11/23] Add error for trailing angle brackets. This commit adds a error (and accompanying machine applicable suggestion) for trailing angle brackets on function calls with a turbofish. --- src/libsyntax/ast.rs | 14 ++++ src/libsyntax/parse/parser.rs | 97 +++++++++++++++++++++++++++ src/test/ui/issues/issue-54521-1.rs | 16 +++++ src/test/ui/issues/issue-54521.fixed | 22 ++++++ src/test/ui/issues/issue-54521.rs | 22 ++++++ src/test/ui/issues/issue-54521.stderr | 26 +++++++ 6 files changed, 197 insertions(+) create mode 100644 src/test/ui/issues/issue-54521-1.rs create mode 100644 src/test/ui/issues/issue-54521.fixed create mode 100644 src/test/ui/issues/issue-54521.rs create mode 100644 src/test/ui/issues/issue-54521.stderr diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 405cf612543fb..e520ac3bdd499 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -140,6 +140,20 @@ pub enum GenericArgs { } impl GenericArgs { + pub fn is_parenthesized(&self) -> bool { + match *self { + Parenthesized(..) => true, + _ => false, + } + } + + pub fn is_angle_bracketed(&self) -> bool { + match *self { + AngleBracketed(..) => true, + _ => false, + } + } + pub fn span(&self) -> Span { match *self { AngleBracketed(ref data) => data.span, diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 439eec5b0c48d..d7c209d12a8fc 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2757,6 +2757,8 @@ impl<'a> Parser<'a> { // Assuming we have just parsed `.`, continue parsing into an expression. fn parse_dot_suffix(&mut self, self_arg: P, lo: Span) -> PResult<'a, P> { let segment = self.parse_path_segment(PathStyle::Expr, true)?; + self.check_trailing_angle_brackets(&segment); + Ok(match self.token { token::OpenDelim(token::Paren) => { // Method call `expr.f()` @@ -2784,6 +2786,101 @@ impl<'a> Parser<'a> { }) } + /// This function checks if there are trailing angle brackets and produces + /// a diagnostic to suggest removing them. + /// + /// ```ignore (diagnostic) + /// let _ = vec![1, 2, 3].into_iter().collect::>>>(); + /// ^^ help: remove extra angle brackets + /// ``` + fn check_trailing_angle_brackets(&mut self, segment: &PathSegment) { + // This function is intended to be invoked from `parse_dot_suffix` where there are two + // cases: + // + // - A field access (eg. `x.foo`) + // - A method call (eg. `x.foo()`) + // + // This function is called after parsing `.foo` and before parsing any parenthesis (if + // present). This includes any angle bracket arguments, such as `.foo::`. + + // We only care about trailing angle brackets if we previously parsed angle bracket + // arguments. This helps stop us incorrectly suggesting that extra angle brackets be + // removed in this case: + // + // `x.foo >> (3)` (where `x.foo` is a `u32` for example) + // + // This case is particularly tricky as we won't notice it just looking at the tokens - + // it will appear the same (in terms of upcoming tokens) as below (since the `::` will + // have already been parsed): + // + // `x.foo::>>(3)` + let parsed_angle_bracket_args = segment.args + .as_ref() + .map(|args| args.is_angle_bracketed()) + .unwrap_or(false); + + debug!( + "check_trailing_angle_brackets: parsed_angle_bracket_args={:?}", + parsed_angle_bracket_args, + ); + if !parsed_angle_bracket_args { + return; + } + + // Keep the span at the start so we can highlight the sequence of `>` characters to be + // removed. + let lo = self.span; + + // We need to look-ahead to see if we have `>` characters without moving the cursor forward + // (since we might have the field access case and the characters we're eating are + // actual operators and not trailing characters - ie `x.foo >> 3`). + let mut position = 0; + + // The first tokens we will encounter are shift right tokens (`>>`) since pairs of `>` + // characters will have been grouped together by the tokenizer. + let mut number_of_shr = 0; + while self.look_ahead(position, |t| *t == token::BinOp(token::BinOpToken::Shr)) { + number_of_shr += 1; + position += 1; + } + + // Afterwards, there will be at most one `>` character remaining (more than one and it'd + // have shown up as a `>>`). + let encountered_gt = self.look_ahead(position, |t| *t == token::Gt); + if encountered_gt { + position += 1; + } + + // If we didn't find any trailing `>>` characters or a trailing `>`, then we have + // nothing to error about. + debug!( + "check_trailing_angle_brackets: encountered_gt={:?} number_of_shr={:?}", + encountered_gt, number_of_shr, + ); + if !encountered_gt && number_of_shr < 1 { + return; + } + + // Finally, double check that we have a left parenthesis next as otherwise this is the + // field case. + if self.look_ahead(position, |t| *t == token::OpenDelim(token::Paren)) { + // Eat from where we started until the left parenthesis so that parsing can continue + // as if we didn't have those extra angle brackets. + self.eat_to_tokens(&[&token::OpenDelim(token::Paren)]); + let span = lo.until(self.span); + + self.diagnostic() + .struct_span_err(span, "unmatched angle bracket") + .span_suggestion_with_applicability( + span, + "remove extra angle bracket", + String::new(), + Applicability::MachineApplicable, + ) + .emit(); + } + } + fn parse_dot_or_call_expr_with_(&mut self, e0: P, lo: Span) -> PResult<'a, P> { let mut e = e0; let mut hi; diff --git a/src/test/ui/issues/issue-54521-1.rs b/src/test/ui/issues/issue-54521-1.rs new file mode 100644 index 0000000000000..d6a14a6e11f67 --- /dev/null +++ b/src/test/ui/issues/issue-54521-1.rs @@ -0,0 +1,16 @@ +// compile-pass + +// This test checks that the `remove extra angle brackets` error doesn't happen for some +// potential edge-cases.. + +struct X { + len: u32, +} + +fn main() { + let x = X { len: 3 }; + + let _ = x.len > (3); + + let _ = x.len >> (3); +} diff --git a/src/test/ui/issues/issue-54521.fixed b/src/test/ui/issues/issue-54521.fixed new file mode 100644 index 0000000000000..84ab6866cf133 --- /dev/null +++ b/src/test/ui/issues/issue-54521.fixed @@ -0,0 +1,22 @@ +// run-rustfix + +// This test checks that the following error is emitted and the suggestion works: +// +// ``` +// let _ = vec![1, 2, 3].into_iter().collect::>>>(); +// ^^ help: remove extra angle brackets +// ``` + +fn main() { + let _ = vec![1, 2, 3].into_iter().collect::>(); + //~^ ERROR unmatched angle bracket + + let _ = vec![1, 2, 3].into_iter().collect::>(); + //~^ ERROR unmatched angle bracket + + let _ = vec![1, 2, 3].into_iter().collect::>(); + //~^ ERROR unmatched angle bracket + + let _ = vec![1, 2, 3].into_iter().collect::>(); + //~^ ERROR unmatched angle bracket +} diff --git a/src/test/ui/issues/issue-54521.rs b/src/test/ui/issues/issue-54521.rs new file mode 100644 index 0000000000000..f1d6850417880 --- /dev/null +++ b/src/test/ui/issues/issue-54521.rs @@ -0,0 +1,22 @@ +// run-rustfix + +// This test checks that the following error is emitted and the suggestion works: +// +// ``` +// let _ = vec![1, 2, 3].into_iter().collect::>>>(); +// ^^ help: remove extra angle brackets +// ``` + +fn main() { + let _ = vec![1, 2, 3].into_iter().collect::>>>>>(); + //~^ ERROR unmatched angle bracket + + let _ = vec![1, 2, 3].into_iter().collect::>>>>(); + //~^ ERROR unmatched angle bracket + + let _ = vec![1, 2, 3].into_iter().collect::>>>(); + //~^ ERROR unmatched angle bracket + + let _ = vec![1, 2, 3].into_iter().collect::>>(); + //~^ ERROR unmatched angle bracket +} diff --git a/src/test/ui/issues/issue-54521.stderr b/src/test/ui/issues/issue-54521.stderr new file mode 100644 index 0000000000000..a67e9ca8daf40 --- /dev/null +++ b/src/test/ui/issues/issue-54521.stderr @@ -0,0 +1,26 @@ +error: unmatched angle bracket + --> $DIR/issue-54521.rs:11:60 + | +LL | let _ = vec![1, 2, 3].into_iter().collect::>>>>>(); + | ^^^^ help: remove extra angle bracket + +error: unmatched angle bracket + --> $DIR/issue-54521.rs:14:60 + | +LL | let _ = vec![1, 2, 3].into_iter().collect::>>>>(); + | ^^^ help: remove extra angle bracket + +error: unmatched angle bracket + --> $DIR/issue-54521.rs:17:60 + | +LL | let _ = vec![1, 2, 3].into_iter().collect::>>>(); + | ^^ help: remove extra angle bracket + +error: unmatched angle bracket + --> $DIR/issue-54521.rs:20:60 + | +LL | let _ = vec![1, 2, 3].into_iter().collect::>>(); + | ^ help: remove extra angle bracket + +error: aborting due to 4 previous errors + From 3f0fc9b03569e03dbdf5fdc3a67f246aad3b40b8 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 21 Jan 2019 21:16:46 +0100 Subject: [PATCH 12/23] Pluralize error messages. This commit pluralizes error messages when more than a single trailing `>` character is present. --- src/libsyntax/parse/parser.rs | 11 +++++++++-- src/test/ui/issues/issue-54521.stderr | 12 ++++++------ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index d7c209d12a8fc..6a881eb624196 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2869,11 +2869,18 @@ impl<'a> Parser<'a> { self.eat_to_tokens(&[&token::OpenDelim(token::Paren)]); let span = lo.until(self.span); + // We needn't check `encountered_gt` to determine if we should pluralize "bracket". + // `encountered_gt` can only represent a single `>` character, if `number_of_shr >= 1` + // then there is either `>>` or `>>>` - in either case a plural is warranted. + let plural = number_of_shr >= 1; self.diagnostic() - .struct_span_err(span, "unmatched angle bracket") + .struct_span_err( + span, + &format!("unmatched angle bracket{}", if plural { "s" } else { "" }), + ) .span_suggestion_with_applicability( span, - "remove extra angle bracket", + &format!("remove extra angle bracket{}", if plural { "s" } else { "" }), String::new(), Applicability::MachineApplicable, ) diff --git a/src/test/ui/issues/issue-54521.stderr b/src/test/ui/issues/issue-54521.stderr index a67e9ca8daf40..ffefbfd0348a8 100644 --- a/src/test/ui/issues/issue-54521.stderr +++ b/src/test/ui/issues/issue-54521.stderr @@ -1,20 +1,20 @@ -error: unmatched angle bracket +error: unmatched angle brackets --> $DIR/issue-54521.rs:11:60 | LL | let _ = vec![1, 2, 3].into_iter().collect::>>>>>(); - | ^^^^ help: remove extra angle bracket + | ^^^^ help: remove extra angle brackets -error: unmatched angle bracket +error: unmatched angle brackets --> $DIR/issue-54521.rs:14:60 | LL | let _ = vec![1, 2, 3].into_iter().collect::>>>>(); - | ^^^ help: remove extra angle bracket + | ^^^ help: remove extra angle brackets -error: unmatched angle bracket +error: unmatched angle brackets --> $DIR/issue-54521.rs:17:60 | LL | let _ = vec![1, 2, 3].into_iter().collect::>>>(); - | ^^ help: remove extra angle bracket + | ^^ help: remove extra angle brackets error: unmatched angle bracket --> $DIR/issue-54521.rs:20:60 From ab2479b00db475b7b8fe3b9e93d2e92dbe72bff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 21 Jan 2019 15:13:59 -0800 Subject: [PATCH 13/23] Move logic to its own method --- src/librustc_typeck/check/coercion.rs | 136 ++++++++++++-------------- 1 file changed, 63 insertions(+), 73 deletions(-) diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index e3aae21584c21..d1dfe9469fb77 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -1208,83 +1208,20 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E> db.span_label(cause.span, "return type is not `()`"); } ObligationCauseCode::BlockTailExpression(blk_id) => { - db = fcx.report_mismatched_types(cause, expected, found, err); - - let expr = expression.unwrap_or_else(|| { - span_bug!(cause.span, - "supposed to be part of a block tail expression, but the \ - expression is empty"); - }); - let pointing_at_return_type = fcx.suggest_mismatched_types_on_tail( - &mut db, - expr, + let parent_id = fcx.tcx.hir().get_parent_node(blk_id); + db = self.report_return_mismatched_types( + cause, expected, found, - cause.span, - blk_id, + err, + fcx, + parent_id, + expression.map(|expr| (expr, blk_id)), ); - // FIXME: replace with navigating up the chain until hitting an fn or - // bailing if no "pass-through" Node is found, in order to provide a - // suggestion when encountering something like: - // ``` - // fn foo(a: bool) -> impl Debug { - // if a { - // bar()?; - // } - // { - // let x = unsafe { bar() }; - // x - // } - // } - // ``` - // - // Verify that this is a tail expression of a function, otherwise the - // label pointing out the cause for the type coercion will be wrong - // as prior return coercions would not be relevant (#57664). - let parent_id = fcx.tcx.hir().get_parent_node(blk_id); - let parent = fcx.tcx.hir().get(fcx.tcx.hir().get_parent_node(parent_id)); - if let (Some((fn_decl, _, _)), false) = ( - fcx.get_node_fn_decl(parent), - pointing_at_return_type, - ) { - if let Some(sp) = fcx.ret_coercion_span.borrow().as_ref() { - db.span_label( - fn_decl.output.span(), - "expected because this return type...", - ); - db.span_label(*sp, format!( - "...is found to be `{}` here", - fcx.resolve_type_vars_with_obligations(expected), - )); - } - } } - ObligationCauseCode::ReturnType(_id) => { - db = fcx.report_mismatched_types(cause, expected, found, err); - let _id = fcx.tcx.hir().get_parent_node(_id); - let mut pointing_at_return_type = false; - let mut return_sp = None; - if let Some((fn_decl, can_suggest)) = fcx.get_fn_decl(_id) { - pointing_at_return_type = fcx.suggest_missing_return_type( - &mut db, &fn_decl, expected, found, can_suggest); - if !pointing_at_return_type { - return_sp = Some(fn_decl.output.span()); // `impl Trait` return type - } - } - if let (Some(sp), false) = ( - fcx.ret_coercion_span.borrow().as_ref(), - pointing_at_return_type, - ) { - if let Some(return_sp) = return_sp { - db.span_label(return_sp, "expected because this return type..."); - db.span_label( *sp, format!( - "...is found to be `{}` here", - fcx.resolve_type_vars_with_obligations(expected), - )); - } else if !sp.overlaps(cause.span) { - db.span_label(*sp, "expected because of this statement"); - } - } + ObligationCauseCode::ReturnType(id) => { + db = self.report_return_mismatched_types( + cause, expected, found, err, fcx, id, None); } _ => { db = fcx.report_mismatched_types(cause, expected, found, err); @@ -1302,6 +1239,59 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E> } } + fn report_return_mismatched_types<'a>( + &self, + cause: &ObligationCause<'tcx>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + err: TypeError<'tcx>, + fcx: &FnCtxt<'a, 'gcx, 'tcx>, + id: syntax::ast::NodeId, + expression: Option<(&'gcx hir::Expr, syntax::ast::NodeId)>, + ) -> DiagnosticBuilder<'a> { + let mut db = fcx.report_mismatched_types(cause, expected, found, err); + + let mut pointing_at_return_type = false; + let mut return_sp = None; + + // Verify that this is a tail expression of a function, otherwise the + // label pointing out the cause for the type coercion will be wrong + // as prior return coercions would not be relevant (#57664). + let parent_id = fcx.tcx.hir().get_parent_node(id); + let fn_decl = if let Some((expr, blk_id)) = expression { + pointing_at_return_type = fcx.suggest_mismatched_types_on_tail( + &mut db, + expr, + expected, + found, + cause.span, + blk_id, + ); + let parent = fcx.tcx.hir().get(parent_id); + fcx.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main)) + } else { + fcx.get_fn_decl(parent_id) + }; + + if let (Some((fn_decl, can_suggest)), _) = (fn_decl, pointing_at_return_type) { + if expression.is_none() { + pointing_at_return_type |= fcx.suggest_missing_return_type( + &mut db, &fn_decl, expected, found, can_suggest); + } + if !pointing_at_return_type { + return_sp = Some(fn_decl.output.span()); // `impl Trait` return type + } + } + if let (Some(sp), Some(return_sp)) = (fcx.ret_coercion_span.borrow().as_ref(), return_sp) { + db.span_label(return_sp, "expected because this return type..."); + db.span_label( *sp, format!( + "...is found to be `{}` here", + fcx.resolve_type_vars_with_obligations(expected), + )); + } + db + } + pub fn complete<'a>(self, fcx: &FnCtxt<'a, 'gcx, 'tcx>) -> Ty<'tcx> { if let Some(final_ty) = self.final_ty { final_ty From 914d142c02b558a597055c66a0e7e09115416211 Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 22 Jan 2019 00:35:31 +0100 Subject: [PATCH 14/23] Extend trailing `>` detection for paths. This commit extends the trailing `>` detection to also work for paths such as `Foo::>:Baz`. This involves making the existing check take the token that is expected to follow the path being checked as a parameter. Care is taken to ensure that this only happens on the construction of a whole path segment and not a partial path segment (during recursion). Through this enhancement, it was also observed that the ordering of right shift token and greater than tokens was overfitted to the examples being tested. In practice, given a sequence of `>` characters: `>>>>>>>>>` ..then they will be split into `>>` eagerly: `>> >> >> >> >`. ..but when a `<` is prepended, then the first `>>` is split: ` > >> >> >> >` ..and then when another `<` is prepended, a right shift is first again: `Vec<> >> >> >> >` In the previous commits, a example that had two `<<` characters was always used and therefore it was incorrectly assumed that `>>` would always be first - but when there is a single `<`, this is not the case. --- src/libsyntax/parse/parser.rs | 94 ++++++++++++++++--------- src/test/ui/issues/issue-54521-2.fixed | 22 ++++++ src/test/ui/issues/issue-54521-2.rs | 22 ++++++ src/test/ui/issues/issue-54521-2.stderr | 26 +++++++ 4 files changed, 131 insertions(+), 33 deletions(-) create mode 100644 src/test/ui/issues/issue-54521-2.fixed create mode 100644 src/test/ui/issues/issue-54521-2.rs create mode 100644 src/test/ui/issues/issue-54521-2.stderr diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 6a881eb624196..d380948b8913d 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2149,7 +2149,27 @@ impl<'a> Parser<'a> { enable_warning: bool) -> PResult<'a, ()> { loop { - segments.push(self.parse_path_segment(style, enable_warning)?); + let segment = self.parse_path_segment(style, enable_warning)?; + if style == PathStyle::Expr { + // In order to check for trailing angle brackets, we must have finished + // recursing (`parse_path_segment` can indirectly call this function), + // that is, the next token must be the highlighted part of the below example: + // + // `Foo::>::Qux` + // ^ here + // + // As opposed to the below highlight (if we had only finished the first + // recursion): + // + // `Foo::>::Qux` + // ^ here + // + // `PathStyle::Expr` is only provided at the root invocation and never in + // `parse_path_segment` to recurse and therefore can be checked to maintain + // this invariant. + self.check_trailing_angle_brackets(&segment, token::ModSep); + } + segments.push(segment); if self.is_import_coupler() || !self.eat(&token::ModSep) { return Ok(()); @@ -2757,7 +2777,7 @@ impl<'a> Parser<'a> { // Assuming we have just parsed `.`, continue parsing into an expression. fn parse_dot_suffix(&mut self, self_arg: P, lo: Span) -> PResult<'a, P> { let segment = self.parse_path_segment(PathStyle::Expr, true)?; - self.check_trailing_angle_brackets(&segment); + self.check_trailing_angle_brackets(&segment, token::OpenDelim(token::Paren)); Ok(match self.token { token::OpenDelim(token::Paren) => { @@ -2793,15 +2813,19 @@ impl<'a> Parser<'a> { /// let _ = vec![1, 2, 3].into_iter().collect::>>>(); /// ^^ help: remove extra angle brackets /// ``` - fn check_trailing_angle_brackets(&mut self, segment: &PathSegment) { - // This function is intended to be invoked from `parse_dot_suffix` where there are two + fn check_trailing_angle_brackets(&mut self, segment: &PathSegment, end: token::Token) { + // This function is intended to be invoked after parsing a path segment where there are two // cases: // - // - A field access (eg. `x.foo`) - // - A method call (eg. `x.foo()`) + // 1. A specific token is expected after the path segment. + // eg. `x.foo(`, `x.foo::(` (parenthesis - method call), + // `Foo::`, or `Foo::::` (mod sep - continued path). + // 2. No specific token is expected after the path segment. + // eg. `x.foo` (field access) // - // This function is called after parsing `.foo` and before parsing any parenthesis (if - // present). This includes any angle bracket arguments, such as `.foo::`. + // This function is called after parsing `.foo` and before parsing the token `end` (if + // present). This includes any angle bracket arguments, such as `.foo::` or + // `Foo::`. // We only care about trailing angle brackets if we previously parsed angle bracket // arguments. This helps stop us incorrectly suggesting that extra angle brackets be @@ -2836,43 +2860,47 @@ impl<'a> Parser<'a> { // actual operators and not trailing characters - ie `x.foo >> 3`). let mut position = 0; - // The first tokens we will encounter are shift right tokens (`>>`) since pairs of `>` - // characters will have been grouped together by the tokenizer. + // We can encounter `>` or `>>` tokens in any order, so we need to keep track of how + // many of each (so we can correctly pluralize our error messages) and continue to + // advance. let mut number_of_shr = 0; - while self.look_ahead(position, |t| *t == token::BinOp(token::BinOpToken::Shr)) { - number_of_shr += 1; - position += 1; - } - - // Afterwards, there will be at most one `>` character remaining (more than one and it'd - // have shown up as a `>>`). - let encountered_gt = self.look_ahead(position, |t| *t == token::Gt); - if encountered_gt { + let mut number_of_gt = 0; + while self.look_ahead(position, |t| { + trace!("check_trailing_angle_brackets: t={:?}", t); + if *t == token::BinOp(token::BinOpToken::Shr) { + number_of_shr += 1; + true + } else if *t == token::Gt { + number_of_gt += 1; + true + } else { + false + } + }) { position += 1; } - // If we didn't find any trailing `>>` characters or a trailing `>`, then we have - // nothing to error about. + // If we didn't find any trailing `>` characters, then we have nothing to error about. debug!( - "check_trailing_angle_brackets: encountered_gt={:?} number_of_shr={:?}", - encountered_gt, number_of_shr, + "check_trailing_angle_brackets: number_of_gt={:?} number_of_shr={:?}", + number_of_gt, number_of_shr, ); - if !encountered_gt && number_of_shr < 1 { + if number_of_gt < 1 && number_of_shr < 1 { return; } - // Finally, double check that we have a left parenthesis next as otherwise this is the - // field case. - if self.look_ahead(position, |t| *t == token::OpenDelim(token::Paren)) { - // Eat from where we started until the left parenthesis so that parsing can continue + // Finally, double check that we have our end token as otherwise this is the + // second case. + if self.look_ahead(position, |t| { + trace!("check_trailing_angle_brackets: t={:?}", t); + *t == end + }) { + // Eat from where we started until the end token so that parsing can continue // as if we didn't have those extra angle brackets. - self.eat_to_tokens(&[&token::OpenDelim(token::Paren)]); + self.eat_to_tokens(&[&end]); let span = lo.until(self.span); - // We needn't check `encountered_gt` to determine if we should pluralize "bracket". - // `encountered_gt` can only represent a single `>` character, if `number_of_shr >= 1` - // then there is either `>>` or `>>>` - in either case a plural is warranted. - let plural = number_of_shr >= 1; + let plural = number_of_gt > 1 || number_of_shr >= 1; self.diagnostic() .struct_span_err( span, diff --git a/src/test/ui/issues/issue-54521-2.fixed b/src/test/ui/issues/issue-54521-2.fixed new file mode 100644 index 0000000000000..a91c4fe43ea46 --- /dev/null +++ b/src/test/ui/issues/issue-54521-2.fixed @@ -0,0 +1,22 @@ +// run-rustfix + +// This test checks that the following error is emitted and the suggestion works: +// +// ``` +// let _ = Vec::>>::new(); +// ^^ help: remove extra angle brackets +// ``` + +fn main() { + let _ = Vec::::new(); + //~^ ERROR unmatched angle bracket + + let _ = Vec::::new(); + //~^ ERROR unmatched angle bracket + + let _ = Vec::::new(); + //~^ ERROR unmatched angle bracket + + let _ = Vec::::new(); + //~^ ERROR unmatched angle bracket +} diff --git a/src/test/ui/issues/issue-54521-2.rs b/src/test/ui/issues/issue-54521-2.rs new file mode 100644 index 0000000000000..3639aac87ee7f --- /dev/null +++ b/src/test/ui/issues/issue-54521-2.rs @@ -0,0 +1,22 @@ +// run-rustfix + +// This test checks that the following error is emitted and the suggestion works: +// +// ``` +// let _ = Vec::>>::new(); +// ^^ help: remove extra angle brackets +// ``` + +fn main() { + let _ = Vec::>>>>::new(); + //~^ ERROR unmatched angle bracket + + let _ = Vec::>>>::new(); + //~^ ERROR unmatched angle bracket + + let _ = Vec::>>::new(); + //~^ ERROR unmatched angle bracket + + let _ = Vec::>::new(); + //~^ ERROR unmatched angle bracket +} diff --git a/src/test/ui/issues/issue-54521-2.stderr b/src/test/ui/issues/issue-54521-2.stderr new file mode 100644 index 0000000000000..9556b83b730a4 --- /dev/null +++ b/src/test/ui/issues/issue-54521-2.stderr @@ -0,0 +1,26 @@ +error: unmatched angle brackets + --> $DIR/issue-54521-2.rs:11:25 + | +LL | let _ = Vec::>>>>::new(); + | ^^^^ help: remove extra angle brackets + +error: unmatched angle brackets + --> $DIR/issue-54521-2.rs:14:25 + | +LL | let _ = Vec::>>>::new(); + | ^^^ help: remove extra angle brackets + +error: unmatched angle brackets + --> $DIR/issue-54521-2.rs:17:25 + | +LL | let _ = Vec::>>::new(); + | ^^ help: remove extra angle brackets + +error: unmatched angle bracket + --> $DIR/issue-54521-2.rs:20:25 + | +LL | let _ = Vec::>::new(); + | ^ help: remove extra angle bracket + +error: aborting due to 4 previous errors + From 4745b86202f0e96b4c0d0de05220a5ac4b5308ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 21 Jan 2019 15:28:51 -0800 Subject: [PATCH 15/23] Accept more invalid code that is close to correct fields --- src/libsyntax/parse/parser.rs | 22 +++++++++++++++++-- src/test/ui/parser/removed-syntax-with-1.rs | 3 +-- .../ui/parser/removed-syntax-with-1.stderr | 11 ++-------- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index a2d3595b47206..745f2c7dc19e4 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2695,6 +2695,21 @@ impl<'a> Parser<'a> { break; } + let mut recovery_field = None; + if let token::Ident(ident, _) = self.token { + if !self.token.is_reserved_ident() && self.look_ahead(1, |t| *t == token::Colon) { + // Use in case of error after field-looking code: `S { foo: () with a }` + let mut ident = ident.clone(); + ident.span = self.span; + recovery_field = Some(ast::Field { + ident, + span: self.span, + expr: self.mk_expr(self.span, ExprKind::Err, ThinVec::new()), + is_shorthand: false, + attrs: ThinVec::new(), + }); + } + } let mut parsed_field = None; match self.parse_field() { Ok(f) => parsed_field = Some(f), @@ -2716,11 +2731,14 @@ impl<'a> Parser<'a> { match self.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Brace)]) { - Ok(()) => if let Some(f) = parsed_field { - // only include the field if there's no parse error + Ok(()) => if let Some(f) = parsed_field.or(recovery_field) { + // only include the field if there's no parse error for the field name fields.push(f); } Err(mut e) => { + if let Some(f) = recovery_field { + fields.push(f); + } e.span_label(struct_sp, "while parsing this struct"); e.emit(); self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore); diff --git a/src/test/ui/parser/removed-syntax-with-1.rs b/src/test/ui/parser/removed-syntax-with-1.rs index add024ea3907f..2c1e152dcee75 100644 --- a/src/test/ui/parser/removed-syntax-with-1.rs +++ b/src/test/ui/parser/removed-syntax-with-1.rs @@ -5,7 +5,6 @@ fn main() { } let a = S { foo: (), bar: () }; - let b = S { foo: () with a }; + let b = S { foo: () with a, bar: () }; //~^ ERROR expected one of `,`, `.`, `?`, `}`, or an operator, found `with` - //~| ERROR missing fields `bar`, `foo` in initializer of `main::S` } diff --git a/src/test/ui/parser/removed-syntax-with-1.stderr b/src/test/ui/parser/removed-syntax-with-1.stderr index aae29efa85e54..a157873916a64 100644 --- a/src/test/ui/parser/removed-syntax-with-1.stderr +++ b/src/test/ui/parser/removed-syntax-with-1.stderr @@ -1,17 +1,10 @@ error: expected one of `,`, `.`, `?`, `}`, or an operator, found `with` --> $DIR/removed-syntax-with-1.rs:8:25 | -LL | let b = S { foo: () with a }; +LL | let b = S { foo: () with a, bar: () }; | - ^^^^ expected one of `,`, `.`, `?`, `}`, or an operator here | | | while parsing this struct -error[E0063]: missing fields `bar`, `foo` in initializer of `main::S` - --> $DIR/removed-syntax-with-1.rs:8:13 - | -LL | let b = S { foo: () with a }; - | ^ missing `bar`, `foo` - -error: aborting due to 2 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0063`. From fb5d3c1f379e42b9d31887b744982159a0dc651b Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 22 Jan 2019 14:25:27 +0100 Subject: [PATCH 16/23] Stabilize Any::get_type_id and rename to type_id FCP: /~https://github.com/rust-lang/rust/issues/27745#issuecomment-373906749 --- src/libcore/any.rs | 16 ++++++---------- src/test/ui/traits/trait-privacy.rs | 3 +-- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/libcore/any.rs b/src/libcore/any.rs index 6a863ff369a87..2afd9e0c07237 100644 --- a/src/libcore/any.rs +++ b/src/libcore/any.rs @@ -81,12 +81,10 @@ pub trait Any: 'static { /// # Examples /// /// ``` - /// #![feature(get_type_id)] - /// /// use std::any::{Any, TypeId}; /// /// fn is_string(s: &dyn Any) -> bool { - /// TypeId::of::() == s.get_type_id() + /// TypeId::of::() == s.type_id() /// } /// /// fn main() { @@ -94,15 +92,13 @@ pub trait Any: 'static { /// assert_eq!(is_string(&"cookie monster".to_string()), true); /// } /// ``` - #[unstable(feature = "get_type_id", - reason = "this method will likely be replaced by an associated static", - issue = "27745")] - fn get_type_id(&self) -> TypeId; + #[stable(feature = "get_type_id", since = "1.34.0")] + fn type_id(&self) -> TypeId; } #[stable(feature = "rust1", since = "1.0.0")] impl Any for T { - fn get_type_id(&self) -> TypeId { TypeId::of::() } + fn type_id(&self) -> TypeId { TypeId::of::() } } /////////////////////////////////////////////////////////////////////////////// @@ -161,10 +157,10 @@ impl dyn Any { let t = TypeId::of::(); // Get TypeId of the type in the trait object - let boxed = self.get_type_id(); + let concrete = self.type_id(); // Compare both TypeIds on equality - t == boxed + t == concrete } /// Returns some reference to the boxed value if it is of type `T`, or diff --git a/src/test/ui/traits/trait-privacy.rs b/src/test/ui/traits/trait-privacy.rs index 523211fea9368..6254157e25da3 100644 --- a/src/test/ui/traits/trait-privacy.rs +++ b/src/test/ui/traits/trait-privacy.rs @@ -1,5 +1,4 @@ // compile-pass -#![feature(get_type_id)] #![allow(dead_code)] mod foo { pub use self::bar::T; @@ -18,7 +17,7 @@ fn g() { fn f() { let error = ::std::thread::spawn(|| {}).join().unwrap_err(); - error.get_type_id(); // Regression test for #21670 + error.type_id(); // Regression test for #21670 } From 26edb28d317fa54aec3358ec3179884d2b35ca98 Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Tue, 22 Jan 2019 15:08:31 +0100 Subject: [PATCH 17/23] Fix some cross crate existential type ICEs --- src/librustc_metadata/decoder.rs | 3 +++ src/librustc_resolve/build_reduced_graph.rs | 1 + src/librustc_typeck/check/wfcheck.rs | 4 ++-- .../auxiliary/cross_crate_ice.rs | 13 +++++++++++ .../auxiliary/cross_crate_ice2.rs | 22 +++++++++++++++++++ .../ui/existential_types/cross_crate_ice.rs | 16 ++++++++++++++ .../ui/existential_types/cross_crate_ice2.rs | 11 ++++++++++ 7 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 src/test/ui/existential_types/auxiliary/cross_crate_ice.rs create mode 100644 src/test/ui/existential_types/auxiliary/cross_crate_ice2.rs create mode 100644 src/test/ui/existential_types/cross_crate_ice.rs create mode 100644 src/test/ui/existential_types/cross_crate_ice2.rs diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 1f07e8f478b5a..ad6296e1a3bd8 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -893,6 +893,9 @@ impl<'a, 'tcx> CrateMetadata { EntryKind::AssociatedType(container) => { (ty::AssociatedKind::Type, container, false) } + EntryKind::AssociatedExistential(container) => { + (ty::AssociatedKind::Existential, container, false) + } _ => bug!("cannot get associated-item of `{:?}`", def_key) }; diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 35616cc03a936..3db73800d640a 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -680,6 +680,7 @@ impl<'a> Resolver<'a> { } module.populated.set(true); } + Def::Existential(..) | Def::TraitAlias(..) => { self.define(parent, ident, TypeNS, (def, vis, DUMMY_SP, expansion)); } diff --git a/src/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index 53e44d53e6a9b..6cae8d6fc5b94 100644 --- a/src/librustc_typeck/check/wfcheck.rs +++ b/src/librustc_typeck/check/wfcheck.rs @@ -608,8 +608,8 @@ fn check_existential_types<'a, 'fcx, 'gcx, 'tcx>( if let ty::Opaque(def_id, substs) = ty.sty { trace!("check_existential_types: opaque_ty, {:?}, {:?}", def_id, substs); let generics = tcx.generics_of(def_id); - // only check named existential types - if generics.parent.is_none() { + // only check named existential types defined in this crate + if generics.parent.is_none() && def_id.is_local() { let opaque_node_id = tcx.hir().as_local_node_id(def_id).unwrap(); if may_define_existential_type(tcx, fn_def_id, opaque_node_id) { trace!("check_existential_types may define. Generics: {:#?}", generics); diff --git a/src/test/ui/existential_types/auxiliary/cross_crate_ice.rs b/src/test/ui/existential_types/auxiliary/cross_crate_ice.rs new file mode 100644 index 0000000000000..c6fba2c33a81f --- /dev/null +++ b/src/test/ui/existential_types/auxiliary/cross_crate_ice.rs @@ -0,0 +1,13 @@ +// Crate that exports an existential type. Used for testing cross-crate. + +#![feature(const_fn)] +#![crate_type="rlib"] + +#![feature(existential_type)] + +pub existential type Foo: std::fmt::Debug; + +pub fn foo() -> Foo { + 5 +} + diff --git a/src/test/ui/existential_types/auxiliary/cross_crate_ice2.rs b/src/test/ui/existential_types/auxiliary/cross_crate_ice2.rs new file mode 100644 index 0000000000000..c23fa6b5dea01 --- /dev/null +++ b/src/test/ui/existential_types/auxiliary/cross_crate_ice2.rs @@ -0,0 +1,22 @@ +// Crate that exports an existential type. Used for testing cross-crate. + +#![feature(const_fn)] +#![crate_type="rlib"] + +#![feature(existential_type)] + +pub trait View { + type Tmp: Iterator; + + fn test(&self) -> Self::Tmp; +} + +pub struct X; + +impl View for X { + existential type Tmp: Iterator; + + fn test(&self) -> Self::Tmp { + vec![1,2,3].into_iter() + } +} diff --git a/src/test/ui/existential_types/cross_crate_ice.rs b/src/test/ui/existential_types/cross_crate_ice.rs new file mode 100644 index 0000000000000..c5d5ca916a48a --- /dev/null +++ b/src/test/ui/existential_types/cross_crate_ice.rs @@ -0,0 +1,16 @@ +// aux-build:cross_crate_ice.rs +// compile-pass + +extern crate cross_crate_ice; + +struct Bar(cross_crate_ice::Foo); + +impl Bar { + fn zero(&self) -> &cross_crate_ice::Foo { + &self.0 + } +} + +fn main() { + let _ = cross_crate_ice::foo(); +} diff --git a/src/test/ui/existential_types/cross_crate_ice2.rs b/src/test/ui/existential_types/cross_crate_ice2.rs new file mode 100644 index 0000000000000..a0f3933ce33b2 --- /dev/null +++ b/src/test/ui/existential_types/cross_crate_ice2.rs @@ -0,0 +1,11 @@ +// aux-build:cross_crate_ice2.rs +// compile-pass + +extern crate cross_crate_ice2; + +use cross_crate_ice2::View; + +fn main() { + let v = cross_crate_ice2::X; + v.test(); +} From 4781c6f8e75fec74ab519d819499aa2148380518 Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Tue, 22 Jan 2019 09:39:19 -0700 Subject: [PATCH 18/23] Remove unused links --- src/libstd/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 5a33f3e7a37df..93ce8a1495f46 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -155,7 +155,6 @@ //! [TCP]: net/struct.TcpStream.html //! [The Rust Prelude]: prelude/index.html //! [UDP]: net/struct.UdpSocket.html -//! [`::std::env::args`]: env/fn.args.html //! [`Arc`]: sync/struct.Arc.html //! [owned slice]: boxed/index.html //! [`Cell`]: cell/struct.Cell.html @@ -189,7 +188,6 @@ //! [`thread`]: thread/index.html //! [`use std::env`]: env/index.html //! [`use`]: ../book/ch07-02-modules-and-use-to-control-scope-and-privacy.html#the-use-keyword-to-bring-paths-into-a-scope -//! [crate root]: ../book/ch07-01-packages-and-crates-for-making-libraries-and-executables.html //! [crates.io]: https://crates.io //! [deref-coercions]: ../book/ch15-02-deref.html#implicit-deref-coercions-with-functions-and-methods //! [files]: fs/struct.File.html From 1c95f5a34c14f08d65cdd198827e3a2fcb63cf39 Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Tue, 22 Jan 2019 11:13:53 -0700 Subject: [PATCH 19/23] Fix issue 57762 Issue 57762 points out a compiler crash when the compiler was built using a stock LLVM 7. LLVM 7 was released without a necessary fix for a bug in the DWARF discriminant code. This patch changes rustc to use the fallback mode on (non-Rust) LLVM 7. Closes #57762 --- src/librustc_codegen_llvm/debuginfo/metadata.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index 6deedd0b5ea33..a354eef6887ae 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -1164,7 +1164,11 @@ fn use_enum_fallback(cx: &CodegenCx) -> bool { // On MSVC we have to use the fallback mode, because LLVM doesn't // lower variant parts to PDB. return cx.sess().target.target.options.is_like_msvc - || llvm_util::get_major_version() < 7; + || llvm_util::get_major_version() < 7 + // LLVM version 7 did not release with an important bug fix; + // but the required patch is in the equivalent Rust LLVM. + // See /~https://github.com/rust-lang/rust/issues/57762. + || (llvm_util::get_major_version() == 7 && unsafe { !llvm::LLVMRustIsRustLLVM() }); } // Describes the members of an enum value: An enum is described as a union of From 9452a8dfa3ba3575d5cf090a4e2305ee106d259e Mon Sep 17 00:00:00 2001 From: Tom Tromey Date: Tue, 22 Jan 2019 11:44:23 -0700 Subject: [PATCH 20/23] Simplify the version check Address the review comments by simplifying the version check to just "< 8". --- src/librustc_codegen_llvm/debuginfo/metadata.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index a354eef6887ae..9f63038c3623b 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -1164,11 +1164,10 @@ fn use_enum_fallback(cx: &CodegenCx) -> bool { // On MSVC we have to use the fallback mode, because LLVM doesn't // lower variant parts to PDB. return cx.sess().target.target.options.is_like_msvc - || llvm_util::get_major_version() < 7 // LLVM version 7 did not release with an important bug fix; - // but the required patch is in the equivalent Rust LLVM. - // See /~https://github.com/rust-lang/rust/issues/57762. - || (llvm_util::get_major_version() == 7 && unsafe { !llvm::LLVMRustIsRustLLVM() }); + // but the required patch is in the LLVM 8. Rust LLVM reports + // 8 as well. + || llvm_util::get_major_version() < 8; } // Describes the members of an enum value: An enum is described as a union of From 4e649ccc3a6ee5bd910ae41fd0bc6df4be234b4c Mon Sep 17 00:00:00 2001 From: Andy Russell Date: Tue, 22 Jan 2019 16:15:02 -0500 Subject: [PATCH 21/23] use port 80 for retrieving GPG key This works around firewalls blocking port 11371. See https://unix.stackexchange.com/questions/75892/keyserver-timed-out-when-trying-to-add-a-gpg-public-key. --- src/ci/docker/dist-various-1/install-x86_64-redox.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ci/docker/dist-various-1/install-x86_64-redox.sh b/src/ci/docker/dist-various-1/install-x86_64-redox.sh index 29222ff60f2a0..c39be14941c90 100755 --- a/src/ci/docker/dist-various-1/install-x86_64-redox.sh +++ b/src/ci/docker/dist-various-1/install-x86_64-redox.sh @@ -6,7 +6,7 @@ set -ex apt-get update apt-get install -y --no-install-recommends software-properties-common apt-transport-https -apt-key adv --batch --yes --keyserver keyserver.ubuntu.com --recv-keys AA12E97F0881517F +apt-key adv --batch --yes --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys AA12E97F0881517F add-apt-repository -y 'deb https://static.redox-os.org/toolchain/apt /' apt-get update From 5d6faf7b4af94d0da681e9873539f9f13279290d Mon Sep 17 00:00:00 2001 From: Oliver Scherer Date: Wed, 23 Jan 2019 11:34:58 +0100 Subject: [PATCH 22/23] Remove unused feature gates --- src/test/ui/existential_types/auxiliary/cross_crate_ice.rs | 1 - src/test/ui/existential_types/auxiliary/cross_crate_ice2.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/src/test/ui/existential_types/auxiliary/cross_crate_ice.rs b/src/test/ui/existential_types/auxiliary/cross_crate_ice.rs index c6fba2c33a81f..af2d209826e19 100644 --- a/src/test/ui/existential_types/auxiliary/cross_crate_ice.rs +++ b/src/test/ui/existential_types/auxiliary/cross_crate_ice.rs @@ -1,6 +1,5 @@ // Crate that exports an existential type. Used for testing cross-crate. -#![feature(const_fn)] #![crate_type="rlib"] #![feature(existential_type)] diff --git a/src/test/ui/existential_types/auxiliary/cross_crate_ice2.rs b/src/test/ui/existential_types/auxiliary/cross_crate_ice2.rs index c23fa6b5dea01..39ec5394febe4 100644 --- a/src/test/ui/existential_types/auxiliary/cross_crate_ice2.rs +++ b/src/test/ui/existential_types/auxiliary/cross_crate_ice2.rs @@ -1,6 +1,5 @@ // Crate that exports an existential type. Used for testing cross-crate. -#![feature(const_fn)] #![crate_type="rlib"] #![feature(existential_type)] From 645b7c2c36a3d71de69af51a29a0d4d832b95cbd Mon Sep 17 00:00:00 2001 From: Pietro Albini Date: Wed, 23 Jan 2019 11:54:23 +0100 Subject: [PATCH 23/23] ignore images line ending on older git versions On Ubuntu 16.04 git 2.7.4 tries to fix the line ending of .png and .ico files, and obviously it ruins them. This commit adds an attribute to those files to properly mark them as binary. --- .gitattributes | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitattributes b/.gitattributes index b223c8ac5fb84..f0b1c67bd0fdd 100644 --- a/.gitattributes +++ b/.gitattributes @@ -9,3 +9,7 @@ src/etc/installer/gfx/* binary *.woff binary src/vendor/** -text Cargo.lock -merge linguist-generated=false + +# Older git versions try to fix line endings on images, this prevents it. +*.png binary +*.ico binary