diff --git a/compiler/rustc_error_messages/locales/en-US/parse.ftl b/compiler/rustc_error_messages/locales/en-US/parse.ftl index b53550e5fd556..3401978caf5f0 100644 --- a/compiler/rustc_error_messages/locales/en-US/parse.ftl +++ b/compiler/rustc_error_messages/locales/en-US/parse.ftl @@ -365,3 +365,6 @@ parse_invalid_identifier_with_leading_number = expected identifier, found number parse_maybe_fn_typo_with_impl = you might have meant to write `impl` instead of `fn` .suggestion = replace `fn` with `impl` here + +parse_expected_fn_path_found_fn_keyword = expected identifier, found keyword `fn` + .suggestion = use `Fn` to refer to the trait diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index ef50efb81253f..406e1569a6f75 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -626,7 +626,7 @@ impl<'a> ExtCtxt<'a> { // Builds `#[name = val]`. // - // Note: `span` is used for both the identifer and the value. + // Note: `span` is used for both the identifier and the value. pub fn attr_name_value_str(&self, name: Symbol, val: Symbol, span: Span) -> ast::Attribute { let g = &self.sess.parse_sess.attr_id_generator; attr::mk_attr_name_value_str(g, ast::AttrStyle::Outer, name, val, span) diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index b7d599f57fd44..9e46968c40819 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -24,6 +24,8 @@ use rustc_hir as hir; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{GenericParamKind, Node}; +use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::hir::nested_filter; use rustc_middle::ty::query::Providers; use rustc_middle::ty::util::{Discr, IntTypeExt}; @@ -31,7 +33,9 @@ use rustc_middle::ty::{self, AdtKind, Const, IsSuggestable, ToPredicate, Ty, TyC use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; use rustc_target::spec::abi; +use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName; +use rustc_trait_selection::traits::ObligationCtxt; use std::iter; mod generics_of; @@ -1224,7 +1228,17 @@ fn infer_return_ty_for_fn_sig<'tcx>( // to prevent the user from getting a papercut while trying to use the unique closure // syntax (e.g. `[closure@src/lib.rs:2:5: 2:9]`). diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound"); - diag.note("for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html"); + diag.note( + "for more information on `Fn` traits and closure types, see \ + https://doc.rust-lang.org/book/ch13-01-closures.html", + ); + } else if let Some(i_ty) = suggest_impl_iterator(tcx, ret_ty, ty.span, hir_id, def_id) { + diag.span_suggestion( + ty.span, + "replace with an appropriate return type", + format!("impl Iterator", i_ty), + Applicability::MachineApplicable, + ); } diag.emit(); @@ -1242,6 +1256,51 @@ fn infer_return_ty_for_fn_sig<'tcx>( } } +fn suggest_impl_iterator<'tcx>( + tcx: TyCtxt<'tcx>, + ret_ty: Ty<'tcx>, + span: Span, + hir_id: hir::HirId, + def_id: LocalDefId, +) -> Option> { + let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) else { return None; }; + let Some(iterator_item) = tcx.get_diagnostic_item(sym::IteratorItem) else { return None; }; + if !tcx + .infer_ctxt() + .build() + .type_implements_trait(iter_trait, [ret_ty], tcx.param_env(def_id)) + .must_apply_modulo_regions() + { + return None; + } + let infcx = tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new_in_snapshot(&infcx); + // Find the type of `Iterator::Item`. + let origin = TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }; + let ty_var = infcx.next_ty_var(origin); + let projection = ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::Projection( + ty::ProjectionPredicate { + projection_ty: tcx.mk_alias_ty(iterator_item, tcx.mk_substs([ret_ty.into()].iter())), + term: ty_var.into(), + }, + ))); + // Add `::Item = _` obligation. + ocx.register_obligation(crate::traits::Obligation::misc( + tcx, + span, + hir_id, + tcx.param_env(def_id), + projection, + )); + if ocx.select_where_possible().is_empty() + && let item_ty = infcx.resolve_vars_if_possible(ty_var) + && item_ty.is_suggestable(tcx, false) + { + return Some(item_ty); + } + None +} + fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option> { let icx = ItemCtxt::new(tcx, def_id); let item = tcx.hir().expect_item(def_id.expect_local()); diff --git a/compiler/rustc_hir_analysis/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs index b678990f94e91..4bd55a5483147 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs @@ -5,6 +5,7 @@ use rustc_hir::intravisit; use rustc_hir::intravisit::Visitor; use rustc_hir::{HirId, Node}; use rustc_middle::hir::nested_filter; +use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::util::IntTypeExt; use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable}; @@ -907,10 +908,10 @@ fn infer_placeholder_type<'a>( Applicability::MachineApplicable, ); } else { - err.span_note( + with_forced_trimmed_paths!(err.span_note( tcx.hir().body(body_id).value.span, - &format!("however, the inferred type `{}` cannot be named", ty), - ); + &format!("however, the inferred type `{ty}` cannot be named"), + )); } } @@ -931,10 +932,10 @@ fn infer_placeholder_type<'a>( Applicability::MaybeIncorrect, ); } else { - diag.span_note( + with_forced_trimmed_paths!(diag.span_note( tcx.hir().body(body_id).value.span, - &format!("however, the inferred type `{}` cannot be named", ty), - ); + &format!("however, the inferred type `{ty}` cannot be named"), + )); } } diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index af14ee08a9981..829913d278d06 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -241,7 +241,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }); if let Some(ok) = self.lookup_method_in_trait( - call_expr.span, + self.misc(call_expr.span), method_name, trait_def_id, adjusted_ty, diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index fddb8a458a7d5..b9b27e8627aff 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -11,7 +11,7 @@ pub use self::suggest::SelfSource; pub use self::MethodError::*; use crate::errors::OpMethodGenericParams; -use crate::{Expectation, FnCtxt}; +use crate::FnCtxt; use rustc_data_structures::sync::Lrc; use rustc_errors::{Applicability, Diagnostic}; use rustc_hir as hir; @@ -264,7 +264,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pub(super) fn obligation_for_method( &self, - span: Span, + cause: ObligationCause<'tcx>, trait_def_id: DefId, self_ty: Ty<'tcx>, opt_input_types: Option<&[Ty<'tcx>]>, @@ -282,71 +282,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - self.var_for_def(span, param) - }); - - let trait_ref = self.tcx.mk_trait_ref(trait_def_id, substs); - - // Construct an obligation - let poly_trait_ref = ty::Binder::dummy(trait_ref); - ( - traits::Obligation::misc( - self.tcx, - span, - self.body_id, - self.param_env, - poly_trait_ref.without_const(), - ), - substs, - ) - } - - pub(super) fn obligation_for_op_method( - &self, - span: Span, - trait_def_id: DefId, - self_ty: Ty<'tcx>, - opt_input_type: Option>, - opt_input_expr: Option<&'tcx hir::Expr<'tcx>>, - expected: Expectation<'tcx>, - ) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List>) - { - // Construct a trait-reference `self_ty : Trait` - let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| { - match param.kind { - GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {} - GenericParamDefKind::Type { .. } => { - if param.index == 0 { - return self_ty.into(); - } else if let Some(input_type) = opt_input_type { - return input_type.into(); - } - } - } - self.var_for_def(span, param) + self.var_for_def(cause.span, param) }); let trait_ref = self.tcx.mk_trait_ref(trait_def_id, substs); // Construct an obligation let poly_trait_ref = ty::Binder::dummy(trait_ref); - let output_ty = expected.only_has_type(self).and_then(|ty| (!ty.needs_infer()).then(|| ty)); - ( traits::Obligation::new( self.tcx, - traits::ObligationCause::new( - span, - self.body_id, - traits::BinOp { - rhs_span: opt_input_expr.map(|expr| expr.span), - is_lit: opt_input_expr - .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))), - output_ty, - }, - ), + cause, self.param_env, - poly_trait_ref, + poly_trait_ref.without_const(), ), substs, ) @@ -357,55 +305,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// In particular, it doesn't really do any probing: it simply constructs /// an obligation for a particular trait with the given self type and checks /// whether that trait is implemented. - #[instrument(level = "debug", skip(self, span))] + #[instrument(level = "debug", skip(self))] pub(super) fn lookup_method_in_trait( &self, - span: Span, + cause: ObligationCause<'tcx>, m_name: Ident, trait_def_id: DefId, self_ty: Ty<'tcx>, opt_input_types: Option<&[Ty<'tcx>]>, ) -> Option>> { let (obligation, substs) = - self.obligation_for_method(span, trait_def_id, self_ty, opt_input_types); - self.construct_obligation_for_trait( - span, - m_name, - trait_def_id, - obligation, - substs, - None, - false, - ) - } - - pub(super) fn lookup_op_method_in_trait( - &self, - span: Span, - m_name: Ident, - trait_def_id: DefId, - self_ty: Ty<'tcx>, - opt_input_type: Option>, - opt_input_expr: Option<&'tcx hir::Expr<'tcx>>, - expected: Expectation<'tcx>, - ) -> Option>> { - let (obligation, substs) = self.obligation_for_op_method( - span, - trait_def_id, - self_ty, - opt_input_type, - opt_input_expr, - expected, - ); - self.construct_obligation_for_trait( - span, - m_name, - trait_def_id, - obligation, - substs, - opt_input_expr, - true, - ) + self.obligation_for_method(cause, trait_def_id, self_ty, opt_input_types); + self.construct_obligation_for_trait(m_name, trait_def_id, obligation, substs) } // FIXME(#18741): it seems likely that we can consolidate some of this @@ -413,13 +324,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // of this method is basically the same as confirmation. fn construct_obligation_for_trait( &self, - span: Span, m_name: Ident, trait_def_id: DefId, obligation: traits::PredicateObligation<'tcx>, substs: &'tcx ty::List>, - opt_input_expr: Option<&'tcx hir::Expr<'tcx>>, - is_op: bool, ) -> Option>> { debug!(?obligation); @@ -435,7 +343,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tcx = self.tcx; let Some(method_item) = self.associated_value(trait_def_id, m_name) else { tcx.sess.delay_span_bug( - span, + obligation.cause.span, "operator trait does not have corresponding operator method", ); return None; @@ -461,24 +369,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // with bound regions. let fn_sig = tcx.bound_fn_sig(def_id); let fn_sig = fn_sig.subst(self.tcx, substs); - let fn_sig = self.replace_bound_vars_with_fresh_vars(span, infer::FnCall, fn_sig); - - let cause = if is_op { - ObligationCause::new( - span, - self.body_id, - traits::BinOp { - rhs_span: opt_input_expr.map(|expr| expr.span), - is_lit: opt_input_expr - .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))), - output_ty: None, - }, - ) - } else { - traits::ObligationCause::misc(span, self.body_id) - }; + let fn_sig = + self.replace_bound_vars_with_fresh_vars(obligation.cause.span, infer::FnCall, fn_sig); - let InferOk { value, obligations: o } = self.at(&cause, self.param_env).normalize(fn_sig); + let InferOk { value, obligations: o } = + self.at(&obligation.cause, self.param_env).normalize(fn_sig); let fn_sig = { obligations.extend(o); value @@ -494,7 +389,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // any late-bound regions appearing in its bounds. let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs); - let InferOk { value, obligations: o } = self.at(&cause, self.param_env).normalize(bounds); + let InferOk { value, obligations: o } = + self.at(&obligation.cause, self.param_env).normalize(bounds); let bounds = { obligations.extend(o); value @@ -502,7 +398,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { assert!(!bounds.has_escaping_bound_vars()); - let predicates_cause = cause.clone(); + let predicates_cause = obligation.cause.clone(); obligations.extend(traits::predicates_for_generics( move |_, _| predicates_cause.clone(), self.param_env, @@ -517,7 +413,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); obligations.push(traits::Obligation::new( tcx, - cause, + obligation.cause, self.param_env, ty::Binder::dummy(ty::PredicateKind::WellFormed(method_ty.into())), )); diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index 9f0d175c4c669..34140f3e1fe3e 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -12,14 +12,16 @@ use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, }; use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable}; +use rustc_middle::ty::{ + self, DefIdTree, IsSuggestable, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable, +}; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::Spanned; use rustc_span::symbol::{sym, Ident}; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::error_reporting::suggestions::TypeErrCtxtExt as _; -use rustc_trait_selection::traits::FulfillmentError; +use rustc_trait_selection::traits::{self, FulfillmentError}; use rustc_type_ir::sty::TyKind::*; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -48,8 +50,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self .lookup_op_method( lhs_deref_ty, - Some(rhs_ty), - Some(rhs), + Some((rhs, rhs_ty)), Op::Binary(op, IsAssign::Yes), expected, ) @@ -60,8 +61,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self .lookup_op_method( lhs_ty, - Some(rhs_ty), - Some(rhs), + Some((rhs, rhs_ty)), Op::Binary(op, IsAssign::Yes), expected, ) @@ -248,8 +248,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let result = self.lookup_op_method( lhs_ty, - Some(rhs_ty_var), - Some(rhs_expr), + Some((rhs_expr, rhs_ty_var)), Op::Binary(op, is_assign), expected, ); @@ -382,8 +381,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if self .lookup_op_method( lhs_deref_ty, - Some(rhs_ty), - Some(rhs_expr), + Some((rhs_expr, rhs_ty)), Op::Binary(op, is_assign), expected, ) @@ -410,8 +408,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let is_compatible = |lhs_ty, rhs_ty| { self.lookup_op_method( lhs_ty, - Some(rhs_ty), - Some(rhs_expr), + Some((rhs_expr, rhs_ty)), Op::Binary(op, is_assign), expected, ) @@ -471,8 +468,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let errors = self .lookup_op_method( lhs_ty, - Some(rhs_ty), - Some(rhs_expr), + Some((rhs_expr, rhs_ty)), Op::Binary(op, is_assign), expected, ) @@ -492,6 +488,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(output_def_id) = output_def_id && let Some(trait_def_id) = trait_def_id && self.tcx.parent(output_def_id) == trait_def_id + && output_ty.is_suggestable(self.tcx, false) { Some(("Output", *output_ty)) } else { @@ -625,7 +622,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Expectation<'tcx>, ) -> Ty<'tcx> { assert!(op.is_by_value()); - match self.lookup_op_method(operand_ty, None, None, Op::Unary(op, ex.span), expected) { + match self.lookup_op_method(operand_ty, None, Op::Unary(op, ex.span), expected) { Ok(method) => { self.write_method_call(ex.hir_id, method); method.sig.output() @@ -712,8 +709,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn lookup_op_method( &self, lhs_ty: Ty<'tcx>, - other_ty: Option>, - other_ty_expr: Option<&'tcx hir::Expr<'tcx>>, + opt_rhs: Option<(&'tcx hir::Expr<'tcx>, Ty<'tcx>)>, op: Op, expected: Expectation<'tcx>, ) -> Result, Vec>> { @@ -742,20 +738,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Op::Unary(..) => 0, }, ) { + self.tcx + .sess + .delay_span_bug(span, "operator didn't have the right number of generic args"); return Err(vec![]); } let opname = Ident::with_dummy_span(opname); + let input_types = + opt_rhs.as_ref().map(|(_, ty)| std::slice::from_ref(ty)).unwrap_or_default(); + let cause = self.cause( + span, + traits::BinOp { + rhs_span: opt_rhs.map(|(expr, _)| expr.span), + is_lit: opt_rhs + .map_or(false, |(expr, _)| matches!(expr.kind, hir::ExprKind::Lit(_))), + output_ty: expected.only_has_type(self), + }, + ); + let method = trait_did.and_then(|trait_did| { - self.lookup_op_method_in_trait( - span, - opname, - trait_did, - lhs_ty, - other_ty, - other_ty_expr, - expected, - ) + self.lookup_method_in_trait(cause.clone(), opname, trait_did, lhs_ty, Some(input_types)) }); match (method, trait_did) { @@ -766,14 +769,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } (None, None) => Err(vec![]), (None, Some(trait_did)) => { - let (obligation, _) = self.obligation_for_op_method( - span, - trait_did, - lhs_ty, - other_ty, - other_ty_expr, - expected, - ); + let (obligation, _) = + self.obligation_for_method(cause, trait_did, lhs_ty, Some(input_types)); Err(rustc_trait_selection::traits::fully_solve_obligation(self, obligation)) } } diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index 952ea14887f7b..a0f048fc09b9b 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -225,7 +225,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { imm_tr.and_then(|trait_did| { self.lookup_method_in_trait( - span, + self.misc(span), Ident::with_dummy_span(imm_op), trait_did, base_ty, @@ -264,7 +264,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { mut_tr.and_then(|trait_did| { self.lookup_method_in_trait( - span, + self.misc(span), Ident::with_dummy_span(mut_op), trait_did, base_ty, diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index 50d6d5b9bab91..4c65fca29b897 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -851,7 +851,7 @@ impl Cursor<'_> { } // Eats the identifier. Note: succeeds on `_`, which isn't a valid - // identifer. + // identifier. fn eat_identifier(&mut self) { if !is_id_start(self.first()) { return; diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 18a0bee9c2e5e..574591529f331 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1229,3 +1229,11 @@ pub(crate) struct FnTypoWithImpl { #[suggestion(applicability = "maybe-incorrect", code = "impl", style = "verbose")] pub fn_span: Span, } + +#[derive(Diagnostic)] +#[diag(parse_expected_fn_path_found_fn_keyword)] +pub(crate) struct ExpectedFnPathFoundFnKeyword { + #[primary_span] + #[suggestion(applicability = "machine-applicable", code = "Fn", style = "verbose")] + pub fn_token_span: Span, +} diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index fc26278909c2d..8661e9ca16b8d 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -1,8 +1,9 @@ use super::{Parser, PathStyle, TokenType}; -use crate::errors::{FnPtrWithGenerics, FnPtrWithGenericsSugg}; +use crate::errors::{ExpectedFnPathFoundFnKeyword, FnPtrWithGenerics, FnPtrWithGenericsSugg}; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; +use ast::DUMMY_NODE_ID; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::util::case::Case; @@ -12,7 +13,9 @@ use rustc_ast::{ }; use rustc_errors::{pluralize, struct_span_err, Applicability, PResult}; use rustc_span::source_map::Span; -use rustc_span::symbol::{kw, sym}; +use rustc_span::symbol::{kw, sym, Ident}; +use rustc_span::Symbol; +use thin_vec::thin_vec; /// Any `?` or `~const` modifiers that appear at the start of a bound. struct BoundModifiers { @@ -931,7 +934,14 @@ impl<'a> Parser<'a> { modifiers: BoundModifiers, ) -> PResult<'a, GenericBound> { let lifetime_defs = self.parse_late_bound_lifetime_defs()?; - let path = self.parse_path(PathStyle::Type)?; + let path = if self.token.is_keyword(kw::Fn) + && self.look_ahead(1, |tok| tok.kind == TokenKind::OpenDelim(Delimiter::Parenthesis)) + && let Some(path) = self.recover_path_from_fn() + { + path + } else { + self.parse_path(PathStyle::Type)? + }; if has_parens { if self.token.is_like_plus() { // Someone has written something like `&dyn (Trait + Other)`. The correct code @@ -960,6 +970,38 @@ impl<'a> Parser<'a> { Ok(GenericBound::Trait(poly_trait, modifier)) } + // recovers a `Fn(..)` parenthesized-style path from `fn(..)` + fn recover_path_from_fn(&mut self) -> Option { + let fn_token_span = self.token.span; + self.bump(); + let args_lo = self.token.span; + let snapshot = self.create_snapshot_for_diagnostic(); + match self.parse_fn_decl(|_| false, AllowPlus::No, RecoverReturnSign::OnlyFatArrow) { + Ok(decl) => { + self.sess.emit_err(ExpectedFnPathFoundFnKeyword { fn_token_span }); + Some(ast::Path { + span: fn_token_span.to(self.prev_token.span), + segments: thin_vec![ast::PathSegment { + ident: Ident::new(Symbol::intern("Fn"), fn_token_span), + id: DUMMY_NODE_ID, + args: Some(P(ast::GenericArgs::Parenthesized(ast::ParenthesizedArgs { + span: args_lo.to(self.prev_token.span), + inputs: decl.inputs.iter().map(|a| a.ty.clone()).collect(), + inputs_span: args_lo.until(decl.output.span()), + output: decl.output.clone(), + }))), + }], + tokens: None, + }) + } + Err(diag) => { + diag.cancel(); + self.restore_snapshot(snapshot); + None + } + } + } + /// Optionally parses `for<$generic_params>`. pub(super) fn parse_late_bound_lifetime_defs(&mut self) -> PResult<'a, Vec> { if self.eat_keyword(kw::For) { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 90f654c68ecbf..85510fa2c660c 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -213,6 +213,7 @@ symbols! { Is, ItemContext, Iterator, + IteratorItem, Layout, Left, LinkedList, diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index b154688fb087b..b5fe8d72f7d24 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -954,7 +954,7 @@ impl Box { /// [`Layout`]: crate::Layout #[stable(feature = "box_raw", since = "1.4.0")] #[inline] - #[must_use = "call `drop(from_raw(ptr))` if you intend to drop the `Box`"] + #[must_use = "call `drop(Box::from_raw(ptr))` if you intend to drop the `Box`"] pub unsafe fn from_raw(raw: *mut T) -> Self { unsafe { Self::from_raw_in(raw, Global) } } diff --git a/library/alloc/src/collections/binary_heap/tests.rs b/library/alloc/src/collections/binary_heap/tests.rs index 5a05215aeeddf..fe08e0e10e818 100644 --- a/library/alloc/src/collections/binary_heap/tests.rs +++ b/library/alloc/src/collections/binary_heap/tests.rs @@ -1,8 +1,8 @@ use super::*; use crate::boxed::Box; +use crate::testing::crash_test::{CrashTestDummy, Panic}; use std::iter::TrustedLen; use std::panic::{catch_unwind, AssertUnwindSafe}; -use std::sync::atomic::{AtomicU32, Ordering}; #[test] fn test_iterator() { @@ -291,33 +291,83 @@ fn test_drain_sorted() { #[test] fn test_drain_sorted_leak() { - static DROPS: AtomicU32 = AtomicU32::new(0); - - #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] - struct D(u32, bool); - - impl Drop for D { - fn drop(&mut self) { - DROPS.fetch_add(1, Ordering::SeqCst); - - if self.1 { - panic!("panic in `drop`"); - } - } - } - + let d0 = CrashTestDummy::new(0); + let d1 = CrashTestDummy::new(1); + let d2 = CrashTestDummy::new(2); + let d3 = CrashTestDummy::new(3); + let d4 = CrashTestDummy::new(4); + let d5 = CrashTestDummy::new(5); let mut q = BinaryHeap::from(vec![ - D(0, false), - D(1, false), - D(2, false), - D(3, true), - D(4, false), - D(5, false), + d0.spawn(Panic::Never), + d1.spawn(Panic::Never), + d2.spawn(Panic::Never), + d3.spawn(Panic::InDrop), + d4.spawn(Panic::Never), + d5.spawn(Panic::Never), ]); - catch_unwind(AssertUnwindSafe(|| drop(q.drain_sorted()))).ok(); + catch_unwind(AssertUnwindSafe(|| drop(q.drain_sorted()))).unwrap_err(); + + assert_eq!(d0.dropped(), 1); + assert_eq!(d1.dropped(), 1); + assert_eq!(d2.dropped(), 1); + assert_eq!(d3.dropped(), 1); + assert_eq!(d4.dropped(), 1); + assert_eq!(d5.dropped(), 1); + assert!(q.is_empty()); +} - assert_eq!(DROPS.load(Ordering::SeqCst), 6); +#[test] +fn test_drain_forget() { + let a = CrashTestDummy::new(0); + let b = CrashTestDummy::new(1); + let c = CrashTestDummy::new(2); + let mut q = + BinaryHeap::from(vec![a.spawn(Panic::Never), b.spawn(Panic::Never), c.spawn(Panic::Never)]); + + catch_unwind(AssertUnwindSafe(|| { + let mut it = q.drain(); + it.next(); + mem::forget(it); + })) + .unwrap(); + // Behaviour after leaking is explicitly unspecified and order is arbitrary, + // so it's fine if these start failing, but probably worth knowing. + assert!(q.is_empty()); + assert_eq!(a.dropped() + b.dropped() + c.dropped(), 1); + assert_eq!(a.dropped(), 0); + assert_eq!(b.dropped(), 0); + assert_eq!(c.dropped(), 1); + drop(q); + assert_eq!(a.dropped(), 0); + assert_eq!(b.dropped(), 0); + assert_eq!(c.dropped(), 1); +} + +#[test] +fn test_drain_sorted_forget() { + let a = CrashTestDummy::new(0); + let b = CrashTestDummy::new(1); + let c = CrashTestDummy::new(2); + let mut q = + BinaryHeap::from(vec![a.spawn(Panic::Never), b.spawn(Panic::Never), c.spawn(Panic::Never)]); + + catch_unwind(AssertUnwindSafe(|| { + let mut it = q.drain_sorted(); + it.next(); + mem::forget(it); + })) + .unwrap(); + // Behaviour after leaking is explicitly unspecified, + // so it's fine if these start failing, but probably worth knowing. + assert_eq!(q.len(), 2); + assert_eq!(a.dropped(), 0); + assert_eq!(b.dropped(), 0); + assert_eq!(c.dropped(), 1); + drop(q); + assert_eq!(a.dropped(), 1); + assert_eq!(b.dropped(), 1); + assert_eq!(c.dropped(), 1); } #[test] diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs index 4c372b1d60ac4..700b1463bfd51 100644 --- a/library/alloc/src/collections/btree/map/tests.rs +++ b/library/alloc/src/collections/btree/map/tests.rs @@ -1,12 +1,12 @@ -use super::super::testing::crash_test::{CrashTestDummy, Panic}; -use super::super::testing::ord_chaos::{Cyclic3, Governed, Governor}; -use super::super::testing::rng::DeterministicRng; use super::Entry::{Occupied, Vacant}; use super::*; use crate::boxed::Box; use crate::fmt::Debug; use crate::rc::Rc; use crate::string::{String, ToString}; +use crate::testing::crash_test::{CrashTestDummy, Panic}; +use crate::testing::ord_chaos::{Cyclic3, Governed, Governor}; +use crate::testing::rng::DeterministicRng; use crate::vec::Vec; use std::cmp::Ordering; use std::convert::TryFrom; diff --git a/library/alloc/src/collections/btree/mod.rs b/library/alloc/src/collections/btree/mod.rs index 9d43ac5c5be59..7552f2fc04ce8 100644 --- a/library/alloc/src/collections/btree/mod.rs +++ b/library/alloc/src/collections/btree/mod.rs @@ -21,6 +21,3 @@ trait Recover { fn take(&mut self, key: &Q) -> Option; fn replace(&mut self, key: Self::Key) -> Option; } - -#[cfg(test)] -mod testing; diff --git a/library/alloc/src/collections/btree/set/tests.rs b/library/alloc/src/collections/btree/set/tests.rs index 502d3e1d12667..7b8d41a603176 100644 --- a/library/alloc/src/collections/btree/set/tests.rs +++ b/library/alloc/src/collections/btree/set/tests.rs @@ -1,6 +1,6 @@ -use super::super::testing::crash_test::{CrashTestDummy, Panic}; -use super::super::testing::rng::DeterministicRng; use super::*; +use crate::testing::crash_test::{CrashTestDummy, Panic}; +use crate::testing::rng::DeterministicRng; use crate::vec::Vec; use std::cmp::Ordering; use std::hash::{Hash, Hasher}; diff --git a/library/alloc/src/collections/linked_list/tests.rs b/library/alloc/src/collections/linked_list/tests.rs index f8fbfa1bfbc87..5d5af22bb292f 100644 --- a/library/alloc/src/collections/linked_list/tests.rs +++ b/library/alloc/src/collections/linked_list/tests.rs @@ -1,4 +1,5 @@ use super::*; +use crate::testing::crash_test::{CrashTestDummy, Panic}; use crate::vec::Vec; use std::panic::{catch_unwind, AssertUnwindSafe}; @@ -984,35 +985,34 @@ fn drain_filter_complex() { #[test] fn drain_filter_drop_panic_leak() { - static mut DROPS: i32 = 0; - - struct D(bool); - - impl Drop for D { - fn drop(&mut self) { - unsafe { - DROPS += 1; - } - - if self.0 { - panic!("panic in `drop`"); - } - } - } - + let d0 = CrashTestDummy::new(0); + let d1 = CrashTestDummy::new(1); + let d2 = CrashTestDummy::new(2); + let d3 = CrashTestDummy::new(3); + let d4 = CrashTestDummy::new(4); + let d5 = CrashTestDummy::new(5); + let d6 = CrashTestDummy::new(6); + let d7 = CrashTestDummy::new(7); let mut q = LinkedList::new(); - q.push_back(D(false)); - q.push_back(D(false)); - q.push_back(D(false)); - q.push_back(D(false)); - q.push_back(D(false)); - q.push_front(D(false)); - q.push_front(D(true)); - q.push_front(D(false)); - - catch_unwind(AssertUnwindSafe(|| drop(q.drain_filter(|_| true)))).ok(); - - assert_eq!(unsafe { DROPS }, 8); + q.push_back(d3.spawn(Panic::Never)); + q.push_back(d4.spawn(Panic::Never)); + q.push_back(d5.spawn(Panic::Never)); + q.push_back(d6.spawn(Panic::Never)); + q.push_back(d7.spawn(Panic::Never)); + q.push_front(d2.spawn(Panic::Never)); + q.push_front(d1.spawn(Panic::InDrop)); + q.push_front(d0.spawn(Panic::Never)); + + catch_unwind(AssertUnwindSafe(|| drop(q.drain_filter(|_| true)))).unwrap_err(); + + assert_eq!(d0.dropped(), 1); + assert_eq!(d1.dropped(), 1); + assert_eq!(d2.dropped(), 1); + assert_eq!(d3.dropped(), 1); + assert_eq!(d4.dropped(), 1); + assert_eq!(d5.dropped(), 1); + assert_eq!(d6.dropped(), 1); + assert_eq!(d7.dropped(), 1); assert!(q.is_empty()); } diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 96960d43f5820..b7d5c5c2437af 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -206,6 +206,8 @@ extern crate std; #[cfg(test)] extern crate test; +#[cfg(test)] +mod testing; // Module with internal macros used by other modules (needs to be included before other modules). #[macro_use] diff --git a/library/alloc/src/collections/btree/testing/crash_test.rs b/library/alloc/src/testing/crash_test.rs similarity index 100% rename from library/alloc/src/collections/btree/testing/crash_test.rs rename to library/alloc/src/testing/crash_test.rs diff --git a/library/alloc/src/collections/btree/testing/mod.rs b/library/alloc/src/testing/mod.rs similarity index 100% rename from library/alloc/src/collections/btree/testing/mod.rs rename to library/alloc/src/testing/mod.rs diff --git a/library/alloc/src/collections/btree/testing/ord_chaos.rs b/library/alloc/src/testing/ord_chaos.rs similarity index 100% rename from library/alloc/src/collections/btree/testing/ord_chaos.rs rename to library/alloc/src/testing/ord_chaos.rs diff --git a/library/alloc/src/collections/btree/testing/rng.rs b/library/alloc/src/testing/rng.rs similarity index 100% rename from library/alloc/src/collections/btree/testing/rng.rs rename to library/alloc/src/testing/rng.rs diff --git a/library/core/src/any.rs b/library/core/src/any.rs index 1a379ecc11c01..9ca4947ed8f8b 100644 --- a/library/core/src/any.rs +++ b/library/core/src/any.rs @@ -148,7 +148,7 @@ //! ``` //! //! In this example, if the concrete type of `obj` in `use_my_trait` is `SomeConcreteType`, then -//! the `get_context_ref` call will return a reference to `obj.some_string` with type `&String`. +//! the `get_context_by_ref` call will return a reference to `obj.some_string` with type `&String`. #![stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index ba93165cf53a2..77f1ffaacff66 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -807,7 +807,8 @@ impl RefCell { /// /// # Panics /// - /// Panics if the value in either `RefCell` is currently borrowed. + /// Panics if the value in either `RefCell` is currently borrowed, or + /// if `self` and `other` point to the same `RefCell`. /// /// # Examples /// @@ -1193,7 +1194,7 @@ impl Default for RefCell { impl PartialEq for RefCell { /// # Panics /// - /// Panics if the value in either `RefCell` is currently borrowed. + /// Panics if the value in either `RefCell` is currently mutably borrowed. #[inline] fn eq(&self, other: &RefCell) -> bool { *self.borrow() == *other.borrow() @@ -1207,7 +1208,7 @@ impl Eq for RefCell {} impl PartialOrd for RefCell { /// # Panics /// - /// Panics if the value in either `RefCell` is currently borrowed. + /// Panics if the value in either `RefCell` is currently mutably borrowed. #[inline] fn partial_cmp(&self, other: &RefCell) -> Option { self.borrow().partial_cmp(&*other.borrow()) @@ -1215,7 +1216,7 @@ impl PartialOrd for RefCell { /// # Panics /// - /// Panics if the value in either `RefCell` is currently borrowed. + /// Panics if the value in either `RefCell` is currently mutably borrowed. #[inline] fn lt(&self, other: &RefCell) -> bool { *self.borrow() < *other.borrow() @@ -1223,7 +1224,7 @@ impl PartialOrd for RefCell { /// # Panics /// - /// Panics if the value in either `RefCell` is currently borrowed. + /// Panics if the value in either `RefCell` is currently mutably borrowed. #[inline] fn le(&self, other: &RefCell) -> bool { *self.borrow() <= *other.borrow() @@ -1231,7 +1232,7 @@ impl PartialOrd for RefCell { /// # Panics /// - /// Panics if the value in either `RefCell` is currently borrowed. + /// Panics if the value in either `RefCell` is currently mutably borrowed. #[inline] fn gt(&self, other: &RefCell) -> bool { *self.borrow() > *other.borrow() @@ -1239,7 +1240,7 @@ impl PartialOrd for RefCell { /// # Panics /// - /// Panics if the value in either `RefCell` is currently borrowed. + /// Panics if the value in either `RefCell` is currently mutably borrowed. #[inline] fn ge(&self, other: &RefCell) -> bool { *self.borrow() >= *other.borrow() @@ -1250,7 +1251,7 @@ impl PartialOrd for RefCell { impl Ord for RefCell { /// # Panics /// - /// Panics if the value in either `RefCell` is currently borrowed. + /// Panics if the value in either `RefCell` is currently mutably borrowed. #[inline] fn cmp(&self, other: &RefCell) -> Ordering { self.borrow().cmp(&*other.borrow()) diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index 08abb0b0d924b..b4863bb2589fd 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -66,6 +66,7 @@ fn _assert_is_object_safe(_: &dyn Iterator) {} #[must_use = "iterators are lazy and do nothing unless consumed"] pub trait Iterator { /// The type of the elements being iterated over. + #[rustc_diagnostic_item = "IteratorItem"] #[stable(feature = "rust1", since = "1.0.0")] type Item; diff --git a/library/std/src/sync/mpmc/utils.rs b/library/std/src/sync/mpmc/utils.rs index d0904b4b94cbc..e030c55ce8f61 100644 --- a/library/std/src/sync/mpmc/utils.rs +++ b/library/std/src/sync/mpmc/utils.rs @@ -136,7 +136,7 @@ impl Backoff { } } - /// Returns `true` if exponential backoff has completed and blocking the thread is advised. + /// Returns `true` if quadratic backoff has completed and blocking the thread is advised. #[inline] pub fn is_completed(&self) -> bool { self.step.get() > YIELD_LIMIT diff --git a/src/test/ui/lint/unused/must-use-box-from-raw.stderr b/src/test/ui/lint/unused/must-use-box-from-raw.stderr index 72118275774d1..47ab613bec21c 100644 --- a/src/test/ui/lint/unused/must-use-box-from-raw.stderr +++ b/src/test/ui/lint/unused/must-use-box-from-raw.stderr @@ -4,7 +4,7 @@ warning: unused return value of `Box::::from_raw` that must be used LL | Box::from_raw(ptr); | ^^^^^^^^^^^^^^^^^^ | - = note: call `drop(from_raw(ptr))` if you intend to drop the `Box` + = note: call `drop(Box::from_raw(ptr))` if you intend to drop the `Box` note: the lint level is defined here --> $DIR/must-use-box-from-raw.rs:5:9 | diff --git a/src/test/ui/parser/kw-in-trait-bounds.rs b/src/test/ui/parser/kw-in-trait-bounds.rs index fa037e5937dca..e9e85339affa9 100644 --- a/src/test/ui/parser/kw-in-trait-bounds.rs +++ b/src/test/ui/parser/kw-in-trait-bounds.rs @@ -4,21 +4,13 @@ fn _f(_: impl fn(), _: &dyn fn()) //~^ ERROR expected identifier, found keyword `fn` //~| ERROR expected identifier, found keyword `fn` //~| ERROR expected identifier, found keyword `fn` -//~| ERROR cannot find trait `r#fn` in this scope -//~| ERROR cannot find trait `r#fn` in this scope -//~| ERROR cannot find trait `r#fn` in this scope -//~| HELP a trait with a similar name exists -//~| HELP a trait with a similar name exists -//~| HELP a trait with a similar name exists -//~| HELP escape `fn` to use it as an identifier -//~| HELP escape `fn` to use it as an identifier -//~| HELP escape `fn` to use it as an identifier +//~| HELP use `Fn` to refer to the trait +//~| HELP use `Fn` to refer to the trait +//~| HELP use `Fn` to refer to the trait where G: fn(), //~^ ERROR expected identifier, found keyword `fn` - //~| ERROR cannot find trait `r#fn` in this scope - //~| HELP a trait with a similar name exists - //~| HELP escape `fn` to use it as an identifier + //~| HELP use `Fn` to refer to the trait {} fn _g(_: impl struct, _: &dyn struct) diff --git a/src/test/ui/parser/kw-in-trait-bounds.stderr b/src/test/ui/parser/kw-in-trait-bounds.stderr index 79643660e8b00..2d3aad4d6baf4 100644 --- a/src/test/ui/parser/kw-in-trait-bounds.stderr +++ b/src/test/ui/parser/kw-in-trait-bounds.stderr @@ -2,48 +2,48 @@ error: expected identifier, found keyword `fn` --> $DIR/kw-in-trait-bounds.rs:3:10 | LL | fn _f(_: impl fn(), _: &dyn fn()) - | ^^ expected identifier, found keyword + | ^^ | -help: escape `fn` to use it as an identifier +help: use `Fn` to refer to the trait | -LL | fn _f(_: impl fn(), _: &dyn fn()) - | ++ +LL | fn _f(_: impl fn(), _: &dyn fn()) + | ~~ error: expected identifier, found keyword `fn` --> $DIR/kw-in-trait-bounds.rs:3:27 | LL | fn _f(_: impl fn(), _: &dyn fn()) - | ^^ expected identifier, found keyword + | ^^ | -help: escape `fn` to use it as an identifier +help: use `Fn` to refer to the trait | -LL | fn _f(_: impl r#fn(), _: &dyn fn()) - | ++ +LL | fn _f(_: impl Fn(), _: &dyn fn()) + | ~~ error: expected identifier, found keyword `fn` --> $DIR/kw-in-trait-bounds.rs:3:41 | LL | fn _f(_: impl fn(), _: &dyn fn()) - | ^^ expected identifier, found keyword + | ^^ | -help: escape `fn` to use it as an identifier +help: use `Fn` to refer to the trait | -LL | fn _f(_: impl fn(), _: &dyn r#fn()) - | ++ +LL | fn _f(_: impl fn(), _: &dyn Fn()) + | ~~ error: expected identifier, found keyword `fn` - --> $DIR/kw-in-trait-bounds.rs:17:4 + --> $DIR/kw-in-trait-bounds.rs:11:4 | LL | G: fn(), - | ^^ expected identifier, found keyword + | ^^ | -help: escape `fn` to use it as an identifier +help: use `Fn` to refer to the trait | -LL | G: r#fn(), - | ++ +LL | G: Fn(), + | ~~ error: expected identifier, found keyword `struct` - --> $DIR/kw-in-trait-bounds.rs:24:10 + --> $DIR/kw-in-trait-bounds.rs:16:10 | LL | fn _g(_: impl struct, _: &dyn struct) | ^^^^^^ expected identifier, found keyword @@ -54,7 +54,7 @@ LL | fn _g(_: impl struct, _: &dyn struct) | ++ error: expected identifier, found keyword `struct` - --> $DIR/kw-in-trait-bounds.rs:24:29 + --> $DIR/kw-in-trait-bounds.rs:16:29 | LL | fn _g(_: impl struct, _: &dyn struct) | ^^^^^^ expected identifier, found keyword @@ -65,7 +65,7 @@ LL | fn _g(_: impl r#struct, _: &dyn struct) | ++ error: expected identifier, found keyword `struct` - --> $DIR/kw-in-trait-bounds.rs:24:45 + --> $DIR/kw-in-trait-bounds.rs:16:45 | LL | fn _g(_: impl struct, _: &dyn struct) | ^^^^^^ expected identifier, found keyword @@ -76,7 +76,7 @@ LL | fn _g(_: impl struct, _: &dyn r#struct) | ++ error: expected identifier, found keyword `struct` - --> $DIR/kw-in-trait-bounds.rs:38:8 + --> $DIR/kw-in-trait-bounds.rs:30:8 | LL | B: struct, | ^^^^^^ expected identifier, found keyword @@ -86,44 +86,8 @@ help: escape `struct` to use it as an identifier LL | B: r#struct, | ++ -error[E0405]: cannot find trait `r#fn` in this scope - --> $DIR/kw-in-trait-bounds.rs:3:10 - | -LL | fn _f(_: impl fn(), _: &dyn fn()) - | ^^ help: a trait with a similar name exists (notice the capitalization): `Fn` - --> $SRC_DIR/core/src/ops/function.rs:LL:COL - | - = note: similarly named trait `Fn` defined here - -error[E0405]: cannot find trait `r#fn` in this scope - --> $DIR/kw-in-trait-bounds.rs:17:4 - | -LL | G: fn(), - | ^^ help: a trait with a similar name exists (notice the capitalization): `Fn` - --> $SRC_DIR/core/src/ops/function.rs:LL:COL - | - = note: similarly named trait `Fn` defined here - -error[E0405]: cannot find trait `r#fn` in this scope - --> $DIR/kw-in-trait-bounds.rs:3:27 - | -LL | fn _f(_: impl fn(), _: &dyn fn()) - | ^^ help: a trait with a similar name exists (notice the capitalization): `Fn` - --> $SRC_DIR/core/src/ops/function.rs:LL:COL - | - = note: similarly named trait `Fn` defined here - -error[E0405]: cannot find trait `r#fn` in this scope - --> $DIR/kw-in-trait-bounds.rs:3:41 - | -LL | fn _f(_: impl fn(), _: &dyn fn()) - | ^^ help: a trait with a similar name exists (notice the capitalization): `Fn` - --> $SRC_DIR/core/src/ops/function.rs:LL:COL - | - = note: similarly named trait `Fn` defined here - error[E0405]: cannot find trait `r#struct` in this scope - --> $DIR/kw-in-trait-bounds.rs:24:10 + --> $DIR/kw-in-trait-bounds.rs:16:10 | LL | fn _g(_: impl struct, _: &dyn struct) | ^^^^^^ help: a trait with a similar name exists (notice the capitalization): `Struct` @@ -132,7 +96,7 @@ LL | trait Struct {} | ------------ similarly named trait `Struct` defined here error[E0405]: cannot find trait `r#struct` in this scope - --> $DIR/kw-in-trait-bounds.rs:38:8 + --> $DIR/kw-in-trait-bounds.rs:30:8 | LL | B: struct, | ^^^^^^ help: a trait with a similar name exists (notice the capitalization): `Struct` @@ -141,7 +105,7 @@ LL | trait Struct {} | ------------ similarly named trait `Struct` defined here error[E0405]: cannot find trait `r#struct` in this scope - --> $DIR/kw-in-trait-bounds.rs:24:29 + --> $DIR/kw-in-trait-bounds.rs:16:29 | LL | fn _g(_: impl struct, _: &dyn struct) | ^^^^^^ help: a trait with a similar name exists (notice the capitalization): `Struct` @@ -150,7 +114,7 @@ LL | trait Struct {} | ------------ similarly named trait `Struct` defined here error[E0405]: cannot find trait `r#struct` in this scope - --> $DIR/kw-in-trait-bounds.rs:24:45 + --> $DIR/kw-in-trait-bounds.rs:16:45 | LL | fn _g(_: impl struct, _: &dyn struct) | ^^^^^^ help: a trait with a similar name exists (notice the capitalization): `Struct` @@ -158,6 +122,6 @@ LL | fn _g(_: impl struct, _: &dyn struct) LL | trait Struct {} | ------------ similarly named trait `Struct` defined here -error: aborting due to 16 previous errors +error: aborting due to 12 previous errors For more information about this error, try `rustc --explain E0405`. diff --git a/src/test/ui/parser/recover-fn-trait-from-fn-kw.rs b/src/test/ui/parser/recover-fn-trait-from-fn-kw.rs new file mode 100644 index 0000000000000..b6611e6273d37 --- /dev/null +++ b/src/test/ui/parser/recover-fn-trait-from-fn-kw.rs @@ -0,0 +1,12 @@ +fn foo(_: impl fn() -> i32) {} +//~^ ERROR expected identifier, found keyword `fn` + +fn foo2(_: T) {} +//~^ ERROR expected identifier, found keyword `fn` + +fn main() { + foo(|| ()); + //~^ mismatched types + foo2(|_: ()| {}); + //~^ type mismatch in closure arguments +} diff --git a/src/test/ui/parser/recover-fn-trait-from-fn-kw.stderr b/src/test/ui/parser/recover-fn-trait-from-fn-kw.stderr new file mode 100644 index 0000000000000..3681a796c53ed --- /dev/null +++ b/src/test/ui/parser/recover-fn-trait-from-fn-kw.stderr @@ -0,0 +1,48 @@ +error: expected identifier, found keyword `fn` + --> $DIR/recover-fn-trait-from-fn-kw.rs:1:16 + | +LL | fn foo(_: impl fn() -> i32) {} + | ^^ + | +help: use `Fn` to refer to the trait + | +LL | fn foo(_: impl Fn() -> i32) {} + | ~~ + +error: expected identifier, found keyword `fn` + --> $DIR/recover-fn-trait-from-fn-kw.rs:4:12 + | +LL | fn foo2(_: T) {} + | ^^ + | +help: use `Fn` to refer to the trait + | +LL | fn foo2(_: T) {} + | ~~ + +error[E0308]: mismatched types + --> $DIR/recover-fn-trait-from-fn-kw.rs:8:12 + | +LL | foo(|| ()); + | ^^ expected `i32`, found `()` + +error[E0631]: type mismatch in closure arguments + --> $DIR/recover-fn-trait-from-fn-kw.rs:10:5 + | +LL | foo2(|_: ()| {}); + | ^^^^ ------- found signature defined here + | | + | expected due to this + | + = note: expected closure signature `fn(i32) -> _` + found closure signature `fn(()) -> _` +note: required by a bound in `foo2` + --> $DIR/recover-fn-trait-from-fn-kw.rs:4:12 + | +LL | fn foo2(_: T) {} + | ^^^^^^^ required by this bound in `foo2` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0308, E0631. +For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/suggestions/unnamable-types.stderr b/src/test/ui/suggestions/unnamable-types.stderr index ede3ebfa739c4..24bedb5297b1f 100644 --- a/src/test/ui/suggestions/unnamable-types.stderr +++ b/src/test/ui/suggestions/unnamable-types.stderr @@ -19,7 +19,7 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures LL | const C: _ = || 42; | ^ not allowed in type signatures | -note: however, the inferred type `[closure@$DIR/unnamable-types.rs:17:14: 17:16]` cannot be named +note: however, the inferred type `[closure@unnamable-types.rs:17:14]` cannot be named --> $DIR/unnamable-types.rs:17:14 | LL | const C: _ = || 42; @@ -31,7 +31,7 @@ error: missing type for `const` item LL | const D = S { t: { let i = 0; move || -> i32 { i } } }; | ^ | -note: however, the inferred type `S<[closure@$DIR/unnamable-types.rs:23:31: 23:45]>` cannot be named +note: however, the inferred type `S<[closure@unnamable-types.rs:23:31]>` cannot be named --> $DIR/unnamable-types.rs:23:11 | LL | const D = S { t: { let i = 0; move || -> i32 { i } } }; diff --git a/src/test/ui/typeck/typeck_type_placeholder_item.rs b/src/test/ui/typeck/typeck_type_placeholder_item.rs index 22fedb22d66c6..b96c527133959 100644 --- a/src/test/ui/typeck/typeck_type_placeholder_item.rs +++ b/src/test/ui/typeck/typeck_type_placeholder_item.rs @@ -220,3 +220,11 @@ fn value() -> Option<&'static _> { const _: Option<_> = map(value); //~^ ERROR the placeholder `_` is not allowed within types on item signatures for constants + +fn evens_squared(n: usize) -> _ { +//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types + (1..n).filter(|x| x % 2 == 0).map(|x| x * x) +} + +const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x); +//~^ ERROR the placeholder `_` is not allowed within types on item signatures for constants diff --git a/src/test/ui/typeck/typeck_type_placeholder_item.stderr b/src/test/ui/typeck/typeck_type_placeholder_item.stderr index c57f71b8057a9..bc02547c65eb8 100644 --- a/src/test/ui/typeck/typeck_type_placeholder_item.stderr +++ b/src/test/ui/typeck/typeck_type_placeholder_item.stderr @@ -428,6 +428,27 @@ LL | const _: Option<_> = map(value); | not allowed in type signatures | help: replace with the correct type: `Option` +error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types + --> $DIR/typeck_type_placeholder_item.rs:224:31 + | +LL | fn evens_squared(n: usize) -> _ { + | ^ + | | + | not allowed in type signatures + | help: replace with an appropriate return type: `impl Iterator` + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants + --> $DIR/typeck_type_placeholder_item.rs:229:10 + | +LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x); + | ^ not allowed in type signatures + | +note: however, the inferred type `Map, [closure@typeck_type_placeholder_item.rs:229:29]>, [closure@typeck_type_placeholder_item.rs:229:49]>` cannot be named + --> $DIR/typeck_type_placeholder_item.rs:229:14 + | +LL | const _: _ = (1..10).filter(|x| x % 2 == 0).map(|x| x * x); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions --> $DIR/typeck_type_placeholder_item.rs:140:31 | @@ -636,7 +657,7 @@ LL | const D: _ = 42; | not allowed in type signatures | help: replace with the correct type: `i32` -error: aborting due to 69 previous errors +error: aborting due to 71 previous errors Some errors have detailed explanations: E0121, E0282, E0403. For more information about an error, try `rustc --explain E0121`.