From 9f4934ec19af7fe970fc1278973ec5763ea0312c Mon Sep 17 00:00:00 2001 From: Jonathan Giddy Date: Wed, 23 Mar 2022 13:01:00 +0000 Subject: [PATCH 01/11] Clarify that `Cow::into_owned` returns owned data --- library/alloc/src/borrow.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/alloc/src/borrow.rs b/library/alloc/src/borrow.rs index 63234ee91f091..2597c30e6b156 100644 --- a/library/alloc/src/borrow.rs +++ b/library/alloc/src/borrow.rs @@ -292,8 +292,7 @@ impl Cow<'_, B> { /// /// # Examples /// - /// Calling `into_owned` on a `Cow::Borrowed` clones the underlying data - /// and becomes a `Cow::Owned`: + /// Calling `into_owned` on a `Cow::Borrowed` returns a clone of the borrowed data: /// /// ``` /// use std::borrow::Cow; @@ -307,7 +306,8 @@ impl Cow<'_, B> { /// ); /// ``` /// - /// Calling `into_owned` on a `Cow::Owned` is a no-op: + /// Calling `into_owned` on a `Cow::Owned` returns the owned data. The data is moved out of the + /// `Cow` without being cloned. /// /// ``` /// use std::borrow::Cow; From 8d5a4963df609e27d8daff49cee792f200e03408 Mon Sep 17 00:00:00 2001 From: Soni L Date: Mon, 11 Apr 2022 17:56:27 -0300 Subject: [PATCH 02/11] Implement Default for AssertUnwindSafe Trait impls are still insta-stable yeah...? --- library/core/src/panic/unwind_safe.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/library/core/src/panic/unwind_safe.rs b/library/core/src/panic/unwind_safe.rs index 95be879e319aa..f2948aac3c235 100644 --- a/library/core/src/panic/unwind_safe.rs +++ b/library/core/src/panic/unwind_safe.rs @@ -279,6 +279,13 @@ impl fmt::Debug for AssertUnwindSafe { } } +#[stable(feature = "assertunwindsafe_default", since = "1.62.0")] +impl Default for AssertUnwindSafe { + fn default() -> Self { + Self(Default::default()) + } +} + #[stable(feature = "futures_api", since = "1.36.0")] impl Future for AssertUnwindSafe { type Output = F::Output; From 875cd8930e7bd27eb61d280c58b15e967fddd641 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 24 Apr 2022 15:05:06 -0700 Subject: [PATCH 03/11] remove indentation in report_method_error --- .../rustc_typeck/src/check/method/suggest.rs | 342 +++++++++--------- 1 file changed, 162 insertions(+), 180 deletions(-) diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 2921176ca4b38..f45859cc50962 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -271,205 +271,187 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (None, true) => "variant", } }; - // FIXME(eddyb) this indentation is probably unnecessary. - let mut err = { - // Suggest clamping down the type if the method that is being attempted to - // be used exists at all, and the type is an ambiguous numeric type - // ({integer}/{float}). - let mut candidates = all_traits(self.tcx) - .into_iter() - .filter_map(|info| self.associated_value(info.def_id, item_name)); - // There are methods that are defined on the primitive types and won't be - // found when exploring `all_traits`, but we also need them to be accurate on - // our suggestions (#47759). - let found_assoc = |ty: Ty<'tcx>| { - simplify_type(tcx, ty, TreatParams::AsPlaceholders) - .and_then(|simp| { - tcx.incoherent_impls(simp) - .iter() - .find_map(|&id| self.associated_value(id, item_name)) - }) - .is_some() - }; - let found_candidate = candidates.next().is_some() - || found_assoc(tcx.types.i8) - || found_assoc(tcx.types.i16) - || found_assoc(tcx.types.i32) - || found_assoc(tcx.types.i64) - || found_assoc(tcx.types.i128) - || found_assoc(tcx.types.u8) - || found_assoc(tcx.types.u16) - || found_assoc(tcx.types.u32) - || found_assoc(tcx.types.u64) - || found_assoc(tcx.types.u128) - || found_assoc(tcx.types.f32) - || found_assoc(tcx.types.f32); - if let (true, false, SelfSource::MethodCall(expr), true) = ( - actual.is_numeric(), - actual.has_concrete_skeleton(), - source, - found_candidate, - ) { - let mut err = struct_span_err!( - tcx.sess, - span, - E0689, - "can't call {} `{}` on ambiguous numeric type `{}`", - item_kind, - item_name, - ty_str - ); - let concrete_type = if actual.is_integral() { "i32" } else { "f32" }; - match expr.kind { - ExprKind::Lit(ref lit) => { - // numeric literal - let snippet = tcx - .sess - .source_map() - .span_to_snippet(lit.span) - .unwrap_or_else(|_| "".to_owned()); - - // If this is a floating point literal that ends with '.', - // get rid of it to stop this from becoming a member access. - let snippet = snippet.strip_suffix('.').unwrap_or(&snippet); - - err.span_suggestion( - lit.span, - &format!( - "you must specify a concrete type for this numeric value, \ + // Suggest clamping down the type if the method that is being attempted to + // be used exists at all, and the type is an ambiguous numeric type + // ({integer}/{float}). + let mut candidates = all_traits(self.tcx) + .into_iter() + .filter_map(|info| self.associated_value(info.def_id, item_name)); + // There are methods that are defined on the primitive types and won't be + // found when exploring `all_traits`, but we also need them to be accurate on + // our suggestions (#47759). + let found_assoc = |ty: Ty<'tcx>| { + simplify_type(tcx, ty, TreatParams::AsPlaceholders) + .and_then(|simp| { + tcx.incoherent_impls(simp) + .iter() + .find_map(|&id| self.associated_value(id, item_name)) + }) + .is_some() + }; + let found_candidate = candidates.next().is_some() + || found_assoc(tcx.types.i8) + || found_assoc(tcx.types.i16) + || found_assoc(tcx.types.i32) + || found_assoc(tcx.types.i64) + || found_assoc(tcx.types.i128) + || found_assoc(tcx.types.u8) + || found_assoc(tcx.types.u16) + || found_assoc(tcx.types.u32) + || found_assoc(tcx.types.u64) + || found_assoc(tcx.types.u128) + || found_assoc(tcx.types.f32) + || found_assoc(tcx.types.f32); + if let (true, false, SelfSource::MethodCall(expr), true) = + (actual.is_numeric(), actual.has_concrete_skeleton(), source, found_candidate) + { + let mut err = struct_span_err!( + tcx.sess, + span, + E0689, + "can't call {} `{}` on ambiguous numeric type `{}`", + item_kind, + item_name, + ty_str + ); + let concrete_type = if actual.is_integral() { "i32" } else { "f32" }; + match expr.kind { + ExprKind::Lit(ref lit) => { + // numeric literal + let snippet = tcx + .sess + .source_map() + .span_to_snippet(lit.span) + .unwrap_or_else(|_| "".to_owned()); + + // If this is a floating point literal that ends with '.', + // get rid of it to stop this from becoming a member access. + let snippet = snippet.strip_suffix('.').unwrap_or(&snippet); + + err.span_suggestion( + lit.span, + &format!( + "you must specify a concrete type for this numeric value, \ like `{}`", - concrete_type - ), - format!("{snippet}_{concrete_type}"), - Applicability::MaybeIncorrect, + concrete_type + ), + format!("{snippet}_{concrete_type}"), + Applicability::MaybeIncorrect, + ); + } + ExprKind::Path(QPath::Resolved(_, path)) => { + // local binding + if let hir::def::Res::Local(hir_id) = path.res { + let span = tcx.hir().span(hir_id); + let snippet = tcx.sess.source_map().span_to_snippet(span); + let filename = tcx.sess.source_map().span_to_filename(span); + + let parent_node = + self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id)); + let msg = format!( + "you must specify a type for this binding, like `{}`", + concrete_type, ); - } - ExprKind::Path(QPath::Resolved(_, path)) => { - // local binding - if let hir::def::Res::Local(hir_id) = path.res { - let span = tcx.hir().span(hir_id); - let snippet = tcx.sess.source_map().span_to_snippet(span); - let filename = tcx.sess.source_map().span_to_filename(span); - - let parent_node = - self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id)); - let msg = format!( - "you must specify a type for this binding, like `{}`", - concrete_type, - ); - match (filename, parent_node, snippet) { - ( - FileName::Real(_), - Node::Local(hir::Local { - source: hir::LocalSource::Normal, - ty, - .. - }), - Ok(ref snippet), - ) => { - err.span_suggestion( - // account for `let x: _ = 42;` - // ^^^^ - span.to(ty - .as_ref() - .map(|ty| ty.span) - .unwrap_or(span)), - &msg, - format!("{}: {}", snippet, concrete_type), - Applicability::MaybeIncorrect, - ); - } - _ => { - err.span_label(span, msg); - } + match (filename, parent_node, snippet) { + ( + FileName::Real(_), + Node::Local(hir::Local { + source: hir::LocalSource::Normal, + ty, + .. + }), + Ok(ref snippet), + ) => { + err.span_suggestion( + // account for `let x: _ = 42;` + // ^^^^ + span.to(ty.as_ref().map(|ty| ty.span).unwrap_or(span)), + &msg, + format!("{}: {}", snippet, concrete_type), + Applicability::MaybeIncorrect, + ); + } + _ => { + err.span_label(span, msg); } } } - _ => {} } - err.emit(); - return None; - } else { - span = item_name.span; - - // Don't show generic arguments when the method can't be found in any implementation (#81576). - let mut ty_str_reported = ty_str.clone(); - if let ty::Adt(_, generics) = actual.kind() { - if generics.len() > 0 { - let mut autoderef = self.autoderef(span, actual); - let candidate_found = autoderef.any(|(ty, _)| { - if let ty::Adt(adt_deref, _) = ty.kind() { - self.tcx - .inherent_impls(adt_deref.did()) - .iter() - .filter_map(|def_id| { - self.associated_value(*def_id, item_name) - }) - .count() - >= 1 - } else { - false - } - }); - let has_deref = autoderef.step_count() > 0; - if !candidate_found - && !has_deref - && unsatisfied_predicates.is_empty() - { - if let Some((path_string, _)) = ty_str.split_once('<') { - ty_str_reported = path_string.to_string(); - } - } + _ => {} + } + err.emit(); + return None; + } + span = item_name.span; + + // Don't show generic arguments when the method can't be found in any implementation (#81576). + let mut ty_str_reported = ty_str.clone(); + if let ty::Adt(_, generics) = actual.kind() { + if generics.len() > 0 { + let mut autoderef = self.autoderef(span, actual); + let candidate_found = autoderef.any(|(ty, _)| { + if let ty::Adt(adt_deref, _) = ty.kind() { + self.tcx + .inherent_impls(adt_deref.did()) + .iter() + .filter_map(|def_id| self.associated_value(*def_id, item_name)) + .count() + >= 1 + } else { + false + } + }); + let has_deref = autoderef.step_count() > 0; + if !candidate_found && !has_deref && unsatisfied_predicates.is_empty() { + if let Some((path_string, _)) = ty_str.split_once('<') { + ty_str_reported = path_string.to_string(); } } + } + } - let mut err = struct_span_err!( - tcx.sess, - span, - E0599, - "no {} named `{}` found for {} `{}` in the current scope", - item_kind, - item_name, - actual.prefix_string(self.tcx), - ty_str_reported, - ); - if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source { + let mut err = struct_span_err!( + tcx.sess, + span, + E0599, + "no {} named `{}` found for {} `{}` in the current scope", + item_kind, + item_name, + actual.prefix_string(self.tcx), + ty_str_reported, + ); + if actual.references_error() { + err.downgrade_to_delayed_bug(); + } + + if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source { self.suggest_await_before_method( &mut err, item_name, actual, cal, span, ); } - if let Some(span) = - tcx.resolutions(()).confused_type_with_std_module.get(&span) - { - if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*span) { - err.span_suggestion( - *span, - "you are looking for the module in `std`, \ + if let Some(span) = tcx.resolutions(()).confused_type_with_std_module.get(&span) { + if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*span) { + err.span_suggestion( + *span, + "you are looking for the module in `std`, \ not the primitive type", - format!("std::{}", snippet), - Applicability::MachineApplicable, - ); - } - } - if let ty::RawPtr(_) = &actual.kind() { - err.note( - "try using `<*const T>::as_ref()` to get a reference to the \ + format!("std::{}", snippet), + Applicability::MachineApplicable, + ); + } + } + if let ty::RawPtr(_) = &actual.kind() { + err.note( + "try using `<*const T>::as_ref()` to get a reference to the \ type behind the pointer: https://doc.rust-lang.org/std/\ primitive.pointer.html#method.as_ref", - ); - err.note( - "using `<*const T>::as_ref()` on a pointer \ + ); + err.note( + "using `<*const T>::as_ref()` on a pointer \ which is unaligned or points to invalid \ or uninitialized memory is undefined behavior", - ); - } - err - } - }; - - if actual.references_error() { - err.downgrade_to_delayed_bug(); + ); } if let Some(def) = actual.ty_adt_def() { From 2da65da5a753a9c63ac4789afdfccc783a806c61 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 24 Apr 2022 15:24:20 -0700 Subject: [PATCH 04/11] pull some methods out of report_method_error --- .../rustc_typeck/src/check/method/suggest.rs | 364 ++++++++++-------- 1 file changed, 196 insertions(+), 168 deletions(-) diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index f45859cc50962..4329c88e81e25 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -271,118 +271,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { (None, true) => "variant", } }; - // Suggest clamping down the type if the method that is being attempted to - // be used exists at all, and the type is an ambiguous numeric type - // ({integer}/{float}). - let mut candidates = all_traits(self.tcx) - .into_iter() - .filter_map(|info| self.associated_value(info.def_id, item_name)); - // There are methods that are defined on the primitive types and won't be - // found when exploring `all_traits`, but we also need them to be accurate on - // our suggestions (#47759). - let found_assoc = |ty: Ty<'tcx>| { - simplify_type(tcx, ty, TreatParams::AsPlaceholders) - .and_then(|simp| { - tcx.incoherent_impls(simp) - .iter() - .find_map(|&id| self.associated_value(id, item_name)) - }) - .is_some() - }; - let found_candidate = candidates.next().is_some() - || found_assoc(tcx.types.i8) - || found_assoc(tcx.types.i16) - || found_assoc(tcx.types.i32) - || found_assoc(tcx.types.i64) - || found_assoc(tcx.types.i128) - || found_assoc(tcx.types.u8) - || found_assoc(tcx.types.u16) - || found_assoc(tcx.types.u32) - || found_assoc(tcx.types.u64) - || found_assoc(tcx.types.u128) - || found_assoc(tcx.types.f32) - || found_assoc(tcx.types.f32); - if let (true, false, SelfSource::MethodCall(expr), true) = - (actual.is_numeric(), actual.has_concrete_skeleton(), source, found_candidate) - { - let mut err = struct_span_err!( - tcx.sess, - span, - E0689, - "can't call {} `{}` on ambiguous numeric type `{}`", - item_kind, - item_name, - ty_str - ); - let concrete_type = if actual.is_integral() { "i32" } else { "f32" }; - match expr.kind { - ExprKind::Lit(ref lit) => { - // numeric literal - let snippet = tcx - .sess - .source_map() - .span_to_snippet(lit.span) - .unwrap_or_else(|_| "".to_owned()); - - // If this is a floating point literal that ends with '.', - // get rid of it to stop this from becoming a member access. - let snippet = snippet.strip_suffix('.').unwrap_or(&snippet); - err.span_suggestion( - lit.span, - &format!( - "you must specify a concrete type for this numeric value, \ - like `{}`", - concrete_type - ), - format!("{snippet}_{concrete_type}"), - Applicability::MaybeIncorrect, - ); - } - ExprKind::Path(QPath::Resolved(_, path)) => { - // local binding - if let hir::def::Res::Local(hir_id) = path.res { - let span = tcx.hir().span(hir_id); - let snippet = tcx.sess.source_map().span_to_snippet(span); - let filename = tcx.sess.source_map().span_to_filename(span); - - let parent_node = - self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id)); - let msg = format!( - "you must specify a type for this binding, like `{}`", - concrete_type, - ); - - match (filename, parent_node, snippet) { - ( - FileName::Real(_), - Node::Local(hir::Local { - source: hir::LocalSource::Normal, - ty, - .. - }), - Ok(ref snippet), - ) => { - err.span_suggestion( - // account for `let x: _ = 42;` - // ^^^^ - span.to(ty.as_ref().map(|ty| ty.span).unwrap_or(span)), - &msg, - format!("{}: {}", snippet, concrete_type), - Applicability::MaybeIncorrect, - ); - } - _ => { - err.span_label(span, msg); - } - } - } - } - _ => {} - } - err.emit(); + if self.suggest_constraining_numerical_ty( + tcx, actual, source, span, item_kind, item_name, &ty_str, + ) { return None; } + span = item_name.span; // Don't show generic arguments when the method can't be found in any implementation (#81576). @@ -426,10 +321,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source { - self.suggest_await_before_method( - &mut err, item_name, actual, cal, span, - ); - } + self.suggest_await_before_method( + &mut err, item_name, actual, cal, span, + ); + } if let Some(span) = tcx.resolutions(()).confused_type_with_std_module.get(&span) { if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*span) { err.span_suggestion( @@ -967,7 +862,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - let mut label_span_not_found = || { + let label_span_not_found = |err: &mut DiagnosticBuilder<'_, _>| { if unsatisfied_predicates.is_empty() { err.span_label(span, format!("{item_kind} not found in `{ty_str}`")); let is_string_or_ref_str = match actual.kind() { @@ -1053,62 +948,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the method name is the name of a field with a function or closure type, // give a helping note that it has to be called as `(x.f)(...)`. if let SelfSource::MethodCall(expr) = source { - let field_receiver = - self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() { - ty::Adt(def, substs) if !def.is_enum() => { - let variant = &def.non_enum_variant(); - self.tcx.find_field_index(item_name, variant).map(|index| { - let field = &variant.fields[index]; - let field_ty = field.ty(tcx, substs); - (field, field_ty) - }) - } - _ => None, - }); - - if let Some((field, field_ty)) = field_receiver { - let scope = self.tcx.parent_module(self.body_id).to_def_id(); - let is_accessible = field.vis.is_accessible_from(scope, self.tcx); - - if is_accessible { - if self.is_fn_ty(field_ty, span) { - let expr_span = expr.span.to(item_name.span); - err.multipart_suggestion( - &format!( - "to call the function stored in `{}`, \ - surround the field access with parentheses", - item_name, - ), - vec![ - (expr_span.shrink_to_lo(), '('.to_string()), - (expr_span.shrink_to_hi(), ')'.to_string()), - ], - Applicability::MachineApplicable, - ); - } else { - let call_expr = self - .tcx - .hir() - .expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)); - - if let Some(span) = call_expr.span.trim_start(item_name.span) { - err.span_suggestion( - span, - "remove the arguments", - String::new(), - Applicability::MaybeIncorrect, - ); - } - } - } - - let field_kind = if is_accessible { "field" } else { "private field" }; - err.span_label(item_name.span, format!("{}, not a method", field_kind)); - } else if lev_candidate.is_none() && !custom_span_label { - label_span_not_found(); + if !self.suggest_field_call(span, rcvr_ty, expr, item_name, &mut err) + && lev_candidate.is_none() + && !custom_span_label + { + label_span_not_found(&mut err); } } else if !custom_span_label { - label_span_not_found(); + label_span_not_found(&mut err); } bound_spans.sort(); @@ -1255,6 +1102,187 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { None } + fn suggest_field_call( + &self, + span: Span, + rcvr_ty: Ty<'tcx>, + expr: &hir::Expr<'_>, + item_name: Ident, + err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, + ) -> bool { + let tcx = self.tcx; + let field_receiver = self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() { + ty::Adt(def, substs) if !def.is_enum() => { + let variant = &def.non_enum_variant(); + tcx.find_field_index(item_name, variant).map(|index| { + let field = &variant.fields[index]; + let field_ty = field.ty(tcx, substs); + (field, field_ty) + }) + } + _ => None, + }); + if let Some((field, field_ty)) = field_receiver { + let scope = tcx.parent_module(self.body_id).to_def_id(); + let is_accessible = field.vis.is_accessible_from(scope, tcx); + + if is_accessible { + if self.is_fn_ty(field_ty, span) { + let expr_span = expr.span.to(item_name.span); + err.multipart_suggestion( + &format!( + "to call the function stored in `{}`, \ + surround the field access with parentheses", + item_name, + ), + vec![ + (expr_span.shrink_to_lo(), '('.to_string()), + (expr_span.shrink_to_hi(), ')'.to_string()), + ], + Applicability::MachineApplicable, + ); + } else { + let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id)); + + if let Some(span) = call_expr.span.trim_start(item_name.span) { + err.span_suggestion( + span, + "remove the arguments", + String::new(), + Applicability::MaybeIncorrect, + ); + } + } + } + + let field_kind = if is_accessible { "field" } else { "private field" }; + err.span_label(item_name.span, format!("{}, not a method", field_kind)); + return true; + } + false + } + + fn suggest_constraining_numerical_ty( + &self, + tcx: TyCtxt<'tcx>, + actual: Ty<'tcx>, + source: SelfSource<'_>, + span: Span, + item_kind: &str, + item_name: Ident, + ty_str: &str, + ) -> bool { + let found_candidate = all_traits(self.tcx) + .into_iter() + .any(|info| self.associated_value(info.def_id, item_name).is_some()); + let found_assoc = |ty: Ty<'tcx>| { + simplify_type(tcx, ty, TreatParams::AsPlaceholders) + .and_then(|simp| { + tcx.incoherent_impls(simp) + .iter() + .find_map(|&id| self.associated_value(id, item_name)) + }) + .is_some() + }; + let found_candidate = found_candidate + || found_assoc(tcx.types.i8) + || found_assoc(tcx.types.i16) + || found_assoc(tcx.types.i32) + || found_assoc(tcx.types.i64) + || found_assoc(tcx.types.i128) + || found_assoc(tcx.types.u8) + || found_assoc(tcx.types.u16) + || found_assoc(tcx.types.u32) + || found_assoc(tcx.types.u64) + || found_assoc(tcx.types.u128) + || found_assoc(tcx.types.f32) + || found_assoc(tcx.types.f32); + if found_candidate + && actual.is_numeric() + && !actual.has_concrete_skeleton() + && let SelfSource::MethodCall(expr) = source + { + let mut err = struct_span_err!( + tcx.sess, + span, + E0689, + "can't call {} `{}` on ambiguous numeric type `{}`", + item_kind, + item_name, + ty_str + ); + let concrete_type = if actual.is_integral() { "i32" } else { "f32" }; + match expr.kind { + ExprKind::Lit(ref lit) => { + // numeric literal + let snippet = tcx + .sess + .source_map() + .span_to_snippet(lit.span) + .unwrap_or_else(|_| "".to_owned()); + + // If this is a floating point literal that ends with '.', + // get rid of it to stop this from becoming a member access. + let snippet = snippet.strip_suffix('.').unwrap_or(&snippet); + + err.span_suggestion( + lit.span, + &format!( + "you must specify a concrete type for this numeric value, \ + like `{}`", + concrete_type + ), + format!("{snippet}_{concrete_type}"), + Applicability::MaybeIncorrect, + ); + } + ExprKind::Path(QPath::Resolved(_, path)) => { + // local binding + if let hir::def::Res::Local(hir_id) = path.res { + let span = tcx.hir().span(hir_id); + let snippet = tcx.sess.source_map().span_to_snippet(span); + let filename = tcx.sess.source_map().span_to_filename(span); + + let parent_node = + self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id)); + let msg = format!( + "you must specify a type for this binding, like `{}`", + concrete_type, + ); + + match (filename, parent_node, snippet) { + ( + FileName::Real(_), + Node::Local(hir::Local { + source: hir::LocalSource::Normal, + ty, + .. + }), + Ok(ref snippet), + ) => { + err.span_suggestion( + // account for `let x: _ = 42;` + // ^^^^ + span.to(ty.as_ref().map(|ty| ty.span).unwrap_or(span)), + &msg, + format!("{}: {}", snippet, concrete_type), + Applicability::MaybeIncorrect, + ); + } + _ => { + err.span_label(span, msg); + } + } + } + } + _ => {} + } + err.emit(); + return true; + } + false + } + crate fn note_unmet_impls_on_type( &self, err: &mut Diagnostic, From e1b074a2a8b89741621d13cdf22ce893e2c3fb8b Mon Sep 17 00:00:00 2001 From: marmeladema Date: Sun, 24 Apr 2022 16:44:09 +0200 Subject: [PATCH 05/11] Recover most `impl Trait` and `dyn Trait` lifetime bound suggestions under NLL --- .../src/diagnostics/region_errors.rs | 119 ++++++--------- .../error_reporting/nice_region_error/mod.rs | 1 + .../error_reporting/nice_region_error/util.rs | 138 +++++++++--------- .../issues/issue-88236-2.nll.stderr | 6 +- ...t_outlive_least_region_or_bound.nll.stderr | 36 ++++- src/test/ui/issues/issue-16922.nll.stderr | 5 + src/test/ui/nll/mir_check_cast_unsize.stderr | 5 + ...lifetime-default-from-box-error.nll.stderr | 5 + ...ion-object-lifetime-in-coercion.nll.stderr | 23 +++ ...ions-close-object-into-object-2.nll.stderr | 9 ++ ...ions-close-object-into-object-4.nll.stderr | 9 ++ .../regions-proc-bound-capture.nll.stderr | 9 ++ ...ait-object-nested-in-impl-trait.nll.stderr | 22 ++- .../dyn-trait-underscore.nll.stderr | 5 + 14 files changed, 244 insertions(+), 148 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index fd78b483b75f2..b5ee4a5edce7d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -1,17 +1,17 @@ //! Error reporting machinery for lifetime errors. -use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; +use rustc_errors::{Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_infer::infer::{ - error_reporting::nice_region_error::NiceRegionError, - error_reporting::unexpected_hidden_region_diagnostic, NllRegionVariableOrigin, - RelateParamBound, + error_reporting::nice_region_error::{self, find_param_with_region, NiceRegionError}, + error_reporting::unexpected_hidden_region_diagnostic, + NllRegionVariableOrigin, RelateParamBound, }; use rustc_middle::hir::place::PlaceBase; use rustc_middle::mir::{ConstraintCategory, ReturnConstraint}; -use rustc_middle::ty::subst::{InternalSubsts, Subst}; +use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::{self, RegionVid, Ty}; -use rustc_span::symbol::{kw, sym}; -use rustc_span::{BytePos, Span}; +use rustc_span::symbol::sym; +use rustc_span::Span; use crate::borrowck_errors; @@ -651,82 +651,47 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { fr_name: RegionName, outlived_fr: RegionVid, ) { - if let (Some(f), Some(ty::ReStatic)) = - (self.to_error_region(fr), self.to_error_region(outlived_fr).as_deref()) + if let (Some(f), Some(outlived_f)) = + (self.to_error_region(fr), self.to_error_region(outlived_fr)) { - if let Some(&ty::Opaque(did, substs)) = self + if *outlived_f != ty::ReStatic { + return; + } + + let fn_returns = self .infcx .tcx .is_suitable_region(f) - .map(|r| r.def_id) - .and_then(|id| self.infcx.tcx.return_type_impl_trait(id)) - .map(|(ty, _)| ty.kind()) - { - // Check whether or not the impl trait return type is intended to capture - // data with the static lifetime. - // - // eg. check for `impl Trait + 'static` instead of `impl Trait`. - let has_static_predicate = { - let bounds = self.infcx.tcx.explicit_item_bounds(did); - - let mut found = false; - for (bound, _) in bounds { - if let ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(_, r)) = - bound.kind().skip_binder() - { - let r = r.subst(self.infcx.tcx, substs); - if r.is_static() { - found = true; - break; - } else { - // If there's already a lifetime bound, don't - // suggest anything. - return; - } - } - } - - found - }; + .map(|r| self.infcx.tcx.return_type_impl_or_dyn_traits(r.def_id)) + .unwrap_or_default(); - debug!( - "add_static_impl_trait_suggestion: has_static_predicate={:?}", - has_static_predicate - ); - let static_str = kw::StaticLifetime; - // If there is a static predicate, then the only sensible suggestion is to replace - // fr with `'static`. - if has_static_predicate { - diag.help(&format!("consider replacing `{fr_name}` with `{static_str}`")); - } else { - // Otherwise, we should suggest adding a constraint on the return type. - let span = self.infcx.tcx.def_span(did); - if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) { - let suggestable_fr_name = if fr_name.was_named() { - fr_name.to_string() - } else { - "'_".to_string() - }; - let span = if snippet.ends_with(';') { - // `type X = impl Trait;` - span.with_hi(span.hi() - BytePos(1)) - } else { - span - }; - let suggestion = format!(" + {suggestable_fr_name}"); - let span = span.shrink_to_hi(); - diag.span_suggestion( - span, - &format!( - "to allow this `impl Trait` to capture borrowed data with lifetime \ - `{fr_name}`, add `{suggestable_fr_name}` as a bound", - ), - suggestion, - Applicability::MachineApplicable, - ); - } - } + if fn_returns.is_empty() { + return; } + + let param = if let Some(param) = find_param_with_region(self.infcx.tcx, f, outlived_f) { + param + } else { + return; + }; + + let lifetime = if f.has_name() { fr_name.to_string() } else { "'_".to_string() }; + + let arg = match param.param.pat.simple_ident() { + Some(simple_ident) => format!("argument `{}`", simple_ident), + None => "the argument".to_string(), + }; + let captures = format!("captures data from {}", arg); + + return nice_region_error::suggest_new_region_bound( + self.infcx.tcx, + diag, + fn_returns, + lifetime, + Some(arg), + captures, + Some((param.param_ty_span, param.param_ty.to_string())), + ); } } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs index df81aea6ef9ec..79f852d8a95e7 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs @@ -15,6 +15,7 @@ mod trait_impl_difference; mod util; pub use static_impl_trait::suggest_new_region_bound; +pub use util::find_param_with_region; impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { pub fn try_report_nice_region_error(&self, error: &RegionResolutionError<'tcx>) -> bool { diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs index 719f6b37a4343..7d3ed2ed38a30 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs @@ -2,6 +2,7 @@ //! anonymous regions. use crate::infer::error_reporting::nice_region_error::NiceRegionError; +use crate::infer::TyCtxt; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::{self, Binder, DefIdTree, Region, Ty, TypeFoldable}; @@ -9,7 +10,7 @@ use rustc_span::Span; /// Information about the anonymous region we are searching for. #[derive(Debug)] -pub(super) struct AnonymousParamInfo<'tcx> { +pub struct AnonymousParamInfo<'tcx> { /// The parameter corresponding to the anonymous region. pub param: &'tcx hir::Param<'tcx>, /// The type corresponding to the anonymous region parameter. @@ -22,76 +23,83 @@ pub(super) struct AnonymousParamInfo<'tcx> { pub is_first: bool, } +// This method walks the Type of the function body parameters using +// `fold_regions()` function and returns the +// &hir::Param of the function parameter corresponding to the anonymous +// region and the Ty corresponding to the named region. +// Currently only the case where the function declaration consists of +// one named region and one anonymous region is handled. +// Consider the example `fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32` +// Here, we would return the hir::Param for y, we return the type &'a +// i32, which is the type of y but with the anonymous region replaced +// with 'a, the corresponding bound region and is_first which is true if +// the hir::Param is the first parameter in the function declaration. +pub fn find_param_with_region<'tcx>( + tcx: TyCtxt<'tcx>, + anon_region: Region<'tcx>, + replace_region: Region<'tcx>, +) -> Option> { + let (id, bound_region) = match *anon_region { + ty::ReFree(ref free_region) => (free_region.scope, free_region.bound_region), + ty::ReEarlyBound(ebr) => { + (tcx.parent(ebr.def_id).unwrap(), ty::BoundRegionKind::BrNamed(ebr.def_id, ebr.name)) + } + _ => return None, // not a free region + }; + + let hir = &tcx.hir(); + let hir_id = hir.local_def_id_to_hir_id(id.as_local()?); + let body_id = hir.maybe_body_owned_by(hir_id)?; + let body = hir.body(body_id); + let owner_id = hir.body_owner(body_id); + let fn_decl = hir.fn_decl_by_hir_id(owner_id).unwrap(); + let poly_fn_sig = tcx.fn_sig(id); + let fn_sig = tcx.liberate_late_bound_regions(id, poly_fn_sig); + body.params + .iter() + .take(if fn_sig.c_variadic { + fn_sig.inputs().len() + } else { + assert_eq!(fn_sig.inputs().len(), body.params.len()); + body.params.len() + }) + .enumerate() + .find_map(|(index, param)| { + // May return None; sometimes the tables are not yet populated. + let ty = fn_sig.inputs()[index]; + let mut found_anon_region = false; + let new_param_ty = tcx.fold_regions(ty, &mut false, |r, _| { + if r == anon_region { + found_anon_region = true; + replace_region + } else { + r + } + }); + if found_anon_region { + let ty_hir_id = fn_decl.inputs[index].hir_id; + let param_ty_span = hir.span(ty_hir_id); + let is_first = index == 0; + Some(AnonymousParamInfo { + param, + param_ty: new_param_ty, + param_ty_span, + bound_region, + is_first, + }) + } else { + None + } + }) +} + impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { - // This method walks the Type of the function body parameters using - // `fold_regions()` function and returns the - // &hir::Param of the function parameter corresponding to the anonymous - // region and the Ty corresponding to the named region. - // Currently only the case where the function declaration consists of - // one named region and one anonymous region is handled. - // Consider the example `fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32` - // Here, we would return the hir::Param for y, we return the type &'a - // i32, which is the type of y but with the anonymous region replaced - // with 'a, the corresponding bound region and is_first which is true if - // the hir::Param is the first parameter in the function declaration. pub(super) fn find_param_with_region( &self, anon_region: Region<'tcx>, replace_region: Region<'tcx>, ) -> Option> { - let (id, bound_region) = match *anon_region { - ty::ReFree(ref free_region) => (free_region.scope, free_region.bound_region), - ty::ReEarlyBound(ebr) => ( - self.tcx().parent(ebr.def_id).unwrap(), - ty::BoundRegionKind::BrNamed(ebr.def_id, ebr.name), - ), - _ => return None, // not a free region - }; - - let hir = &self.tcx().hir(); - let hir_id = hir.local_def_id_to_hir_id(id.as_local()?); - let body_id = hir.maybe_body_owned_by(hir_id)?; - let body = hir.body(body_id); - let owner_id = hir.body_owner(body_id); - let fn_decl = hir.fn_decl_by_hir_id(owner_id).unwrap(); - let poly_fn_sig = self.tcx().fn_sig(id); - let fn_sig = self.tcx().liberate_late_bound_regions(id, poly_fn_sig); - body.params - .iter() - .take(if fn_sig.c_variadic { - fn_sig.inputs().len() - } else { - assert_eq!(fn_sig.inputs().len(), body.params.len()); - body.params.len() - }) - .enumerate() - .find_map(|(index, param)| { - // May return None; sometimes the tables are not yet populated. - let ty = fn_sig.inputs()[index]; - let mut found_anon_region = false; - let new_param_ty = self.tcx().fold_regions(ty, &mut false, |r, _| { - if r == anon_region { - found_anon_region = true; - replace_region - } else { - r - } - }); - if found_anon_region { - let ty_hir_id = fn_decl.inputs[index].hir_id; - let param_ty_span = hir.span(ty_hir_id); - let is_first = index == 0; - Some(AnonymousParamInfo { - param, - param_ty: new_param_ty, - param_ty_span, - bound_region, - is_first, - }) - } else { - None - } - }) + find_param_with_region(self.tcx(), anon_region, replace_region) } // Here, we check for the case where the anonymous region diff --git a/src/test/ui/impl-trait/issues/issue-88236-2.nll.stderr b/src/test/ui/impl-trait/issues/issue-88236-2.nll.stderr index 9cf8ff76c87f8..66cffa9e36c0d 100644 --- a/src/test/ui/impl-trait/issues/issue-88236-2.nll.stderr +++ b/src/test/ui/impl-trait/issues/issue-88236-2.nll.stderr @@ -24,10 +24,14 @@ LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send LL | x | ^ returning this value requires that `'b` must outlive `'static` | -help: to allow this `impl Trait` to capture borrowed data with lifetime `'b`, add `'b` as a bound +help: to declare that the `impl Trait` captures data from argument `x`, you can add an explicit `'b` lifetime bound | LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> + 'b { | ++++ +help: to declare that the `impl Trait` captures data from argument `x`, you can add an explicit `'b` lifetime bound + | +LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a + 'b> { + | ++++ error: implementation of `Hrtb` is not general enough --> $DIR/issue-88236-2.rs:20:5 diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr index 0059f729bae2f..5a190649b6319 100644 --- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr +++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr @@ -32,7 +32,14 @@ LL | fn elided2(x: &i32) -> impl Copy + 'static { x } | | | let's call the lifetime of this reference `'1` | - = help: consider replacing `'1` with `'static` +help: consider changing the `impl Trait`'s explicit `'static` bound to the lifetime of argument `x` + | +LL | fn elided2(x: &i32) -> impl Copy + '_ { x } + | ~~ +help: alternatively, add an explicit `'static` bound to this reference + | +LL | fn elided2(x: &'static i32) -> impl Copy + 'static { x } + | ~~~~~~~~~~~~ error: lifetime may not live long enough --> $DIR/must_outlive_least_region_or_bound.rs:11:55 @@ -40,7 +47,14 @@ error: lifetime may not live long enough LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } | -- lifetime `'a` defined here ^ returning this value requires that `'a` must outlive `'static` | - = help: consider replacing `'a` with `'static` +help: consider changing the `impl Trait`'s explicit `'static` bound to the lifetime of argument `x` + | +LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'a { x } + | ~~ +help: alternatively, add an explicit `'static` bound to this reference + | +LL | fn explicit2<'a>(x: &'static i32) -> impl Copy + 'static { x } + | ~~~~~~~~~~~~ error[E0621]: explicit lifetime required in the type of `x` --> $DIR/must_outlive_least_region_or_bound.rs:13:41 @@ -57,6 +71,15 @@ LL | fn elided5(x: &i32) -> (Box, impl Debug) { (Box::new(x), x) } | - ^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static` | | | let's call the lifetime of this reference `'1` + | +help: to declare that the trait object captures data from argument `x`, you can add an explicit `'_` lifetime bound + | +LL | fn elided5(x: &i32) -> (Box, impl Debug) { (Box::new(x), x) } + | ++++ +help: to declare that the `impl Trait` captures data from argument `x`, you can add an explicit `'_` lifetime bound + | +LL | fn elided5(x: &i32) -> (Box, impl Debug + '_) { (Box::new(x), x) } + | ++++ error: lifetime may not live long enough --> $DIR/must_outlive_least_region_or_bound.rs:29:69 @@ -64,7 +87,14 @@ error: lifetime may not live long enough LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } | -- lifetime `'a` defined here ^ returning this value requires that `'a` must outlive `'static` | - = help: consider replacing `'a` with `'static` +help: consider changing the `impl Trait`'s explicit `'static` bound to the lifetime of argument `x` + | +LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'a { x } + | ~~ +help: alternatively, add an explicit `'static` bound to this reference + | +LL | fn with_bound<'a>(x: &'static i32) -> impl LifetimeTrait<'a> + 'static { x } + | ~~~~~~~~~~~~ error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds --> $DIR/must_outlive_least_region_or_bound.rs:34:5 diff --git a/src/test/ui/issues/issue-16922.nll.stderr b/src/test/ui/issues/issue-16922.nll.stderr index 7f4f5b22eb302..9d9f32a97c065 100644 --- a/src/test/ui/issues/issue-16922.nll.stderr +++ b/src/test/ui/issues/issue-16922.nll.stderr @@ -5,6 +5,11 @@ LL | fn foo(value: &T) -> Box { | - let's call the lifetime of this reference `'1` LL | Box::new(value) as Box | ^^^^^^^^^^^^^^^ cast requires that `'1` must outlive `'static` + | +help: to declare that the trait object captures data from argument `value`, you can add an explicit `'_` lifetime bound + | +LL | fn foo(value: &T) -> Box { + | ++++ error: aborting due to previous error diff --git a/src/test/ui/nll/mir_check_cast_unsize.stderr b/src/test/ui/nll/mir_check_cast_unsize.stderr index 364d6c17ea7f6..8d02ef71d1bc6 100644 --- a/src/test/ui/nll/mir_check_cast_unsize.stderr +++ b/src/test/ui/nll/mir_check_cast_unsize.stderr @@ -5,6 +5,11 @@ LL | fn bar<'a>(x: &'a u32) -> &'static dyn Debug { | -- lifetime `'a` defined here LL | x | ^ returning this value requires that `'a` must outlive `'static` + | +help: to declare that the trait object captures data from argument `x`, you can add an explicit `'a` lifetime bound + | +LL | fn bar<'a>(x: &'a u32) -> &'static dyn Debug + 'a { + | ++++ error: aborting due to previous error diff --git a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr index ae02c58d080aa..43695a7511d27 100644 --- a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr +++ b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr @@ -6,6 +6,11 @@ LL | fn load(ss: &mut SomeStruct) -> Box { ... LL | ss.r | ^^^^ returning this value requires that `'1` must outlive `'static` + | +help: to declare that the trait object captures data from argument `ss`, you can add an explicit `'_` lifetime bound + | +LL | fn load(ss: &mut SomeStruct) -> Box { + | ++++ error[E0507]: cannot move out of `ss.r` which is behind a mutable reference --> $DIR/object-lifetime-default-from-box-error.rs:18:5 diff --git a/src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr b/src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr index 4d72724586e9a..724b06ce8b168 100644 --- a/src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr +++ b/src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr @@ -5,6 +5,15 @@ LL | fn a(v: &[u8]) -> Box { | - let's call the lifetime of this reference `'1` LL | let x: Box = Box::new(v); | ^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'1` must outlive `'static` + | +help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v` + | +LL | fn a(v: &[u8]) -> Box { + | ~~ +help: alternatively, add an explicit `'static` bound to this reference + | +LL | fn a(v: &'static [u8]) -> Box { + | ~~~~~~~~~~~~~ error: lifetime may not live long enough --> $DIR/region-object-lifetime-in-coercion.rs:19:5 @@ -13,6 +22,15 @@ LL | fn b(v: &[u8]) -> Box { | - let's call the lifetime of this reference `'1` LL | Box::new(v) | ^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static` + | +help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v` + | +LL | fn b(v: &[u8]) -> Box { + | ~~ +help: alternatively, add an explicit `'static` bound to this reference + | +LL | fn b(v: &'static [u8]) -> Box { + | ~~~~~~~~~~~~~ error: lifetime may not live long enough --> $DIR/region-object-lifetime-in-coercion.rs:27:5 @@ -22,6 +40,11 @@ LL | fn c(v: &[u8]) -> Box { ... LL | Box::new(v) | ^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static` + | +help: to declare that the trait object captures data from argument `v`, you can add an explicit `'_` lifetime bound + | +LL | fn c(v: &[u8]) -> Box { + | ++++ error: lifetime may not live long enough --> $DIR/region-object-lifetime-in-coercion.rs:33:5 diff --git a/src/test/ui/regions/regions-close-object-into-object-2.nll.stderr b/src/test/ui/regions/regions-close-object-into-object-2.nll.stderr index 1a79da2776b0a..473c99b672fb6 100644 --- a/src/test/ui/regions/regions-close-object-into-object-2.nll.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-2.nll.stderr @@ -5,6 +5,15 @@ LL | fn g<'a, T: 'static>(v: Box + 'a>) -> Box { | -- lifetime `'a` defined here LL | Box::new(B(&*v)) as Box | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static` + | +help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v` + | +LL | fn g<'a, T: 'static>(v: Box + 'a>) -> Box { + | ~~ +help: alternatively, add an explicit `'static` bound to this reference + | +LL | fn g<'a, T: 'static>(v: Box<(dyn A + 'static)>) -> Box { + | ~~~~~~~~~~~~~~~~~~~~~~~~~ error[E0515]: cannot return value referencing local data `*v` --> $DIR/regions-close-object-into-object-2.rs:13:5 diff --git a/src/test/ui/regions/regions-close-object-into-object-4.nll.stderr b/src/test/ui/regions/regions-close-object-into-object-4.nll.stderr index 993b13ddbf803..05ddc09b2d0b7 100644 --- a/src/test/ui/regions/regions-close-object-into-object-4.nll.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-4.nll.stderr @@ -29,6 +29,15 @@ LL | fn i<'a, T, U>(v: Box+'a>) -> Box { | -- lifetime `'a` defined here LL | Box::new(B(&*v)) as Box | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static` + | +help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v` + | +LL | fn i<'a, T, U>(v: Box+'a>) -> Box { + | ~~ +help: alternatively, add an explicit `'static` bound to this reference + | +LL | fn i<'a, T, U>(v: Box<(dyn A + 'static)>) -> Box { + | ~~~~~~~~~~~~~~~~~~~~~~~~~ error[E0515]: cannot return value referencing local data `*v` --> $DIR/regions-close-object-into-object-4.rs:13:5 diff --git a/src/test/ui/regions/regions-proc-bound-capture.nll.stderr b/src/test/ui/regions/regions-proc-bound-capture.nll.stderr index 6120a53eb0955..ce4d2d4d111c3 100644 --- a/src/test/ui/regions/regions-proc-bound-capture.nll.stderr +++ b/src/test/ui/regions/regions-proc-bound-capture.nll.stderr @@ -6,6 +6,15 @@ LL | fn static_proc(x: &isize) -> Box (isize) + 'static> { LL | // This is illegal, because the region bound on `proc` is 'static. LL | Box::new(move || { *x }) | ^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static` + | +help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `x` + | +LL | fn static_proc(x: &isize) -> Box (isize) + '_> { + | ~~ +help: alternatively, add an explicit `'static` bound to this reference + | +LL | fn static_proc(x: &'static isize) -> Box (isize) + 'static> { + | ~~~~~~~~~~~~~~ error: aborting due to previous error diff --git a/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.nll.stderr b/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.nll.stderr index 3ed3827b97da3..6c65e4f017542 100644 --- a/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.nll.stderr +++ b/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.nll.stderr @@ -9,10 +9,14 @@ LL | | remaining: self.0.iter(), LL | | } | |_________^ returning this value requires that `'1` must outlive `'static` | -help: to allow this `impl Trait` to capture borrowed data with lifetime `'1`, add `'_` as a bound +help: to declare that the `impl Trait` captures data from argument `self`, you can add an explicit `'_` lifetime bound | LL | fn iter(&self) -> impl Iterator> + '_ { | ++++ +help: to declare that the trait object captures data from argument `self`, you can add an explicit `'_` lifetime bound + | +LL | fn iter(&self) -> impl Iterator> { + | ++++ error: lifetime may not live long enough --> $DIR/trait-object-nested-in-impl-trait.rs:39:9 @@ -24,6 +28,11 @@ LL | | current: None, LL | | remaining: self.0.iter(), LL | | } | |_________^ returning this value requires that `'1` must outlive `'static` + | +help: to declare that the trait object captures data from argument `self`, you can add an explicit `'_` lifetime bound + | +LL | fn iter(&self) -> impl Iterator> + '_ { + | ++++ error: lifetime may not live long enough --> $DIR/trait-object-nested-in-impl-trait.rs:50:9 @@ -35,6 +44,11 @@ LL | | current: None, LL | | remaining: self.0.iter(), LL | | } | |_________^ returning this value requires that `'a` must outlive `'static` + | +help: to declare that the trait object captures data from argument `self`, you can add an explicit `'a` lifetime bound + | +LL | fn iter<'a>(&'a self) -> impl Iterator> + 'a { + | ++++ error: lifetime may not live long enough --> $DIR/trait-object-nested-in-impl-trait.rs:61:9 @@ -47,10 +61,14 @@ LL | | remaining: self.0.iter(), LL | | } | |_________^ returning this value requires that `'a` must outlive `'static` | -help: to allow this `impl Trait` to capture borrowed data with lifetime `'a`, add `'a` as a bound +help: to declare that the `impl Trait` captures data from argument `self`, you can add an explicit `'a` lifetime bound | LL | fn iter<'a>(&'a self) -> impl Iterator> + 'a { | ++++ +help: to declare that the trait object captures data from argument `self`, you can add an explicit `'a` lifetime bound + | +LL | fn iter<'a>(&'a self) -> impl Iterator> { + | ++++ error: aborting due to 4 previous errors diff --git a/src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr b/src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr index 53d45f6a8f22b..0ffb77cf02164 100644 --- a/src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr +++ b/src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr @@ -6,6 +6,11 @@ LL | fn a(items: &[T]) -> Box> { LL | // ^^^^^^^^^^^^^^^^^^^^^ bound *here* defaults to `'static` LL | Box::new(items.iter()) | ^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static` + | +help: to declare that the trait object captures data from argument `items`, you can add an explicit `'_` lifetime bound + | +LL | fn a(items: &[T]) -> Box + '_> { + | ++++ error: aborting due to previous error From 8a28aa48d20ac4fd64d47815b84f1daf0c62f460 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 24 Apr 2022 23:32:59 -0700 Subject: [PATCH 06/11] Fix issue 96381 --- compiler/rustc_typeck/src/astconv/mod.rs | 8 ++++---- src/test/rustdoc/issue-96381.rs | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 src/test/rustdoc/issue-96381.rs diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 1cd0ace8adb05..1caf93e5fe055 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -2681,21 +2681,21 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let trait_ref = self.instantiate_mono_trait_ref(i.of_trait.as_ref()?, self.ast_ty_to_ty(i.self_ty)); - let x: &ty::AssocItem = tcx.associated_items(trait_ref.def_id).find_by_name_and_kind( + let assoc = tcx.associated_items(trait_ref.def_id).find_by_name_and_kind( tcx, *ident, ty::AssocKind::Fn, trait_ref.def_id, )?; - let fn_sig = tcx.fn_sig(x.def_id).subst( + let fn_sig = tcx.fn_sig(assoc.def_id).subst( tcx, - trait_ref.substs.extend_to(tcx, x.def_id, |param, _| tcx.mk_param_from_def(param)), + trait_ref.substs.extend_to(tcx, assoc.def_id, |param, _| tcx.mk_param_from_def(param)), ); let ty = if let Some(arg_idx) = arg_idx { fn_sig.input(arg_idx) } else { fn_sig.output() }; - Some(tcx.erase_late_bound_regions(ty)) + Some(tcx.liberate_late_bound_regions(fn_hir_id.expect_owner().to_def_id(), ty)) } fn validate_late_bound_regions( diff --git a/src/test/rustdoc/issue-96381.rs b/src/test/rustdoc/issue-96381.rs new file mode 100644 index 0000000000000..f0f123f85a03f --- /dev/null +++ b/src/test/rustdoc/issue-96381.rs @@ -0,0 +1,16 @@ +// should-fail + +#![allow(unused)] + +trait Foo: Sized { + fn bar(i: i32, t: T, s: &Self) -> (T, i32); +} + +impl Foo for () { + fn bar(i: _, t: _, s: _) -> _ { + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions + (1, 2) + } +} + +fn main() {} From a5700cfb500ef98bce4f385729456f3a2c0e7e2c Mon Sep 17 00:00:00 2001 From: est31 Date: Sun, 17 Apr 2022 01:55:32 +0200 Subject: [PATCH 07/11] Add unused_macro_rules lint definition Not fired yet. --- compiler/rustc_lint/src/lib.rs | 1 + compiler/rustc_lint_defs/src/builtin.rs | 30 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 18f229564c2a3..1886debde9b4d 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -303,6 +303,7 @@ fn register_builtins(store: &mut LintStore, no_interleave_lints: bool) { PATH_STATEMENTS, UNUSED_ATTRIBUTES, UNUSED_MACROS, + UNUSED_MACRO_RULES, UNUSED_ALLOCATION, UNUSED_DOC_COMMENTS, UNUSED_EXTERN_CRATES, diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index a42e3d5d95785..a1f8a710f8896 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -775,6 +775,35 @@ declare_lint! { "detects macros that were not used" } +declare_lint! { + /// The `unused_macro_rules` lint detects macro rules that were not used. + /// + /// ### Example + /// + /// ```rust + /// macro_rules! unused_empty { + /// (hello) => { println!("Hello, world!") }; + /// () => { println!("empty") }; + /// } + /// + /// fn main() { + /// unused_empty!(hello); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Unused macro rules may signal a mistake or unfinished code. Furthermore, + /// they slow down compilation. Right now, silencing the warning is not + /// supported on a single rule level, so you have to add an allow to the + /// entire macro definition. + pub UNUSED_MACRO_RULES, + Warn, + "detects macro rules that were not used" +} + declare_lint! { /// The `warnings` lint allows you to change the level of other /// lints which produce warnings. @@ -3138,6 +3167,7 @@ declare_lint_pass! { OVERLAPPING_RANGE_ENDPOINTS, BINDINGS_WITH_VARIANT_NAME, UNUSED_MACROS, + UNUSED_MACRO_RULES, WARNINGS, UNUSED_FEATURES, STABLE_FEATURES, From be08bedbee8949d6bfd3688271e1565f63cebc91 Mon Sep 17 00:00:00 2001 From: est31 Date: Sun, 17 Apr 2022 04:01:17 +0200 Subject: [PATCH 08/11] Implement the unused_macro_rules lint --- compiler/rustc_expand/src/base.rs | 8 ++++ compiler/rustc_expand/src/mbe/macro_rules.rs | 46 +++++++++++++------ compiler/rustc_metadata/src/rmeta/decoder.rs | 1 + compiler/rustc_resolve/Cargo.toml | 1 - .../rustc_resolve/src/build_reduced_graph.rs | 16 +++++-- compiler/rustc_resolve/src/lib.rs | 2 + compiler/rustc_resolve/src/macros.rs | 42 ++++++++++++++++- compiler/rustc_resolve/src/macros/tests.rs | 40 ++++++++++++++++ 8 files changed, 136 insertions(+), 20 deletions(-) create mode 100644 compiler/rustc_resolve/src/macros/tests.rs diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 3799623563f83..e4f9228e1a3ea 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -711,6 +711,9 @@ pub struct SyntaxExtension { /// Built-in macros have a couple of special properties like availability /// in `#[no_implicit_prelude]` modules, so we have to keep this flag. pub builtin_name: Option, + /// Rule spans. Used for linting. If the macro is not made up of rules, + /// it's empty. + pub rule_spans: Vec, } impl SyntaxExtension { @@ -740,6 +743,7 @@ impl SyntaxExtension { edition, builtin_name: None, kind, + rule_spans: Vec::new(), } } @@ -753,6 +757,7 @@ impl SyntaxExtension { edition: Edition, name: Symbol, attrs: &[ast::Attribute], + rule_spans: Vec, ) -> SyntaxExtension { let allow_internal_unstable = attr::allow_internal_unstable(sess, &attrs).collect::>(); @@ -800,6 +805,7 @@ impl SyntaxExtension { helper_attrs, edition, builtin_name, + rule_spans, } } @@ -887,6 +893,8 @@ pub trait ResolverExpand { force: bool, ) -> Result, Indeterminate>; + fn record_macro_rule_usage(&mut self, mac_id: NodeId, rule_index: usize); + fn check_unused_macros(&mut self); // Resolver interfaces for specific built-in macros. diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 2cfd6968accf7..35b5f2dec6f35 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -156,6 +156,7 @@ impl<'a> ParserAnyMacro<'a> { } struct MacroRulesMacroExpander { + id: NodeId, name: Ident, span: Span, transparency: Transparency, @@ -179,6 +180,7 @@ impl TTMacroExpander for MacroRulesMacroExpander { cx, sp, self.span, + self.id, self.name, self.transparency, input, @@ -207,6 +209,7 @@ fn generic_extension<'cx, 'tt>( cx: &'cx mut ExtCtxt<'_>, sp: Span, def_span: Span, + id: NodeId, name: Ident, transparency: Transparency, arg: TokenStream, @@ -297,6 +300,8 @@ fn generic_extension<'cx, 'tt>( let mut p = Parser::new(sess, tts, false, None); p.last_type_ascription = cx.current_expansion.prior_type_ascription; + cx.resolver.record_macro_rule_usage(id, i); + // Let the context choose how to interpret the result. // Weird, but useful for X-macros. return Box::new(ParserAnyMacro { @@ -375,7 +380,7 @@ pub fn compile_declarative_macro( edition: Edition, ) -> SyntaxExtension { debug!("compile_declarative_macro: {:?}", def); - let mk_syn_ext = |expander| { + let mk_syn_ext = |expander, rule_spans| { SyntaxExtension::new( sess, SyntaxExtensionKind::LegacyBang(expander), @@ -384,8 +389,10 @@ pub fn compile_declarative_macro( edition, def.ident.name, &def.attrs, + rule_spans, ) }; + let dummy_syn_ext = || mk_syn_ext(Box::new(macro_rules_dummy_expander), Vec::new()); let diag = &sess.parse_sess.span_diagnostic; let lhs_nm = Ident::new(sym::lhs, def.span); @@ -446,17 +453,17 @@ pub fn compile_declarative_macro( let s = parse_failure_msg(&token); let sp = token.span.substitute_dummy(def.span); sess.parse_sess.span_diagnostic.struct_span_err(sp, &s).span_label(sp, msg).emit(); - return mk_syn_ext(Box::new(macro_rules_dummy_expander)); + return dummy_syn_ext(); } Error(sp, msg) => { sess.parse_sess .span_diagnostic .struct_span_err(sp.substitute_dummy(def.span), &msg) .emit(); - return mk_syn_ext(Box::new(macro_rules_dummy_expander)); + return dummy_syn_ext(); } ErrorReported => { - return mk_syn_ext(Box::new(macro_rules_dummy_expander)); + return dummy_syn_ext(); } }; @@ -531,6 +538,11 @@ pub fn compile_declarative_macro( None => {} } + // Compute the spans of the macro rules + // We only take the span of the lhs here, + // so that the spans of created warnings are smaller. + let rule_spans = lhses.iter().map(|lhs| lhs.span()).collect::>(); + // Convert the lhses into `MatcherLoc` form, which is better for doing the // actual matching. Unless the matcher is invalid. let lhses = if valid { @@ -550,17 +562,21 @@ pub fn compile_declarative_macro( vec![] }; - mk_syn_ext(Box::new(MacroRulesMacroExpander { - name: def.ident, - span: def.span, - transparency, - lhses, - rhses, - valid, - // Macros defined in the current crate have a real node id, - // whereas macros from an external crate have a dummy id. - is_local: def.id != DUMMY_NODE_ID, - })) + mk_syn_ext( + Box::new(MacroRulesMacroExpander { + name: def.ident, + span: def.span, + id: def.id, + transparency, + lhses, + rhses, + valid, + // Macros defined in the current crate have a real node id, + // whereas macros from an external crate have a dummy id. + is_local: def.id != DUMMY_NODE_ID, + }), + rule_spans, + ) } fn check_lhs_nt_follows(sess: &ParseSess, def: &ast::Item, lhs: &mbe::TokenTree) -> bool { diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs index 1edb62e189f97..8765880e2a27c 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder.rs @@ -932,6 +932,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> { self.root.edition, Symbol::intern(name), &attrs, + Vec::new(), ) } diff --git a/compiler/rustc_resolve/Cargo.toml b/compiler/rustc_resolve/Cargo.toml index bd27c16c732a9..b2178ff59954b 100644 --- a/compiler/rustc_resolve/Cargo.toml +++ b/compiler/rustc_resolve/Cargo.toml @@ -4,7 +4,6 @@ version = "0.0.0" edition = "2021" [lib] -test = false doctest = false [dependencies] diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index 291b6645d9aa6..8c242524ba773 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -1218,9 +1218,18 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { // Mark the given macro as unused unless its name starts with `_`. // Macro uses will remove items from this set, and the remaining // items will be reported as `unused_macros`. - fn insert_unused_macro(&mut self, ident: Ident, def_id: LocalDefId, node_id: NodeId) { + fn insert_unused_macro( + &mut self, + ident: Ident, + def_id: LocalDefId, + node_id: NodeId, + rule_count: usize, + ) { if !ident.as_str().starts_with('_') { self.r.unused_macros.insert(def_id, (node_id, ident)); + for rule_i in 0..rule_count { + self.r.unused_macro_rules.insert((def_id, rule_i), (node_id, ident)); + } } } @@ -1244,6 +1253,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { }; let res = Res::Def(DefKind::Macro(ext.macro_kind()), def_id.to_def_id()); + let rule_count = ext.rule_spans.len(); self.r.macro_map.insert(def_id.to_def_id(), ext); self.r.local_macro_def_scopes.insert(def_id, parent_scope.module); @@ -1264,7 +1274,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { self.r.define(module, ident, MacroNS, (res, vis, span, expansion, IsMacroExport)); } else { self.r.check_reserved_macro_name(ident, res); - self.insert_unused_macro(ident, def_id, item.id); + self.insert_unused_macro(ident, def_id, item.id, rule_count); } self.r.visibilities.insert(def_id, vis); self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Binding( @@ -1285,7 +1295,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { _ => self.resolve_visibility(&item.vis), }; if vis != ty::Visibility::Public { - self.insert_unused_macro(ident, def_id, item.id); + self.insert_unused_macro(ident, def_id, item.id, rule_count); } self.r.define(module, ident, MacroNS, (res, vis, span, expansion)); self.r.visibilities.insert(def_id, vis); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index cca1f10258668..54db3c52be883 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -969,6 +969,7 @@ pub struct Resolver<'a> { local_macro_def_scopes: FxHashMap>, ast_transform_scopes: FxHashMap>, unused_macros: FxHashMap, + unused_macro_rules: FxHashMap<(LocalDefId, usize), (NodeId, Ident)>, proc_macro_stubs: FxHashSet, /// Traces collected during macro resolution and validated when it's complete. single_segment_macro_resolutions: @@ -1354,6 +1355,7 @@ impl<'a> Resolver<'a> { potentially_unused_imports: Vec::new(), struct_constructors: Default::default(), unused_macros: Default::default(), + unused_macro_rules: Default::default(), proc_macro_stubs: Default::default(), single_segment_macro_resolutions: Default::default(), multi_segment_macro_resolutions: Default::default(), diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 01f0b11f1ac3b..3457a263e216c 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -22,7 +22,9 @@ use rustc_hir::def::{self, DefKind, NonMacroAttrKind}; use rustc_hir::def_id::{CrateNum, LocalDefId}; use rustc_middle::middle::stability; use rustc_middle::ty::RegisteredTools; -use rustc_session::lint::builtin::{LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE, UNUSED_MACROS}; +use rustc_session::lint::builtin::{ + LEGACY_DERIVE_HELPERS, SOFT_UNSTABLE, UNUSED_MACROS, UNUSED_MACRO_RULES, +}; use rustc_session::lint::BuiltinLintDiagnostics; use rustc_session::parse::feature_err; use rustc_session::Session; @@ -36,6 +38,9 @@ use std::mem; type Res = def::Res; +#[cfg(test)] +mod tests; + /// Binding produced by a `macro_rules` item. /// Not modularized, can shadow previous `macro_rules` bindings, etc. #[derive(Debug)] @@ -311,6 +316,12 @@ impl<'a> ResolverExpand for Resolver<'a> { Ok(ext) } + fn record_macro_rule_usage(&mut self, id: NodeId, rule_i: usize) { + if let Some(did) = self.opt_local_def_id(id) { + self.unused_macro_rules.remove(&(did, rule_i)); + } + } + fn check_unused_macros(&mut self) { for (_, &(node_id, ident)) in self.unused_macros.iter() { self.lint_buffer.buffer_lint( @@ -320,6 +331,24 @@ impl<'a> ResolverExpand for Resolver<'a> { &format!("unused macro definition: `{}`", ident.as_str()), ); } + for (&(def_id, arm_i), &(node_id, ident)) in self.unused_macro_rules.iter() { + if self.unused_macros.contains_key(&def_id) { + // We already lint the entire macro as unused + continue; + } + let ext = &self.macro_map[&def_id.to_def_id()]; + let rule_span = ext.rule_spans[arm_i]; + self.lint_buffer.buffer_lint( + UNUSED_MACRO_RULES, + node_id, + rule_span, + &format!( + "{} rule of macro `{}` is never used", + ordinalize(arm_i + 1), + ident.as_str() + ), + ); + } } fn has_derive_copy(&self, expn_id: LocalExpnId) -> bool { @@ -877,3 +906,14 @@ impl<'a> Resolver<'a> { result } } + +/// Convert the given number into the corresponding ordinal +fn ordinalize(v: usize) -> String { + let suffix = match ((11..=13).contains(&(v % 100)), v % 10) { + (false, 1) => "st", + (false, 2) => "nd", + (false, 3) => "rd", + _ => "th", + }; + format!("{v}{suffix}") +} diff --git a/compiler/rustc_resolve/src/macros/tests.rs b/compiler/rustc_resolve/src/macros/tests.rs new file mode 100644 index 0000000000000..2aa6cc61e460a --- /dev/null +++ b/compiler/rustc_resolve/src/macros/tests.rs @@ -0,0 +1,40 @@ +use super::ordinalize; + +#[test] +fn test_ordinalize() { + assert_eq!(ordinalize(1), "1st"); + assert_eq!(ordinalize(2), "2nd"); + assert_eq!(ordinalize(3), "3rd"); + assert_eq!(ordinalize(4), "4th"); + assert_eq!(ordinalize(5), "5th"); + // ... + assert_eq!(ordinalize(10), "10th"); + assert_eq!(ordinalize(11), "11th"); + assert_eq!(ordinalize(12), "12th"); + assert_eq!(ordinalize(13), "13th"); + assert_eq!(ordinalize(14), "14th"); + // ... + assert_eq!(ordinalize(20), "20th"); + assert_eq!(ordinalize(21), "21st"); + assert_eq!(ordinalize(22), "22nd"); + assert_eq!(ordinalize(23), "23rd"); + assert_eq!(ordinalize(24), "24th"); + // ... + assert_eq!(ordinalize(30), "30th"); + assert_eq!(ordinalize(31), "31st"); + assert_eq!(ordinalize(32), "32nd"); + assert_eq!(ordinalize(33), "33rd"); + assert_eq!(ordinalize(34), "34th"); + // ... + assert_eq!(ordinalize(7010), "7010th"); + assert_eq!(ordinalize(7011), "7011th"); + assert_eq!(ordinalize(7012), "7012th"); + assert_eq!(ordinalize(7013), "7013th"); + assert_eq!(ordinalize(7014), "7014th"); + // ... + assert_eq!(ordinalize(7020), "7020th"); + assert_eq!(ordinalize(7021), "7021st"); + assert_eq!(ordinalize(7022), "7022nd"); + assert_eq!(ordinalize(7023), "7023rd"); + assert_eq!(ordinalize(7024), "7024th"); +} From 090acba0cd6b4da1f3c7b57478e5a01d9f28c9d5 Mon Sep 17 00:00:00 2001 From: est31 Date: Sun, 17 Apr 2022 18:12:06 +0200 Subject: [PATCH 09/11] Allow unused rules in some places in the compiler, library and tools --- compiler/rustc_codegen_llvm/src/intrinsic.rs | 2 ++ compiler/rustc_middle/src/mir/visit.rs | 3 +++ library/alloc/src/macros.rs | 1 + src/tools/error_index_generator/main.rs | 4 ---- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index cf9cf1b70aaa7..4407297c943a8 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -816,6 +816,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( span: Span, ) -> Result<&'ll Value, ()> { // macros for error handling: + #[cfg_attr(not(bootstrap), allow(unused_macro_rules))] macro_rules! emit_error { ($msg: tt) => { emit_error!($msg, ) @@ -1144,6 +1145,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( span: Span, args: &[OperandRef<'tcx, &'ll Value>], ) -> Result<&'ll Value, ()> { + #[cfg_attr(not(bootstrap), allow(unused_macro_rules))] macro_rules! emit_error { ($msg: tt) => { emit_error!($msg, ) diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 45b1ad6df8226..e2d70dd0b9c41 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -258,6 +258,7 @@ macro_rules! make_mir_visitor { // for best performance, we want to use an iterator rather // than a for-loop, to avoid calling `body::Body::invalidate` for // each basic block. + #[cfg_attr(not(bootstrap), allow(unused_macro_rules))] macro_rules! basic_blocks { (mut) => (body.basic_blocks_mut().iter_enumerated_mut()); () => (body.basic_blocks().iter_enumerated()); @@ -279,6 +280,7 @@ macro_rules! make_mir_visitor { self.visit_local_decl(local, & $($mutability)? body.local_decls[local]); } + #[cfg_attr(not(bootstrap), allow(unused_macro_rules))] macro_rules! type_annotations { (mut) => (body.user_type_annotations.iter_enumerated_mut()); () => (body.user_type_annotations.iter_enumerated()); @@ -932,6 +934,7 @@ macro_rules! make_mir_visitor { body: &$($mutability)? Body<'tcx>, location: Location ) { + #[cfg_attr(not(bootstrap), allow(unused_macro_rules))] macro_rules! basic_blocks { (mut) => (body.basic_blocks_mut()); () => (body.basic_blocks()); diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs index 22c19243e7f53..093b02113c3af 100644 --- a/library/alloc/src/macros.rs +++ b/library/alloc/src/macros.rs @@ -56,6 +56,7 @@ macro_rules! vec { // `slice::into_vec` function which is only available with cfg(test) // NB see the slice::hack module in slice.rs for more information #[cfg(all(not(no_global_oom_handling), test))] +#[cfg_attr(not(bootstrap), allow(unused_macro_rules))] macro_rules! vec { () => ( $crate::vec::Vec::new() diff --git a/src/tools/error_index_generator/main.rs b/src/tools/error_index_generator/main.rs index 51ed1b5a61cbe..1ce02e48c05b6 100644 --- a/src/tools/error_index_generator/main.rs +++ b/src/tools/error_index_generator/main.rs @@ -280,10 +280,6 @@ fn main() { fn register_all() -> Vec<(&'static str, Option<&'static str>)> { let mut long_codes: Vec<(&'static str, Option<&'static str>)> = Vec::new(); macro_rules! register_diagnostics { - ($($ecode:ident: $message:expr,)*) => ( - register_diagnostics!{$($ecode:$message,)* ;} - ); - ($($ecode:ident: $message:expr,)* ; $($code:ident,)*) => ( $( {long_codes.extend([ From fa94f03dc64ea912a9ba5cf967a59beac8da2797 Mon Sep 17 00:00:00 2001 From: est31 Date: Sun, 17 Apr 2022 15:54:04 +0200 Subject: [PATCH 10/11] Add tests Also rename the test files for the unused_macros lint to avoid confusion. The test files now follow a <-maybe-decl>.rs scheme. --- .../ui/lint/unused/unused-macro-rules-decl.rs | 49 +++++++++++++++++ .../unused/unused-macro-rules-decl.stderr | 26 +++++++++ src/test/ui/lint/unused/unused-macro-rules.rs | 54 ++++++++++++------- .../ui/lint/unused/unused-macro-rules.stderr | 34 +++++------- ...{unused-macro.rs => unused-macros-decl.rs} | 2 + ...macro.stderr => unused-macros-decl.stderr} | 10 ++-- src/test/ui/lint/unused/unused-macros.rs | 31 +++++++++++ src/test/ui/lint/unused/unused-macros.stderr | 32 +++++++++++ 8 files changed, 195 insertions(+), 43 deletions(-) create mode 100644 src/test/ui/lint/unused/unused-macro-rules-decl.rs create mode 100644 src/test/ui/lint/unused/unused-macro-rules-decl.stderr rename src/test/ui/lint/unused/{unused-macro.rs => unused-macros-decl.rs} (87%) rename src/test/ui/lint/unused/{unused-macro.stderr => unused-macros-decl.stderr} (74%) create mode 100644 src/test/ui/lint/unused/unused-macros.rs create mode 100644 src/test/ui/lint/unused/unused-macros.stderr diff --git a/src/test/ui/lint/unused/unused-macro-rules-decl.rs b/src/test/ui/lint/unused/unused-macro-rules-decl.rs new file mode 100644 index 0000000000000..537c84940fd01 --- /dev/null +++ b/src/test/ui/lint/unused/unused-macro-rules-decl.rs @@ -0,0 +1,49 @@ +#![feature(decl_macro)] +#![deny(unused_macro_rules)] +// To make sure we are not hitting this +#![deny(unused_macros)] + +// Most simple case +macro num { + (one) => { 1 }, + (two) => { 2 }, //~ ERROR: 2nd rule of macro + (three) => { 3 }, + (four) => { 4 }, //~ ERROR: 4th rule of macro +} +const _NUM: u8 = num!(one) + num!(three); + +// Check that allowing the lint works +#[allow(unused_macro_rules)] +macro num_allowed { + (one) => { 1 }, + (two) => { 2 }, + (three) => { 3 }, + (four) => { 4 }, +} +const _NUM_ALLOWED: u8 = num_allowed!(one) + num_allowed!(three); + +// Check that macro calls inside the macro trigger as usage +macro num_rec { + (one) => { 1 }, + (two) => { + num_rec!(one) + num_rec!(one) + }, + (three) => { //~ ERROR: 3rd rule of macro + num_rec!(one) + num_rec!(two) + }, + (four) => { + num_rec!(two) + num_rec!(two) + }, +} +const _NUM_RECURSIVE: u8 = num_rec!(four); + +// No error if the macro is public +pub macro num_public { + (one) => { 1 }, + (two) => { 2 }, + (three) => { 3 }, + (four) => { 4 }, +} +const _NUM_PUBLIC: u8 = num_public!(one) + num_public!(three); + +fn main() {} diff --git a/src/test/ui/lint/unused/unused-macro-rules-decl.stderr b/src/test/ui/lint/unused/unused-macro-rules-decl.stderr new file mode 100644 index 0000000000000..4d9b22feda2a2 --- /dev/null +++ b/src/test/ui/lint/unused/unused-macro-rules-decl.stderr @@ -0,0 +1,26 @@ +error: 4th rule of macro `num` is never used + --> $DIR/unused-macro-rules-decl.rs:11:5 + | +LL | (four) => { 4 }, + | ^^^^^^ + | +note: the lint level is defined here + --> $DIR/unused-macro-rules-decl.rs:2:9 + | +LL | #![deny(unused_macro_rules)] + | ^^^^^^^^^^^^^^^^^^ + +error: 2nd rule of macro `num` is never used + --> $DIR/unused-macro-rules-decl.rs:9:5 + | +LL | (two) => { 2 }, + | ^^^^^ + +error: 3rd rule of macro `num_rec` is never used + --> $DIR/unused-macro-rules-decl.rs:31:5 + | +LL | (three) => { + | ^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/lint/unused/unused-macro-rules.rs b/src/test/ui/lint/unused/unused-macro-rules.rs index 1a714b8f0a0d0..eeaf4d1b0a9ce 100644 --- a/src/test/ui/lint/unused/unused-macro-rules.rs +++ b/src/test/ui/lint/unused/unused-macro-rules.rs @@ -1,29 +1,47 @@ +#![deny(unused_macro_rules)] +// To make sure we are not hitting this #![deny(unused_macros)] // Most simple case -macro_rules! unused { //~ ERROR: unused macro definition - () => {}; +macro_rules! num { + (one) => { 1 }; + (two) => { 2 }; //~ ERROR: 2nd rule of macro + (three) => { 3 }; + (four) => { 4 }; //~ ERROR: 4th rule of macro } +const _NUM: u8 = num!(one) + num!(three); -// Test macros created by macros -macro_rules! create_macro { - () => { - macro_rules! m { //~ ERROR: unused macro definition - () => {}; - } - }; +// Check that allowing the lint works +#[allow(unused_macro_rules)] +macro_rules! num_allowed { + (one) => { 1 }; + (two) => { 2 }; + (three) => { 3 }; + (four) => { 4 }; } -create_macro!(); +const _NUM_ALLOWED: u8 = num_allowed!(one) + num_allowed!(three); -#[allow(unused_macros)] -mod bar { - // Test that putting the #[deny] close to the macro's definition - // works. +// Check that macro calls inside the macro trigger as usage +macro_rules! num_rec { + (one) => { 1 }; + (two) => { + num_rec!(one) + num_rec!(one) + }; + (three) => { //~ ERROR: 3rd rule of macro + num_rec!(one) + num_rec!(two) + }; + (four) => { num_rec!(two) + num_rec!(two) }; +} +const _NUM_RECURSIVE: u8 = num_rec!(four); - #[deny(unused_macros)] - macro_rules! unused { //~ ERROR: unused macro definition - () => {}; - } +// No error if the macro is being exported +#[macro_export] +macro_rules! num_exported { + (one) => { 1 }; + (two) => { 2 }; + (three) => { 3 }; + (four) => { 4 }; } +const _NUM_EXPORTED: u8 = num_exported!(one) + num_exported!(three); fn main() {} diff --git a/src/test/ui/lint/unused/unused-macro-rules.stderr b/src/test/ui/lint/unused/unused-macro-rules.stderr index 59db35b411183..2b3098a5128d4 100644 --- a/src/test/ui/lint/unused/unused-macro-rules.stderr +++ b/src/test/ui/lint/unused/unused-macro-rules.stderr @@ -1,32 +1,26 @@ -error: unused macro definition: `unused` - --> $DIR/unused-macro-rules.rs:4:14 +error: 4th rule of macro `num` is never used + --> $DIR/unused-macro-rules.rs:10:5 | -LL | macro_rules! unused { - | ^^^^^^ +LL | (four) => { 4 }; + | ^^^^^^ | note: the lint level is defined here --> $DIR/unused-macro-rules.rs:1:9 | -LL | #![deny(unused_macros)] - | ^^^^^^^^^^^^^ +LL | #![deny(unused_macro_rules)] + | ^^^^^^^^^^^^^^^^^^ -error: unused macro definition: `m` - --> $DIR/unused-macro-rules.rs:11:22 +error: 2nd rule of macro `num` is never used + --> $DIR/unused-macro-rules.rs:8:5 | -LL | macro_rules! m { - | ^ +LL | (two) => { 2 }; + | ^^^^^ -error: unused macro definition: `unused` - --> $DIR/unused-macro-rules.rs:24:18 +error: 3rd rule of macro `num_rec` is never used + --> $DIR/unused-macro-rules.rs:30:5 | -LL | macro_rules! unused { - | ^^^^^^ - | -note: the lint level is defined here - --> $DIR/unused-macro-rules.rs:23:12 - | -LL | #[deny(unused_macros)] - | ^^^^^^^^^^^^^ +LL | (three) => { + | ^^^^^^^ error: aborting due to 3 previous errors diff --git a/src/test/ui/lint/unused/unused-macro.rs b/src/test/ui/lint/unused/unused-macros-decl.rs similarity index 87% rename from src/test/ui/lint/unused/unused-macro.rs rename to src/test/ui/lint/unused/unused-macros-decl.rs index 302b0727d77b0..21f6108b18ad5 100644 --- a/src/test/ui/lint/unused/unused-macro.rs +++ b/src/test/ui/lint/unused/unused-macros-decl.rs @@ -1,5 +1,7 @@ #![feature(decl_macro)] #![deny(unused_macros)] +// To make sure we are not hitting this +#![deny(unused_macro_rules)] // Most simple case macro unused { //~ ERROR: unused macro definition diff --git a/src/test/ui/lint/unused/unused-macro.stderr b/src/test/ui/lint/unused/unused-macros-decl.stderr similarity index 74% rename from src/test/ui/lint/unused/unused-macro.stderr rename to src/test/ui/lint/unused/unused-macros-decl.stderr index 1a73279ed6dbd..1f426b9d91a61 100644 --- a/src/test/ui/lint/unused/unused-macro.stderr +++ b/src/test/ui/lint/unused/unused-macros-decl.stderr @@ -1,29 +1,29 @@ error: unused macro definition: `unused` - --> $DIR/unused-macro.rs:5:7 + --> $DIR/unused-macros-decl.rs:7:7 | LL | macro unused { | ^^^^^^ | note: the lint level is defined here - --> $DIR/unused-macro.rs:2:9 + --> $DIR/unused-macros-decl.rs:2:9 | LL | #![deny(unused_macros)] | ^^^^^^^^^^^^^ error: unused macro definition: `unused` - --> $DIR/unused-macro.rs:15:11 + --> $DIR/unused-macros-decl.rs:17:11 | LL | macro unused { | ^^^^^^ | note: the lint level is defined here - --> $DIR/unused-macro.rs:14:12 + --> $DIR/unused-macros-decl.rs:16:12 | LL | #[deny(unused_macros)] | ^^^^^^^^^^^^^ error: unused macro definition: `unused` - --> $DIR/unused-macro.rs:21:22 + --> $DIR/unused-macros-decl.rs:23:22 | LL | pub(crate) macro unused { | ^^^^^^ diff --git a/src/test/ui/lint/unused/unused-macros.rs b/src/test/ui/lint/unused/unused-macros.rs new file mode 100644 index 0000000000000..70b50b2082b36 --- /dev/null +++ b/src/test/ui/lint/unused/unused-macros.rs @@ -0,0 +1,31 @@ +#![deny(unused_macros)] +// To make sure we are not hitting this +#![deny(unused_macro_rules)] + +// Most simple case +macro_rules! unused { //~ ERROR: unused macro definition + () => {}; +} + +// Test macros created by macros +macro_rules! create_macro { + () => { + macro_rules! m { //~ ERROR: unused macro definition + () => {}; + } + }; +} +create_macro!(); + +#[allow(unused_macros)] +mod bar { + // Test that putting the #[deny] close to the macro's definition + // works. + + #[deny(unused_macros)] + macro_rules! unused { //~ ERROR: unused macro definition + () => {}; + } +} + +fn main() {} diff --git a/src/test/ui/lint/unused/unused-macros.stderr b/src/test/ui/lint/unused/unused-macros.stderr new file mode 100644 index 0000000000000..d0baf5becec4c --- /dev/null +++ b/src/test/ui/lint/unused/unused-macros.stderr @@ -0,0 +1,32 @@ +error: unused macro definition: `unused` + --> $DIR/unused-macros.rs:6:14 + | +LL | macro_rules! unused { + | ^^^^^^ + | +note: the lint level is defined here + --> $DIR/unused-macros.rs:1:9 + | +LL | #![deny(unused_macros)] + | ^^^^^^^^^^^^^ + +error: unused macro definition: `m` + --> $DIR/unused-macros.rs:13:22 + | +LL | macro_rules! m { + | ^ + +error: unused macro definition: `unused` + --> $DIR/unused-macros.rs:26:18 + | +LL | macro_rules! unused { + | ^^^^^^ + | +note: the lint level is defined here + --> $DIR/unused-macros.rs:25:12 + | +LL | #[deny(unused_macros)] + | ^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + From c3476369deb3b424bdcfef8778b5999f8737ef4e Mon Sep 17 00:00:00 2001 From: est31 Date: Sun, 17 Apr 2022 17:00:10 +0200 Subject: [PATCH 11/11] Allow unused rules in the testsuite where the lint triggers --- src/test/ui/consts/const-float-bits-conv.rs | 1 + src/test/ui/macros/issue-41803.rs | 2 ++ src/test/ui/macros/issue-52169.rs | 1 + src/test/ui/macros/macro-first-set.rs | 1 + src/test/ui/macros/macro-literal.rs | 1 + src/test/ui/macros/macro-pub-matcher.rs | 2 +- src/test/ui/macros/stmt_expr_attr_macro_parse.rs | 2 ++ src/test/ui/macros/type-macros-hlist.rs | 2 ++ .../issues/issue-65846-rollback-gating-failing-matcher.rs | 1 + src/test/ui/rust-2018/async-ident.fixed | 2 +- src/test/ui/rust-2018/async-ident.rs | 2 +- 11 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/test/ui/consts/const-float-bits-conv.rs b/src/test/ui/consts/const-float-bits-conv.rs index 310db2174aafa..fd5e42ef17077 100644 --- a/src/test/ui/consts/const-float-bits-conv.rs +++ b/src/test/ui/consts/const-float-bits-conv.rs @@ -3,6 +3,7 @@ #![feature(const_float_bits_conv)] #![feature(const_float_classify)] +#![allow(unused_macro_rules)] // Don't promote const fn nop(x: T) -> T { x } diff --git a/src/test/ui/macros/issue-41803.rs b/src/test/ui/macros/issue-41803.rs index 19ab81d04d0eb..bccfdc611460e 100644 --- a/src/test/ui/macros/issue-41803.rs +++ b/src/test/ui/macros/issue-41803.rs @@ -1,4 +1,6 @@ // run-pass +#![allow(unused_macro_rules)] + /// A compile-time map from identifiers to arbitrary (heterogeneous) expressions macro_rules! ident_map { ( $name:ident = { $($key:ident => $e:expr,)* } ) => { diff --git a/src/test/ui/macros/issue-52169.rs b/src/test/ui/macros/issue-52169.rs index 60be97f0aee23..f178cd30cb499 100644 --- a/src/test/ui/macros/issue-52169.rs +++ b/src/test/ui/macros/issue-52169.rs @@ -1,5 +1,6 @@ // run-pass +#[allow(unused_macro_rules)] macro_rules! a { ($i:literal) => { "right" }; ($i:tt) => { "wrong" }; diff --git a/src/test/ui/macros/macro-first-set.rs b/src/test/ui/macros/macro-first-set.rs index f85376dabcb5d..eeb1ddd84ae58 100644 --- a/src/test/ui/macros/macro-first-set.rs +++ b/src/test/ui/macros/macro-first-set.rs @@ -1,4 +1,5 @@ // run-pass +#![allow(unused_macro_rules)] //{{{ issue 40569 ============================================================== diff --git a/src/test/ui/macros/macro-literal.rs b/src/test/ui/macros/macro-literal.rs index e08d0a67b4345..3c2e71f9c43f3 100644 --- a/src/test/ui/macros/macro-literal.rs +++ b/src/test/ui/macros/macro-literal.rs @@ -21,6 +21,7 @@ macro_rules! only_expr { }; } +#[allow(unused_macro_rules)] macro_rules! mtester_dbg { ($l:literal) => { &format!("macro caught literal: {:?}", $l) diff --git a/src/test/ui/macros/macro-pub-matcher.rs b/src/test/ui/macros/macro-pub-matcher.rs index c02e6794edbc0..174056d6cdfaf 100644 --- a/src/test/ui/macros/macro-pub-matcher.rs +++ b/src/test/ui/macros/macro-pub-matcher.rs @@ -1,5 +1,5 @@ // run-pass -#![allow(dead_code, unused_imports)] +#![allow(dead_code, unused_imports, unused_macro_rules)] #![feature(crate_visibility_modifier)] /** diff --git a/src/test/ui/macros/stmt_expr_attr_macro_parse.rs b/src/test/ui/macros/stmt_expr_attr_macro_parse.rs index 3ab50db0ea1be..570191d2c90ae 100644 --- a/src/test/ui/macros/stmt_expr_attr_macro_parse.rs +++ b/src/test/ui/macros/stmt_expr_attr_macro_parse.rs @@ -1,4 +1,6 @@ // run-pass +#![allow(unused_macro_rules)] + macro_rules! m { ($e:expr) => { "expr includes attr" diff --git a/src/test/ui/macros/type-macros-hlist.rs b/src/test/ui/macros/type-macros-hlist.rs index 77d866cea9ceb..946b5bd5d9334 100644 --- a/src/test/ui/macros/type-macros-hlist.rs +++ b/src/test/ui/macros/type-macros-hlist.rs @@ -1,4 +1,6 @@ // run-pass +#![allow(unused_macro_rules)] + use std::ops::*; #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] diff --git a/src/test/ui/parser/issues/issue-65846-rollback-gating-failing-matcher.rs b/src/test/ui/parser/issues/issue-65846-rollback-gating-failing-matcher.rs index 9d68a7bffdeee..76c07bbfd8106 100644 --- a/src/test/ui/parser/issues/issue-65846-rollback-gating-failing-matcher.rs +++ b/src/test/ui/parser/issues/issue-65846-rollback-gating-failing-matcher.rs @@ -3,6 +3,7 @@ // Test that failing macro matchers will not cause pre-expansion errors // even though they use a feature that is pre-expansion gated. +#[allow(unused_macro_rules)] macro_rules! m { ($e:expr) => { 0 }; // This fails on the input below due to `, foo`. ($e:expr,) => { 1 }; // This also fails to match due to `foo`. diff --git a/src/test/ui/rust-2018/async-ident.fixed b/src/test/ui/rust-2018/async-ident.fixed index f4ae518c71d27..e909c79070ca7 100644 --- a/src/test/ui/rust-2018/async-ident.fixed +++ b/src/test/ui/rust-2018/async-ident.fixed @@ -1,4 +1,4 @@ -#![allow(dead_code, unused_variables, non_camel_case_types, non_upper_case_globals)] +#![allow(dead_code, unused_variables, unused_macro_rules, bad_style)] #![deny(keyword_idents)] // edition:2015 diff --git a/src/test/ui/rust-2018/async-ident.rs b/src/test/ui/rust-2018/async-ident.rs index 79c73dafac7a3..2bfbc3871d128 100644 --- a/src/test/ui/rust-2018/async-ident.rs +++ b/src/test/ui/rust-2018/async-ident.rs @@ -1,4 +1,4 @@ -#![allow(dead_code, unused_variables, non_camel_case_types, non_upper_case_globals)] +#![allow(dead_code, unused_variables, unused_macro_rules, bad_style)] #![deny(keyword_idents)] // edition:2015