diff --git a/.gitattributes b/.gitattributes index b223c8ac5fb8..f0b1c67bd0fd 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 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 29222ff60f2a..c39be14941c9 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 diff --git a/src/libcore/any.rs b/src/libcore/any.rs index 6a863ff369a8..2afd9e0c0723 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/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs index 6deedd0b5ea3..9f63038c3623 100644 --- a/src/librustc_codegen_llvm/debuginfo/metadata.rs +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -1164,7 +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 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 diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 1f07e8f478b5..ad6296e1a3bd 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_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 3d0e46d99862..d1a3d7c1f81e 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::*; @@ -20,9 +21,73 @@ 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: impl FnOnce(&mut Self)) { + let old = mem::replace(&mut self.is_impl_trait_banned, true); + f(self); + self.is_impl_trait_banned = old; + } + + 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; + } + + // 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 +332,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 +353,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 +492,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 +590,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) } diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 35616cc03a93..3db73800d640 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/coercion.rs b/src/librustc_typeck/check/coercion.rs index dd63b4f20fa5..d1dfe9469fb7 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 => { @@ -1209,63 +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 fcx.get_node_fn_decl(parent).is_some() && !pointing_at_return_type { - if let Some(sp) = fcx.ret_coercion_span.borrow().as_ref() { - db.span_label(*sp, reason_label); - } - } } - 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; - 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 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); - } - } + 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); @@ -1283,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 diff --git a/src/librustc_typeck/check/method/suggest.rs b/src/librustc_typeck/check/method/suggest.rs index 23bcd88d6afb..f71a163cee26 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/librustc_typeck/check/wfcheck.rs b/src/librustc_typeck/check/wfcheck.rs index 53e44d53e6a9..6cae8d6fc5b9 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/libstd/lib.rs b/src/libstd/lib.rs index 83db3f347a76..82f3463dba0f 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 //! @@ -157,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 @@ -191,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 diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 798f14dcba95..af521848e905 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 09ea09952532..c7e33a165642 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)] @@ -1988,6 +1989,44 @@ impl<'a> Parser<'a> { result.unwrap() } + token::Dot if self.look_ahead(1, |t| match t { + 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), + 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() + .struct_span_err(sp, "float literals must have an integer part"); + err.span_suggestion_with_applicability( + sp, + "must have an integer part", + format!("0.{}{}", val, suffix), + Applicability::MachineApplicable, + ); + err.emit(); + 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!(); + }; + } _ => { return self.unexpected_last(&self.token); } }; @@ -2149,7 +2188,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(()); @@ -2656,8 +2715,24 @@ 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) => fields.push(f), + Ok(f) => parsed_field = Some(f), Err(mut e) => { e.span_label(struct_sp, "while parsing this struct"); e.emit(); @@ -2666,19 +2741,28 @@ impl<'a> Parser<'a> { // 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; + } } } } match self.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Brace)]) { - Ok(()) => {} + 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(); - break; + self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore); + self.eat(&token::Comma); } } } @@ -2757,6 +2841,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, token::OpenDelim(token::Paren)); + Ok(match self.token { token::OpenDelim(token::Paren) => { // Method call `expr.f()` @@ -2784,6 +2870,116 @@ 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, end: token::Token) { + // This function is intended to be invoked after parsing a path segment where there are two + // cases: + // + // 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 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 + // 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; + + // 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; + 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, then we have nothing to error about. + debug!( + "check_trailing_angle_brackets: number_of_gt={:?} number_of_shr={:?}", + number_of_gt, number_of_shr, + ); + if number_of_gt < 1 && number_of_shr < 1 { + return; + } + + // 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(&[&end]); + let span = lo.until(self.span); + + let plural = number_of_gt > 1 || number_of_shr >= 1; + self.diagnostic() + .struct_span_err( + span, + &format!("unmatched angle bracket{}", if plural { "s" } else { "" }), + ) + .span_suggestion_with_applicability( + span, + &format!("remove extra angle bracket{}", if plural { "s" } else { "" }), + 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; @@ -4542,13 +4738,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) => { @@ -4560,7 +4756,7 @@ impl<'a> Parser<'a> { } token::Eof => { debug!("recover_stmt_ return - Eof"); - return; + break; } token::Semi => { self.bump(); @@ -4568,7 +4764,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(); } } _ => { @@ -7275,9 +7481,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 ab57fec0e733..356e24d18a78 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 7f386630de59..a6346a5233f4 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 e595e0ccb929..19844503a480 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/existential_types/auxiliary/cross_crate_ice.rs b/src/test/ui/existential_types/auxiliary/cross_crate_ice.rs new file mode 100644 index 000000000000..af2d209826e1 --- /dev/null +++ b/src/test/ui/existential_types/auxiliary/cross_crate_ice.rs @@ -0,0 +1,12 @@ +// Crate that exports an existential type. Used for testing cross-crate. + +#![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 000000000000..39ec5394febe --- /dev/null +++ b/src/test/ui/existential_types/auxiliary/cross_crate_ice2.rs @@ -0,0 +1,21 @@ +// Crate that exports an existential type. Used for testing cross-crate. + +#![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 000000000000..c5d5ca916a48 --- /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 000000000000..a0f3933ce33b --- /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(); +} diff --git a/src/test/ui/impl-trait/equality.stderr b/src/test/ui/impl-trait/equality.stderr index 57bd70de7c68..6cd9d07748c2 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/issues/issue-23217.stderr b/src/test/ui/issues/issue-23217.stderr index 208d0cc499a8..9cad002036ff 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 146ebad6ce17..b6f520c644b3 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 d5dbd5f64885..77d0b53ad216 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 99c4a5b03b32..96d6814b0fe9 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/issues/issue-52496.rs b/src/test/ui/issues/issue-52496.rs new file mode 100644 index 000000000000..4e9453653735 --- /dev/null +++ b/src/test/ui/issues/issue-52496.rs @@ -0,0 +1,12 @@ +struct Foo { bar: f64, baz: i64, bat: i64 } + +fn main() { + let _ = Foo { bar: .5, baz: 42 }; + //~^ ERROR float literals must have an integer part + //~| ERROR missing field `bat` in initializer of `Foo` + let bar = 1.5f32; + let _ = Foo { bar.into(), bat: -1, . }; + //~^ ERROR expected one of + //~| 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 new file mode 100644 index 000000000000..43009a15bd49 --- /dev/null +++ b/src/test/ui/issues/issue-52496.stderr @@ -0,0 +1,37 @@ +error: float literals must have an integer part + --> $DIR/issue-52496.rs:4:24 + | +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: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[E0063]: missing fields `bar`, `baz` in initializer of `Foo` + --> $DIR/issue-52496.rs:8:13 + | +LL | let _ = Foo { bar.into(), bat: -1, . }; + | ^^^ missing `bar`, `baz` + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0063`. 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 000000000000..d6a14a6e11f6 --- /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-2.fixed b/src/test/ui/issues/issue-54521-2.fixed new file mode 100644 index 000000000000..a91c4fe43ea4 --- /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 000000000000..3639aac87ee7 --- /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 000000000000..9556b83b730a --- /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 + diff --git a/src/test/ui/issues/issue-54521.fixed b/src/test/ui/issues/issue-54521.fixed new file mode 100644 index 000000000000..84ab6866cf13 --- /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 000000000000..f1d685041788 --- /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 000000000000..ffefbfd0348a --- /dev/null +++ b/src/test/ui/issues/issue-54521.stderr @@ -0,0 +1,26 @@ +error: unmatched angle brackets + --> $DIR/issue-54521.rs:11:60 + | +LL | let _ = vec![1, 2, 3].into_iter().collect::>>>>>(); + | ^^^^ help: remove extra angle brackets + +error: unmatched angle brackets + --> $DIR/issue-54521.rs:14:60 + | +LL | let _ = vec![1, 2, 3].into_iter().collect::>>>>(); + | ^^^ help: remove extra angle brackets + +error: unmatched angle brackets + --> $DIR/issue-54521.rs:17:60 + | +LL | let _ = vec![1, 2, 3].into_iter().collect::>>>(); + | ^^ help: remove extra angle brackets + +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 + diff --git a/src/test/ui/parser/issue-17718-const-mut.rs b/src/test/ui/parser/issue-17718-const-mut.rs index 4e74516d6b6f..795a8c7631d9 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 29a65ebe4188..19f9fe19ef5a 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/parser/removed-syntax-with-1.rs b/src/test/ui/parser/removed-syntax-with-1.rs index 57cbe8d5be65..2c1e152dcee7 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 field `bar` 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 77ed4fcea517..a157873916a6 100644 --- a/src/test/ui/parser/removed-syntax-with-1.stderr +++ b/src/test/ui/parser/removed-syntax-with-1.stderr @@ -1,15 +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 }; - | ^^^^ expected one of `,`, `.`, `?`, `}`, or an operator here +LL | let b = S { foo: () with a, bar: () }; + | - ^^^^ 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 - | -LL | let b = S { foo: () with a }; - | ^ missing `bar` - -error: aborting due to 2 previous errors +error: aborting due to previous error -For more information about this error, try `rustc --explain E0063`. diff --git a/src/test/ui/parser/removed-syntax-with-2.rs b/src/test/ui/parser/removed-syntax-with-2.rs index 11db391c5489..f666da49696b 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 5642d2f45ffc..7717b49d3a2c 100644 --- a/src/test/ui/parser/removed-syntax-with-2.stderr +++ b/src/test/ui/parser/removed-syntax-with-2.stderr @@ -2,23 +2,16 @@ 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 +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`. diff --git a/src/test/ui/parser/struct-field-numeric-shorthand.rs b/src/test/ui/parser/struct-field-numeric-shorthand.rs index 914588f51e1e..58c40b3d96a4 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 f5dc226934ec..cfb1f8201475 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`. 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 62da0787b02a..5ebe00e624fc 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 | | } 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 000000000000..506ef8900b88 --- /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 000000000000..c464676b444c --- /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`. diff --git a/src/test/ui/suggestions/suggest-methods.stderr b/src/test/ui/suggestions/suggest-methods.stderr index 39d96a943a18..b7727cf03a4e 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 diff --git a/src/test/ui/traits/trait-privacy.rs b/src/test/ui/traits/trait-privacy.rs index 523211fea936..6254157e25da 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 }