From 73a5c1f944b4da797fb494850ea3b3a0ee955856 Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Thu, 13 May 2021 23:09:24 -0700 Subject: [PATCH 01/17] Toggle-wrap items differently than top-doc. This makes sure things like trait methods get wrapped at the `

` level rather than at the `.docblock` level. Also it ensures that only the actual top documentation gets the `.top-doc` class. --- src/librustdoc/html/render/mod.rs | 6 +++++- src/librustdoc/html/render/print_item.rs | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 5b54b32e4ddea..6de4909e95330 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -509,7 +509,11 @@ fn document(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, parent: Option info!("Documenting {}", name); } document_item_info(w, cx, item, parent); - document_full_collapsible(w, item, cx); + if parent.is_none() { + document_full_collapsible(w, item, cx); + } else { + document_full(w, item, cx); + } } /// Render md_text as markdown. diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 20e82cf2caf38..67cb47d6fe668 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -578,12 +578,13 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra info!("Documenting {} on {:?}", name, t.name); let item_type = m.type_(); let id = cx.derive_id(format!("{}.{}", item_type, name)); + write!(w, "
"); write!(w, "

", id = id,); render_assoc_item(w, m, AssocItemLink::Anchor(Some(&id)), ItemType::Impl, cx); w.write_str(""); render_stability_since(w, m, t, cx.tcx()); write_srclink(cx, m, w); - w.write_str("

"); + w.write_str("

"); document(w, cx, m, Some(t)); } From 963bd3b643bae735cc46f8637e744163576244ea Mon Sep 17 00:00:00 2001 From: Ryan Lopopolo Date: Sat, 15 May 2021 10:37:05 -0700 Subject: [PATCH 02/17] Implement more Iterator methods on core::iter::Repeat `core::iter::Repeat` always returns the same element, which means we can do better than implementing most `Iterator` methods in terms of `Iterator::next`. Fixes #81292. --- library/core/src/iter/sources/repeat.rs | 35 +++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/library/core/src/iter/sources/repeat.rs b/library/core/src/iter/sources/repeat.rs index d1f2879235f1a..a9478041c69c4 100644 --- a/library/core/src/iter/sources/repeat.rs +++ b/library/core/src/iter/sources/repeat.rs @@ -72,10 +72,32 @@ impl Iterator for Repeat { fn next(&mut self) -> Option { Some(self.element.clone()) } + #[inline] fn size_hint(&self) -> (usize, Option) { (usize::MAX, None) } + + #[inline] + fn advance_by(&mut self, n: usize) -> Result<(), usize> { + // Advancing an infinite iterator of a single element is a no-op. + let _ = n; + Ok(()) + } + + #[inline] + fn nth(&mut self, n: usize) -> Option { + let _ = n; + Some(self.element.clone()) + } + + fn last(self) -> Option { + loop {} + } + + fn count(self) -> usize { + loop {} + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -84,6 +106,19 @@ impl DoubleEndedIterator for Repeat { fn next_back(&mut self) -> Option { Some(self.element.clone()) } + + #[inline] + fn advance_back_by(&mut self, n: usize) -> Result<(), usize> { + // Advancing an infinite iterator of a single element is a no-op. + let _ = n; + Ok(()) + } + + #[inline] + fn nth_back(&mut self, n: usize) -> Option { + let _ = n; + Some(self.element.clone()) + } } #[stable(feature = "fused", since = "1.26.0")] From 7217d767b2054ac98da4f1840934f35f8285890c Mon Sep 17 00:00:00 2001 From: Fabian Wolff Date: Sat, 15 May 2021 19:53:16 +0200 Subject: [PATCH 03/17] Report an error if a lang item has the wrong number of generic arguments --- compiler/rustc_passes/src/lang_items.rs | 88 +++++++++++++++++-- compiler/rustc_typeck/src/check/method/mod.rs | 4 +- compiler/rustc_typeck/src/check/op.rs | 18 ++++ compiler/rustc_typeck/src/check/place_op.rs | 32 +++++++ .../lang-item-missing-generator.rs | 0 .../lang-item-missing-generator.stderr | 0 .../ui/{ => lang-items}/lang-item-missing.rs | 0 .../{ => lang-items}/lang-item-missing.stderr | 0 .../wrong-number-generic-args-add.rs | 20 +++++ .../wrong-number-generic-args-add.stderr | 20 +++++ .../wrong-number-generic-args-index.rs | 19 ++++ .../wrong-number-generic-args-index.stderr | 18 ++++ 12 files changed, 212 insertions(+), 7 deletions(-) rename src/test/ui/{ => lang-items}/lang-item-missing-generator.rs (100%) rename src/test/ui/{ => lang-items}/lang-item-missing-generator.stderr (100%) rename src/test/ui/{ => lang-items}/lang-item-missing.rs (100%) rename src/test/ui/{ => lang-items}/lang-item-missing.stderr (100%) create mode 100644 src/test/ui/lang-items/wrong-number-generic-args-add.rs create mode 100644 src/test/ui/lang-items/wrong-number-generic-args-add.stderr create mode 100644 src/test/ui/lang-items/wrong-number-generic-args-index.rs create mode 100644 src/test/ui/lang-items/wrong-number-generic-args-index.stderr diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index 7e6bb9779f077..111bdd1a117ea 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -13,12 +13,13 @@ use crate::weak_lang_items; use rustc_middle::middle::cstore::ExternCrate; use rustc_middle::ty::TyCtxt; -use rustc_errors::struct_span_err; +use rustc_errors::{pluralize, struct_span_err}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::lang_items::{extract, ITEM_REFS}; use rustc_hir::{HirId, LangItem, LanguageItems, Target}; +use rustc_span::Span; use rustc_middle::ty::query::Providers; @@ -61,8 +62,7 @@ impl LanguageItemCollector<'tcx> { match ITEM_REFS.get(&value).cloned() { // Known lang item with attribute on correct target. Some((item_index, expected_target)) if actual_target == expected_target => { - let def_id = self.tcx.hir().local_def_id(hir_id); - self.collect_item(item_index, def_id.to_def_id()); + self.collect_item_extended(item_index, hir_id, span); } // Known lang item with attribute on incorrect target. Some((_, expected_target)) => { @@ -100,11 +100,12 @@ impl LanguageItemCollector<'tcx> { } fn collect_item(&mut self, item_index: usize, item_def_id: DefId) { + let lang_item = LangItem::from_u32(item_index as u32).unwrap(); + let name = lang_item.name(); + // Check for duplicates. if let Some(original_def_id) = self.items.items[item_index] { if original_def_id != item_def_id { - let lang_item = LangItem::from_u32(item_index as u32).unwrap(); - let name = lang_item.name(); let mut err = match self.tcx.hir().span_if_local(item_def_id) { Some(span) => struct_span_err!( self.tcx.sess, @@ -180,6 +181,83 @@ impl LanguageItemCollector<'tcx> { self.items.groups[group as usize].push(item_def_id); } } + + fn collect_item_extended(&mut self, item_index: usize, hir_id: HirId, span: Span) { + let item_def_id = self.tcx.hir().local_def_id(hir_id).to_def_id(); + let lang_item = LangItem::from_u32(item_index as u32).unwrap(); + let name = lang_item.name(); + + self.collect_item(item_index, item_def_id); + + // Now check whether the lang_item has the expected number of generic + // arguments. Binary and indexing operations have one (for the RHS/index), + // unary operations have no generic arguments. + + let expected_num = match lang_item { + LangItem::Add + | LangItem::Sub + | LangItem::Mul + | LangItem::Div + | LangItem::Rem + | LangItem::BitXor + | LangItem::BitAnd + | LangItem::BitOr + | LangItem::Shl + | LangItem::Shr + | LangItem::AddAssign + | LangItem::SubAssign + | LangItem::MulAssign + | LangItem::DivAssign + | LangItem::RemAssign + | LangItem::BitXorAssign + | LangItem::BitAndAssign + | LangItem::BitOrAssign + | LangItem::ShlAssign + | LangItem::ShrAssign + | LangItem::Index + | LangItem::IndexMut => Some(1), + + LangItem::Neg | LangItem::Not | LangItem::Deref | LangItem::DerefMut => Some(0), + + // FIXME: add more cases? + _ => None, + }; + + if let Some(expected_num) = expected_num { + let (actual_num, generics_span) = match self.tcx.hir().get(hir_id) { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Trait(_, _, generics, ..), + .. + }) => (generics.params.len(), generics.span), + _ => bug!("op/index/deref lang item target is not a trait: {:?}", lang_item), + }; + + if expected_num != actual_num { + // We are issuing E0718 "incorrect target" here, because while the + // item kind of the target is correct, the target is still wrong + // because of the wrong number of generic arguments. + struct_span_err!( + self.tcx.sess, + span, + E0718, + "`{}` language item must be applied to a trait with {} generic argument{}", + name, + expected_num, + pluralize!(expected_num) + ) + .span_label( + generics_span, + format!( + "this trait has {} generic argument{}, not {}", + actual_num, + pluralize!(actual_num), + expected_num + ), + ) + .emit(); + } + } + } } /// Traverses and collects all the lang items in all crates. diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs index 0b1129a631249..427102afee103 100644 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ b/compiler/rustc_typeck/src/check/method/mod.rs @@ -303,8 +303,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { opt_input_types: Option<&[Ty<'tcx>]>, ) -> Option>> { debug!( - "lookup_in_trait_adjusted(self_ty={:?}, m_name={}, trait_def_id={:?})", - self_ty, m_name, trait_def_id + "lookup_in_trait_adjusted(self_ty={:?}, m_name={}, trait_def_id={:?}, opt_input_types={:?})", + self_ty, m_name, trait_def_id, opt_input_types ); // Construct a trait-reference `self_ty : Trait` diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs index 567cb1a90d0d9..87cb2d6d70c57 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs @@ -795,6 +795,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs_ty, op, opname, trait_did ); + // Catches cases like #83893, where a lang item is declared with the + // wrong number of generic arguments. Should have yielded an error + // elsewhere by now, but we have to catch it here so that we do not + // index `other_tys` out of bounds (if the lang item has too many + // generic arguments, `other_tys` is too short). + if let Some(trait_did) = trait_did { + let generics = self.tcx.generics_of(trait_did); + let expected_num = match op { + // Binary ops have a generic right-hand side, unary ops don't + Op::Binary(..) => 1, + Op::Unary(..) => 0, + } + if generics.has_self { 1 } else { 0 }; + let num_generics = generics.count(); + if num_generics != expected_num { + return Err(()); + } + } + let method = trait_did.and_then(|trait_did| { let opname = Ident::with_dummy_span(opname); self.lookup_method_in_trait(span, opname, trait_did, lhs_ty, Some(other_tys)) diff --git a/compiler/rustc_typeck/src/check/place_op.rs b/compiler/rustc_typeck/src/check/place_op.rs index 5bd385107ca39..23677d04d7301 100644 --- a/compiler/rustc_typeck/src/check/place_op.rs +++ b/compiler/rustc_typeck/src/check/place_op.rs @@ -153,6 +153,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PlaceOp::Deref => (self.tcx.lang_items().deref_trait(), sym::deref), PlaceOp::Index => (self.tcx.lang_items().index_trait(), sym::index), }; + + // If the lang item was declared incorrectly, stop here so that we don't + // run into an ICE (#83893). The error is reported where the lang item is + // declared. + if let Some(trait_did) = imm_tr { + let generics = self.tcx.generics_of(trait_did); + let expected_num = match op { + PlaceOp::Deref => 0, + PlaceOp::Index => 1, + } + if generics.has_self { 1 } else { 0 }; + let num_generics = generics.count(); + if num_generics != expected_num { + return None; + } + } + imm_tr.and_then(|trait_did| { self.lookup_method_in_trait( span, @@ -177,6 +193,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PlaceOp::Deref => (self.tcx.lang_items().deref_mut_trait(), sym::deref_mut), PlaceOp::Index => (self.tcx.lang_items().index_mut_trait(), sym::index_mut), }; + + // If the lang item was declared incorrectly, stop here so that we don't + // run into an ICE (#83893). The error is reported where the lang item is + // declared. + if let Some(trait_did) = mut_tr { + let generics = self.tcx.generics_of(trait_did); + let expected_num = match op { + PlaceOp::Deref => 0, + PlaceOp::Index => 1, + } + if generics.has_self { 1 } else { 0 }; + let num_generics = generics.count(); + if num_generics != expected_num { + return None; + } + } + mut_tr.and_then(|trait_did| { self.lookup_method_in_trait( span, diff --git a/src/test/ui/lang-item-missing-generator.rs b/src/test/ui/lang-items/lang-item-missing-generator.rs similarity index 100% rename from src/test/ui/lang-item-missing-generator.rs rename to src/test/ui/lang-items/lang-item-missing-generator.rs diff --git a/src/test/ui/lang-item-missing-generator.stderr b/src/test/ui/lang-items/lang-item-missing-generator.stderr similarity index 100% rename from src/test/ui/lang-item-missing-generator.stderr rename to src/test/ui/lang-items/lang-item-missing-generator.stderr diff --git a/src/test/ui/lang-item-missing.rs b/src/test/ui/lang-items/lang-item-missing.rs similarity index 100% rename from src/test/ui/lang-item-missing.rs rename to src/test/ui/lang-items/lang-item-missing.rs diff --git a/src/test/ui/lang-item-missing.stderr b/src/test/ui/lang-items/lang-item-missing.stderr similarity index 100% rename from src/test/ui/lang-item-missing.stderr rename to src/test/ui/lang-items/lang-item-missing.stderr diff --git a/src/test/ui/lang-items/wrong-number-generic-args-add.rs b/src/test/ui/lang-items/wrong-number-generic-args-add.rs new file mode 100644 index 0000000000000..9f4f2464a1e65 --- /dev/null +++ b/src/test/ui/lang-items/wrong-number-generic-args-add.rs @@ -0,0 +1,20 @@ +// Checks whether declaring a lang item with the wrong number +// of generic arguments crashes the compiler (issue #83893). + +#![feature(lang_items,no_core)] +#![no_core] +#![crate_type="lib"] + +#[lang = "sized"] +trait MySized {} + +#[lang = "add"] +trait MyAdd<'a, T> {} +//~^^ ERROR: `add` language item must be applied to a trait with 1 generic argument [E0718] + +fn ice() { + let r = 5; + let a = 6; + r + a + //~^ ERROR: cannot add `{integer}` to `{integer}` [E0369] +} diff --git a/src/test/ui/lang-items/wrong-number-generic-args-add.stderr b/src/test/ui/lang-items/wrong-number-generic-args-add.stderr new file mode 100644 index 0000000000000..6f89441fd285d --- /dev/null +++ b/src/test/ui/lang-items/wrong-number-generic-args-add.stderr @@ -0,0 +1,20 @@ +error[E0718]: `add` language item must be applied to a trait with 1 generic argument + --> $DIR/wrong-number-generic-args-add.rs:11:1 + | +LL | #[lang = "add"] + | ^^^^^^^^^^^^^^^ +LL | trait MyAdd<'a, T> {} + | ------- this trait has 2 generic arguments, not 1 + +error[E0369]: cannot add `{integer}` to `{integer}` + --> $DIR/wrong-number-generic-args-add.rs:18:7 + | +LL | r + a + | - ^ - {integer} + | | + | {integer} + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0369, E0718. +For more information about an error, try `rustc --explain E0369`. diff --git a/src/test/ui/lang-items/wrong-number-generic-args-index.rs b/src/test/ui/lang-items/wrong-number-generic-args-index.rs new file mode 100644 index 0000000000000..1d90e63dc5470 --- /dev/null +++ b/src/test/ui/lang-items/wrong-number-generic-args-index.rs @@ -0,0 +1,19 @@ +// Checks whether declaring a lang item with the wrong number +// of generic arguments crashes the compiler (issue #83893). + +#![feature(lang_items,no_core)] +#![no_core] +#![crate_type="lib"] + +#[lang = "sized"] +trait MySized {} + +#[lang = "index"] +trait MyIndex<'a, T> {} +//~^^ ERROR: `index` language item must be applied to a trait with 1 generic argument [E0718] + +fn ice() { + let arr = [0; 5]; + let _ = arr[2]; + //~^ ERROR: cannot index into a value of type `[{integer}; 5]` [E0608] +} diff --git a/src/test/ui/lang-items/wrong-number-generic-args-index.stderr b/src/test/ui/lang-items/wrong-number-generic-args-index.stderr new file mode 100644 index 0000000000000..bc3f19ff27623 --- /dev/null +++ b/src/test/ui/lang-items/wrong-number-generic-args-index.stderr @@ -0,0 +1,18 @@ +error[E0718]: `index` language item must be applied to a trait with 1 generic argument + --> $DIR/wrong-number-generic-args-index.rs:11:1 + | +LL | #[lang = "index"] + | ^^^^^^^^^^^^^^^^^ +LL | trait MyIndex<'a, T> {} + | ------- this trait has 2 generic arguments, not 1 + +error[E0608]: cannot index into a value of type `[{integer}; 5]` + --> $DIR/wrong-number-generic-args-index.rs:17:13 + | +LL | let _ = arr[2]; + | ^^^^^^ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0608, E0718. +For more information about an error, try `rustc --explain E0608`. From 48d07d13266d321a2be15a18d335cf642dbd685c Mon Sep 17 00:00:00 2001 From: Fabian Wolff Date: Sun, 16 May 2021 16:20:35 +0200 Subject: [PATCH 04/17] Suggest borrowing if a trait implementation is found for &/&mut --- .../src/traits/error_reporting/suggestions.rs | 87 ++++++++++++++++--- .../imm-ref-trait-object-literal.stderr | 8 +- src/test/ui/suggestions/issue-84973-2.rs | 13 +++ src/test/ui/suggestions/issue-84973-2.stderr | 15 ++++ src/test/ui/suggestions/issue-84973.rs | 33 +++++++ src/test/ui/suggestions/issue-84973.stderr | 15 ++++ 6 files changed, 154 insertions(+), 17 deletions(-) create mode 100644 src/test/ui/suggestions/issue-84973-2.rs create mode 100644 src/test/ui/suggestions/issue-84973-2.stderr create mode 100644 src/test/ui/suggestions/issue-84973.rs create mode 100644 src/test/ui/suggestions/issue-84973.stderr diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 6a4d41ffc1ac9..d090120406126 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -686,17 +686,42 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { return false; } + // Blacklist traits for which it would be nonsensical to suggest borrowing. + // For instance, immutable references are always Copy, so suggesting to + // borrow would always succeed, but it's probably not what the user wanted. + let blacklist: Vec<_> = [ + LangItem::Copy, + LangItem::Clone, + LangItem::Pin, + LangItem::Unpin, + LangItem::Sized, + LangItem::Send, + ] + .iter() + .filter_map(|lang_item| self.tcx.lang_items().require(*lang_item).ok()) + .collect(); + let span = obligation.cause.span; let param_env = obligation.param_env; let trait_ref = trait_ref.skip_binder(); - if let ObligationCauseCode::ImplDerivedObligation(obligation) = &obligation.cause.code { - // Try to apply the original trait binding obligation by borrowing. - let self_ty = trait_ref.self_ty(); - let found = self_ty.to_string(); - let new_self_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, self_ty); - let substs = self.tcx.mk_substs_trait(new_self_ty, &[]); - let new_trait_ref = ty::TraitRef::new(obligation.parent_trait_ref.def_id(), substs); + let found_ty = trait_ref.self_ty(); + let found_ty_str = found_ty.to_string(); + let imm_borrowed_found_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, found_ty); + let imm_substs = self.tcx.mk_substs_trait(imm_borrowed_found_ty, &[]); + let mut_borrowed_found_ty = self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, found_ty); + let mut_substs = self.tcx.mk_substs_trait(mut_borrowed_found_ty, &[]); + + // Try to apply the original trait binding obligation by borrowing. + let mut try_borrowing = |new_trait_ref: ty::TraitRef<'tcx>, + expected_trait_ref: ty::TraitRef<'tcx>, + mtbl: bool, + blacklist: &[DefId]| + -> bool { + if blacklist.contains(&expected_trait_ref.def_id) { + return false; + } + let new_obligation = Obligation::new( ObligationCause::dummy(), param_env, @@ -713,8 +738,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let msg = format!( "the trait bound `{}: {}` is not satisfied", - found, - obligation.parent_trait_ref.skip_binder().print_only_trait_path(), + found_ty_str, + expected_trait_ref.print_only_trait_path(), ); if has_custom_message { err.note(&msg); @@ -730,7 +755,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { span, &format!( "expected an implementor of trait `{}`", - obligation.parent_trait_ref.skip_binder().print_only_trait_path(), + expected_trait_ref.print_only_trait_path(), ), ); @@ -745,16 +770,52 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err.span_suggestion( span, - "consider borrowing here", - format!("&{}", snippet), + &format!( + "consider borrowing{} here", + if mtbl { " mutably" } else { "" } + ), + format!("&{}{}", if mtbl { "mut " } else { "" }, snippet), Applicability::MaybeIncorrect, ); } return true; } } + return false; + }; + + if let ObligationCauseCode::ImplDerivedObligation(obligation) = &obligation.cause.code { + let expected_trait_ref = obligation.parent_trait_ref.skip_binder(); + let new_imm_trait_ref = + ty::TraitRef::new(obligation.parent_trait_ref.def_id(), imm_substs); + let new_mut_trait_ref = + ty::TraitRef::new(obligation.parent_trait_ref.def_id(), mut_substs); + if try_borrowing(new_imm_trait_ref, expected_trait_ref, false, &[]) { + return true; + } else { + return try_borrowing(new_mut_trait_ref, expected_trait_ref, true, &[]); + } + } else if let ObligationCauseCode::BindingObligation(_, _) + | ObligationCauseCode::ItemObligation(_) = &obligation.cause.code + { + if try_borrowing( + ty::TraitRef::new(trait_ref.def_id, imm_substs), + trait_ref, + false, + &blacklist[..], + ) { + return true; + } else { + return try_borrowing( + ty::TraitRef::new(trait_ref.def_id, mut_substs), + trait_ref, + true, + &blacklist[..], + ); + } + } else { + false } - false } /// Whenever references are used by mistake, like `for (i, e) in &vec.iter().enumerate()`, diff --git a/src/test/ui/suggestions/imm-ref-trait-object-literal.stderr b/src/test/ui/suggestions/imm-ref-trait-object-literal.stderr index df483b3912d69..3cf744105695e 100644 --- a/src/test/ui/suggestions/imm-ref-trait-object-literal.stderr +++ b/src/test/ui/suggestions/imm-ref-trait-object-literal.stderr @@ -21,10 +21,10 @@ LL | fn foo(_: X) {} | ----- required by this bound in `foo` ... LL | foo(s); - | ^ the trait `Trait` is not implemented for `S` - | - = help: the following implementations were found: - <&'a mut S as Trait> + | ^ + | | + | expected an implementor of trait `Trait` + | help: consider borrowing mutably here: `&mut s` error: aborting due to 2 previous errors diff --git a/src/test/ui/suggestions/issue-84973-2.rs b/src/test/ui/suggestions/issue-84973-2.rs new file mode 100644 index 0000000000000..050cf8c64b361 --- /dev/null +++ b/src/test/ui/suggestions/issue-84973-2.rs @@ -0,0 +1,13 @@ +// A slight variation of issue-84973.rs. Here, a mutable borrow is +// required (and the obligation kind is different). + +trait Tr {} +impl Tr for &mut i32 {} + +fn foo(i: T) {} + +fn main() { + let a: i32 = 32; + foo(a); + //~^ ERROR: the trait bound `i32: Tr` is not satisfied [E0277] +} diff --git a/src/test/ui/suggestions/issue-84973-2.stderr b/src/test/ui/suggestions/issue-84973-2.stderr new file mode 100644 index 0000000000000..6394e58a2a910 --- /dev/null +++ b/src/test/ui/suggestions/issue-84973-2.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `i32: Tr` is not satisfied + --> $DIR/issue-84973-2.rs:11:9 + | +LL | fn foo(i: T) {} + | -- required by this bound in `foo` +... +LL | foo(a); + | ^ + | | + | expected an implementor of trait `Tr` + | help: consider borrowing mutably here: `&mut a` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/suggestions/issue-84973.rs b/src/test/ui/suggestions/issue-84973.rs new file mode 100644 index 0000000000000..42468478ed9a2 --- /dev/null +++ b/src/test/ui/suggestions/issue-84973.rs @@ -0,0 +1,33 @@ +// Checks whether borrowing is suggested when a trait bound is not satisfied +// for found type `T`, but is for `&/&mut T`. + +fn main() { + let f = Fancy{}; + let o = Other::new(f); + //~^ ERROR: the trait bound `Fancy: SomeTrait` is not satisfied [E0277] +} + +struct Fancy {} + +impl <'a> SomeTrait for &'a Fancy { +} + +trait SomeTrait {} + +struct Other<'a, G> { + a: &'a str, + g: G, +} + +// Broadly copied from https://docs.rs/petgraph/0.5.1/src/petgraph/dot.rs.html#70 +impl<'a, G> Other<'a, G> +where + G: SomeTrait, +{ + pub fn new(g: G) -> Self { + Other { + a: "hi", + g: g, + } + } +} diff --git a/src/test/ui/suggestions/issue-84973.stderr b/src/test/ui/suggestions/issue-84973.stderr new file mode 100644 index 0000000000000..49fa94da85923 --- /dev/null +++ b/src/test/ui/suggestions/issue-84973.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `Fancy: SomeTrait` is not satisfied + --> $DIR/issue-84973.rs:6:24 + | +LL | let o = Other::new(f); + | ^ + | | + | expected an implementor of trait `SomeTrait` + | help: consider borrowing here: `&f` +... +LL | pub fn new(g: G) -> Self { + | ------------------------ required by `Other::<'a, G>::new` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. From 4efa4a5273293354526801d8e3a3d05d005b2479 Mon Sep 17 00:00:00 2001 From: Fabian Wolff Date: Sun, 16 May 2021 18:16:00 +0200 Subject: [PATCH 05/17] Implement changes suggested by varkor --- compiler/rustc_passes/src/lang_items.rs | 54 +++++++++++++++++++-- compiler/rustc_typeck/src/check/mod.rs | 14 ++++++ compiler/rustc_typeck/src/check/op.rs | 17 +++---- compiler/rustc_typeck/src/check/place_op.rs | 32 ++++++------ 4 files changed, 86 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index 111bdd1a117ea..9086e21579eda 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -182,6 +182,8 @@ impl LanguageItemCollector<'tcx> { } } + // Like collect_item() above, but also checks whether the lang item is declared + // with the right number of generic arguments if it is a trait. fn collect_item_extended(&mut self, item_index: usize, hir_id: HirId, span: Span) { let item_def_id = self.tcx.hir().local_def_id(hir_id).to_def_id(); let lang_item = LangItem::from_u32(item_index as u32).unwrap(); @@ -190,10 +192,15 @@ impl LanguageItemCollector<'tcx> { self.collect_item(item_index, item_def_id); // Now check whether the lang_item has the expected number of generic - // arguments. Binary and indexing operations have one (for the RHS/index), - // unary operations have no generic arguments. + // arguments if it is a trait. Generally speaking, binary and indexing + // operations have one (for the RHS/index), unary operations have none, + // and the rest also have none except for the closure traits (one for + // the argument list), generators (one for the resume argument), + // ordering/equality relations (one for the RHS), and various conversion + // traits. let expected_num = match lang_item { + // Binary operations LangItem::Add | LangItem::Sub | LangItem::Mul @@ -215,11 +222,48 @@ impl LanguageItemCollector<'tcx> { | LangItem::ShlAssign | LangItem::ShrAssign | LangItem::Index - | LangItem::IndexMut => Some(1), + | LangItem::IndexMut - LangItem::Neg | LangItem::Not | LangItem::Deref | LangItem::DerefMut => Some(0), + // Miscellaneous + | LangItem::Unsize + | LangItem::CoerceUnsized + | LangItem::DispatchFromDyn + | LangItem::Fn + | LangItem::FnMut + | LangItem::FnOnce + | LangItem::Generator + | LangItem::PartialEq + | LangItem::PartialOrd + => Some(1), - // FIXME: add more cases? + // Unary operations + LangItem::Neg + | LangItem::Not + + // Miscellaneous + | LangItem::Deref + | LangItem::DerefMut + | LangItem::Sized + | LangItem::StructuralPeq + | LangItem::StructuralTeq + | LangItem::Copy + | LangItem::Clone + | LangItem::Sync + | LangItem::DiscriminantKind + | LangItem::PointeeTrait + | LangItem::Freeze + | LangItem::Drop + | LangItem::Receiver + | LangItem::Future + | LangItem::Unpin + | LangItem::Termination + | LangItem::Try + | LangItem::Send + | LangItem::UnwindSafe + | LangItem::RefUnwindSafe + => Some(0), + + // Not a trait _ => None, }; diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index 994206bd41934..6f96bd544c06c 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -1190,3 +1190,17 @@ fn fatally_break_rust(sess: &Session) { fn potentially_plural_count(count: usize, word: &str) -> String { format!("{} {}{}", count, word, pluralize!(count)) } + +fn has_expected_num_generic_args<'tcx>( + tcx: TyCtxt<'tcx>, + trait_did: Option, + mut expected: usize, +) -> bool { + trait_did.map_or(true, |trait_did| { + let generics = tcx.generics_of(trait_did); + if generics.has_self { + expected += 1; + } + generics.count() == expected + }) +} diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs index 87cb2d6d70c57..963436d05d8ef 100644 --- a/compiler/rustc_typeck/src/check/op.rs +++ b/compiler/rustc_typeck/src/check/op.rs @@ -1,7 +1,7 @@ //! Code related to processing overloaded binary and unary operators. use super::method::MethodCallee; -use super::FnCtxt; +use super::{has_expected_num_generic_args, FnCtxt}; use rustc_ast as ast; use rustc_errors::{self, struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; @@ -800,17 +800,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // elsewhere by now, but we have to catch it here so that we do not // index `other_tys` out of bounds (if the lang item has too many // generic arguments, `other_tys` is too short). - if let Some(trait_did) = trait_did { - let generics = self.tcx.generics_of(trait_did); - let expected_num = match op { + if !has_expected_num_generic_args( + self.tcx, + trait_did, + match op { // Binary ops have a generic right-hand side, unary ops don't Op::Binary(..) => 1, Op::Unary(..) => 0, - } + if generics.has_self { 1 } else { 0 }; - let num_generics = generics.count(); - if num_generics != expected_num { - return Err(()); - } + }, + ) { + return Err(()); } let method = trait_did.and_then(|trait_did| { diff --git a/compiler/rustc_typeck/src/check/place_op.rs b/compiler/rustc_typeck/src/check/place_op.rs index 23677d04d7301..a63aec07ad1c0 100644 --- a/compiler/rustc_typeck/src/check/place_op.rs +++ b/compiler/rustc_typeck/src/check/place_op.rs @@ -1,5 +1,5 @@ use crate::check::method::MethodCallee; -use crate::check::{FnCtxt, PlaceOp}; +use crate::check::{has_expected_num_generic_args, FnCtxt, PlaceOp}; use rustc_hir as hir; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::InferOk; @@ -157,16 +157,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the lang item was declared incorrectly, stop here so that we don't // run into an ICE (#83893). The error is reported where the lang item is // declared. - if let Some(trait_did) = imm_tr { - let generics = self.tcx.generics_of(trait_did); - let expected_num = match op { + if !has_expected_num_generic_args( + self.tcx, + imm_tr, + match op { PlaceOp::Deref => 0, PlaceOp::Index => 1, - } + if generics.has_self { 1 } else { 0 }; - let num_generics = generics.count(); - if num_generics != expected_num { - return None; - } + }, + ) { + return None; } imm_tr.and_then(|trait_did| { @@ -197,16 +196,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the lang item was declared incorrectly, stop here so that we don't // run into an ICE (#83893). The error is reported where the lang item is // declared. - if let Some(trait_did) = mut_tr { - let generics = self.tcx.generics_of(trait_did); - let expected_num = match op { + if !has_expected_num_generic_args( + self.tcx, + mut_tr, + match op { PlaceOp::Deref => 0, PlaceOp::Index => 1, - } + if generics.has_self { 1 } else { 0 }; - let num_generics = generics.count(); - if num_generics != expected_num { - return None; - } + }, + ) { + return None; } mut_tr.and_then(|trait_did| { From fbc01e5332f0388a9a68e641a6adaaaa45ca2c64 Mon Sep 17 00:00:00 2001 From: Milan Landaverde Date: Tue, 11 May 2021 15:41:32 -0400 Subject: [PATCH 06/17] Add abstract namespace support for Unix domain sockets --- library/std/src/os/unix/net/addr.rs | 84 +++++++++++++++- library/std/src/os/unix/net/datagram.rs | 94 +++++++++++++++++- library/std/src/os/unix/net/listener.rs | 34 +++++++ library/std/src/os/unix/net/stream.rs | 31 ++++++ library/std/src/os/unix/net/tests.rs | 125 +++++++++++++++++++++++- 5 files changed, 363 insertions(+), 5 deletions(-) diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs index 459f3590e6470..bf1685d5a40bd 100644 --- a/library/std/src/os/unix/net/addr.rs +++ b/library/std/src/os/unix/net/addr.rs @@ -2,7 +2,7 @@ use crate::ffi::OsStr; use crate::os::unix::ffi::OsStrExt; use crate::path::Path; use crate::sys::cvt; -use crate::{ascii, fmt, io, iter, mem}; +use crate::{ascii, fmt, io, iter, mem, ptr}; // FIXME(#43348): Make libc adapt #[doc(cfg(...))] so we don't need these fake definitions here? #[cfg(not(unix))] @@ -92,8 +92,8 @@ impl<'a> fmt::Display for AsciiEscaped<'a> { #[derive(Clone)] #[stable(feature = "unix_socket", since = "1.10.0")] pub struct SocketAddr { - addr: libc::sockaddr_un, - len: libc::socklen_t, + pub(super) addr: libc::sockaddr_un, + pub(super) len: libc::socklen_t, } impl SocketAddr { @@ -196,6 +196,30 @@ impl SocketAddr { if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None } } + /// Returns the contents of this address if it is an abstract namespace + /// without the leading null byte. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(unix_socket_abstract)] + /// use std::os::unix::net::{UnixListener, SocketAddr}; + /// + /// fn main() -> std::io::Result<()> { + /// let namespace = b"hidden"; + /// let namespace_addr = SocketAddr::from_abstract_namespace(&namespace[..])?; + /// let socket = UnixListener::bind_addr(&namespace_addr)?; + /// let local_addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(local_addr.as_abstract_namespace(), Some(&namespace[..])); + /// Ok(()) + /// } + /// ``` + #[cfg(any(doc, target_os = "android", target_os = "linux",))] + #[unstable(feature = "unix_socket_abstract", issue = "42048")] + pub fn as_abstract_namespace(&self) -> Option<&[u8]> { + if let AddressKind::Abstract(name) = self.address() { Some(name) } else { None } + } + fn address(&self) -> AddressKind<'_> { let len = self.len as usize - sun_path_offset(&self.addr); let path = unsafe { mem::transmute::<&[libc::c_char], &[u8]>(&self.addr.sun_path) }; @@ -212,6 +236,60 @@ impl SocketAddr { AddressKind::Pathname(OsStr::from_bytes(&path[..len - 1]).as_ref()) } } + + /// Creates an abstract domain socket address from a namespace + /// + /// An abstract address does not create a file unlike traditional path-based + /// Unix sockets. The advantage of this is that the address will disappear when + /// the socket bound to it is closed, so no filesystem clean up is required. + /// + /// The leading null byte for the abstract namespace is automatically added. + /// + /// This is a Linux-specific extension. See more at [`unix(7)`]. + /// + /// [`unix(7)`]: https://man7.org/linux/man-pages/man7/unix.7.html + /// + /// # Errors + /// + /// This will return an error if the given namespace is too long + /// + /// # Examples + /// + /// ```no_run + /// #![feature(unix_socket_abstract)] + /// use std::os::unix::net::{UnixListener, SocketAddr}; + /// + /// fn main() -> std::io::Result<()> { + /// let addr = SocketAddr::from_abstract_namespace(b"hidden")?; + /// let listener = match UnixListener::bind_addr(&addr) { + /// Ok(sock) => sock, + /// Err(err) => { + /// println!("Couldn't bind: {:?}", err); + /// return Err(err); + /// } + /// }; + /// Ok(()) + /// } + /// ``` + #[cfg(any(doc, target_os = "android", target_os = "linux",))] + #[unstable(feature = "unix_socket_abstract", issue = "42048")] + pub fn from_abstract_namespace(namespace: &[u8]) -> io::Result { + unsafe { + let mut addr: libc::sockaddr_un = mem::zeroed(); + addr.sun_family = libc::AF_UNIX as libc::sa_family_t; + + if namespace.len() + 1 > addr.sun_path.len() { + return Err(io::Error::new_const( + io::ErrorKind::InvalidInput, + &"namespace must be shorter than SUN_LEN", + )); + } + + ptr::copy_nonoverlapping(namespace.as_ptr(), addr.sun_path.as_mut_ptr().offset(1) as *mut u8, namespace.len()); + let len = (sun_path_offset(&addr) + 1 + namespace.len()) as libc::socklen_t; + SocketAddr::from_parts(addr, len) + } + } } #[stable(feature = "unix_socket", since = "1.10.0")] diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index 9e39f70f68e69..f52c8b0768b3b 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -112,6 +112,35 @@ impl UnixDatagram { } } + /// Creates a Unix datagram socket bound to an address. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(unix_socket_abstract)] + /// use std::os::unix::net::{UnixDatagram, SocketAddr}; + /// + /// fn main() -> std::io::Result<()> { + /// let addr = SocketAddr::from_abstract_namespace(b"hidden")?; // Linux only + /// let sock = match UnixDatagram::bind_addr(&addr) { + /// Ok(sock) => sock, + /// Err(err) => { + /// println!("Couldn't bind: {:?}", err); + /// return Err(err); + /// } + /// }; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "unix_socket_abstract", issue = "42048")] + pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result { + unsafe { + let socket = UnixDatagram::unbound()?; + cvt(libc::bind(*socket.0.as_inner(), &socket_addr.addr as *const _ as *const _, socket_addr.len as _))?; + Ok(socket) + } + } + /// Creates a Unix Datagram socket which is not bound to any address. /// /// # Examples @@ -156,7 +185,7 @@ impl UnixDatagram { Ok((UnixDatagram(i1), UnixDatagram(i2))) } - /// Connects the socket to the specified address. + /// Connects the socket to the specified path address. /// /// The [`send`] method may be used to send data to the specified address. /// [`recv`] and [`recv_from`] will only receive data from that address. @@ -192,6 +221,35 @@ impl UnixDatagram { Ok(()) } + /// Connects the socket to an address. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(unix_socket_abstract)] + /// use std::os::unix::net::{UnixDatagram, SocketAddr}; + /// + /// fn main() -> std::io::Result<()> { + /// let addr = SocketAddr::from_abstract_namespace(b"hidden")?; // Linux only + /// let sock = UnixDatagram::unbound()?; + /// match sock.connect_addr(&addr) { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't connect: {:?}", e); + /// return Err(e) + /// } + /// }; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "unix_socket_abstract", issue = "42048")] + pub fn connect_addr(&self, socket_addr: &SocketAddr) -> io::Result<()> { + unsafe { + cvt(libc::connect(*self.0.as_inner(), &socket_addr.addr as *const _ as *const _, socket_addr.len))?; + } + Ok(()) + } + /// Creates a new independently owned handle to the underlying socket. /// /// The returned `UnixDatagram` is a reference to the same socket that this @@ -473,6 +531,40 @@ impl UnixDatagram { } } + /// Sends data on the socket to the specified [SocketAddr]. + /// + /// On success, returns the number of bytes written. + /// + /// [SocketAddr]: crate::os::unix::net::SocketAddr + /// + /// # Examples + /// + /// ```no_run + /// #![feature(unix_socket_abstract)] + /// use std::os::unix::net::{UnixDatagram, SocketAddr}; + /// + /// fn main() -> std::io::Result<()> { + /// let addr = SocketAddr::from_abstract_namespace(b"hidden")?; + /// let sock = UnixDatagram::unbound()?; + /// sock.send_to_addr(b"bacon egg and cheese", &addr).expect("send_to_addr function failed"); + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "unix_socket_abstract", issue = "42048")] + pub fn send_to_addr(&self, buf: &[u8], socket_addr: &SocketAddr) -> io::Result { + unsafe { + let count = cvt(libc::sendto( + *self.0.as_inner(), + buf.as_ptr() as *const _, + buf.len(), + MSG_NOSIGNAL, + &socket_addr.addr as *const _ as *const _, + socket_addr.len, + ))?; + Ok(count as usize) + } + } + /// Sends data on the socket to the socket's peer. /// /// The peer address may be set by the `connect` method, and this method diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs index bdd08fe8380fa..598d792d98d38 100644 --- a/library/std/src/os/unix/net/listener.rs +++ b/library/std/src/os/unix/net/listener.rs @@ -81,6 +81,40 @@ impl UnixListener { } } + /// Creates a new `UnixListener` bound to the specified [`socket address`]. + /// + /// [`socket address`]: crate::os::unix::net::SocketAddr + /// + /// # Examples + /// + /// ```no_run + /// #![feature(unix_socket_abstract)] + /// use std::os::unix::net::{UnixListener, SocketAddr}; + /// + /// fn main() -> std::io::Result<()> { + /// let addr = SocketAddr::from_abstract_namespace(b"namespace")?; // Linux only + /// let listener = match UnixListener::bind_addr(&addr) { + /// Ok(sock) => sock, + /// Err(err) => { + /// println!("Couldn't bind: {:?}", err); + /// return Err(err); + /// } + /// }; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "unix_socket_abstract", issue = "42048")] + pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result { + unsafe { + let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; + cvt(libc::bind(*inner.as_inner(), &socket_addr.addr as *const _ as *const _, socket_addr.len as _))?; + cvt(libc::listen(*inner.as_inner(), 128))?; + + Ok(UnixListener(inner)) + } + } + + /// Accepts a new incoming connection to this listener. /// /// This function will block the calling thread until a new Unix connection diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index a6f6e091305d5..3f6e2e141057e 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -104,6 +104,37 @@ impl UnixStream { } } + /// Connects to the socket specified by [`address`]. + /// + /// [`address`]: crate::os::unix::net::SocketAddr + /// + /// # Examples + /// + /// ```no_run + /// #![feature(unix_socket_abstract)] + /// use std::os::unix::net::{UnixStream, SocketAddr}; + /// + /// fn main() -> std::io::Result<()> { + /// let addr = SocketAddr::from_abstract_namespace(b"hidden")?; // Linux only + /// match UnixStream::connect_addr(&addr) { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't connect: {:?}", e); + /// return Err(e) + /// } + /// }; + /// Ok(()) + /// } + /// ```` + #[unstable(feature = "unix_socket_abstract", issue = "42048")] + pub fn connect_addr(socket_addr: &SocketAddr) -> io::Result { + unsafe { + let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; + cvt(libc::connect(*inner.as_inner(), &socket_addr.addr as *const _ as *const _, socket_addr.len))?; + Ok(UnixStream(inner)) + } + } + /// Creates an unnamed pair of connected sockets. /// /// Returns two `UnixStream`s which are connected to each other. diff --git a/library/std/src/os/unix/net/tests.rs b/library/std/src/os/unix/net/tests.rs index bd9b6dd727b96..b13ce0e252adb 100644 --- a/library/std/src/os/unix/net/tests.rs +++ b/library/std/src/os/unix/net/tests.rs @@ -388,10 +388,133 @@ fn test_unix_datagram_timeout_zero_duration() { } #[test] -fn abstract_namespace_not_allowed() { +fn abstract_namespace_not_allowed_connect() { assert!(UnixStream::connect("\0asdf").is_err()); } +#[cfg(any(target_os = "android", target_os = "linux"))] +#[test] +fn test_abstract_stream_connect() { + let msg1 = b"hello"; + let msg2 = b"world"; + + let socket_addr = or_panic!(SocketAddr::from_abstract_namespace(b"namespace")); + let listener = or_panic!(UnixListener::bind_addr(&socket_addr)); + + let thread = thread::spawn(move || { + let mut stream = or_panic!(listener.accept()).0; + let mut buf = [0; 5]; + or_panic!(stream.read(&mut buf)); + assert_eq!(&msg1[..], &buf[..]); + or_panic!(stream.write_all(msg2)); + }); + + let mut stream = or_panic!(UnixStream::connect_addr(&socket_addr)); + + let peer = or_panic!(stream.peer_addr()); + assert_eq!(peer.as_abstract_namespace().unwrap(), b"namespace"); + + or_panic!(stream.write_all(msg1)); + let mut buf = vec![]; + or_panic!(stream.read_to_end(&mut buf)); + assert_eq!(&msg2[..], &buf[..]); + drop(stream); + + thread.join().unwrap(); +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[test] +fn test_abstract_stream_iter() { + let addr = or_panic!(SocketAddr::from_abstract_namespace(b"hidden")); + let listener = or_panic!(UnixListener::bind_addr(&addr)); + + let thread = thread::spawn(move || { + for stream in listener.incoming().take(2) { + let mut stream = or_panic!(stream); + let mut buf = [0]; + or_panic!(stream.read(&mut buf)); + } + }); + + for _ in 0..2 { + let mut stream = or_panic!(UnixStream::connect_addr(&addr)); + or_panic!(stream.write_all(&[0])); + } + + thread.join().unwrap(); +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[test] +fn test_abstract_datagram_bind_send_to_addr() { + let addr1 = or_panic!(SocketAddr::from_abstract_namespace(b"ns1")); + let sock1 = or_panic!(UnixDatagram::bind_addr(&addr1)); + + let local = or_panic!(sock1.local_addr()); + assert_eq!(local.as_abstract_namespace().unwrap(), b"ns1"); + + let addr2 = or_panic!(SocketAddr::from_abstract_namespace(b"ns2")); + let sock2 = or_panic!(UnixDatagram::bind_addr(&addr2)); + + let msg = b"hello world"; + or_panic!(sock1.send_to_addr(msg, &addr2)); + let mut buf = [0; 11]; + let (len, addr) = or_panic!(sock2.recv_from(&mut buf)); + assert_eq!(msg, &buf[..]); + assert_eq!(len, 11); + assert_eq!(addr.as_abstract_namespace().unwrap(), b"ns1"); +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[test] +fn test_abstract_datagram_connect_addr() { + let addr1 = or_panic!(SocketAddr::from_abstract_namespace(b"ns3")); + let bsock1 = or_panic!(UnixDatagram::bind_addr(&addr1)); + + let sock = or_panic!(UnixDatagram::unbound()); + or_panic!(sock.connect_addr(&addr1)); + + let msg = b"hello world"; + or_panic!(sock.send(msg)); + let mut buf = [0; 11]; + let (len, addr) = or_panic!(bsock1.recv_from(&mut buf)); + assert_eq!(len, 11); + assert_eq!(addr.is_unnamed(), true); + assert_eq!(msg, &buf[..]); + + let addr2 = or_panic!(SocketAddr::from_abstract_namespace(b"ns4")); + let bsock2 = or_panic!(UnixDatagram::bind_addr(&addr2)); + + or_panic!(sock.connect_addr(&addr2)); + or_panic!(sock.send(msg)); + or_panic!(bsock2.recv_from(&mut buf)); +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[test] +fn test_abstract_namespace_too_long() { + match SocketAddr::from_abstract_namespace( + b"abcdefghijklmnopqrstuvwxyzabcdefghijklmn\ + opqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghi\ + jklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz", + ) { + Err(ref e) if e.kind() == io::ErrorKind::InvalidInput => {} + Err(e) => panic!("unexpected error {}", e), + Ok(_) => panic!("unexpected success"), + } +} + +#[cfg(any(target_os = "android", target_os = "linux"))] +#[test] +fn test_abstract_namespace_no_pathname_and_not_unnamed() { + let namespace = b"local"; + let addr = or_panic!(SocketAddr::from_abstract_namespace(&namespace[..])); + assert_eq!(addr.as_pathname(), None); + assert_eq!(addr.as_abstract_namespace(), Some(&namespace[..])); + assert_eq!(addr.is_unnamed(), false); +} + #[test] fn test_unix_stream_peek() { let (txdone, rxdone) = crate::sync::mpsc::channel(); From 6696a60f0f04e1b7e78927f5747c1080f5189370 Mon Sep 17 00:00:00 2001 From: Jacob Hoffman-Andrews Date: Sun, 16 May 2021 12:50:15 -0700 Subject: [PATCH 07/17] Add test for trait toggle location --- src/test/rustdoc/toggle-trait-fn.rs | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/test/rustdoc/toggle-trait-fn.rs diff --git a/src/test/rustdoc/toggle-trait-fn.rs b/src/test/rustdoc/toggle-trait-fn.rs new file mode 100644 index 0000000000000..a160809cbf957 --- /dev/null +++ b/src/test/rustdoc/toggle-trait-fn.rs @@ -0,0 +1,7 @@ +#![crate_name = "foo"] + +// @has foo/trait.Foo.html +// @has - '//details[@class="rustdoc-toggle"]//code' 'bar' +pub trait Foo { + fn bar() -> (); +} From 1c08ebbfd0b3515bece01de0832a758e7b71b3e8 Mon Sep 17 00:00:00 2001 From: Milan Landaverde Date: Sun, 16 May 2021 17:49:54 -0400 Subject: [PATCH 08/17] rustfmt --- library/std/src/os/unix/net/addr.rs | 6 +++++- library/std/src/os/unix/net/datagram.rs | 12 ++++++++++-- library/std/src/os/unix/net/listener.rs | 7 +++++-- library/std/src/os/unix/net/stream.rs | 6 +++++- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs index bf1685d5a40bd..4d70b510b82f9 100644 --- a/library/std/src/os/unix/net/addr.rs +++ b/library/std/src/os/unix/net/addr.rs @@ -285,7 +285,11 @@ impl SocketAddr { )); } - ptr::copy_nonoverlapping(namespace.as_ptr(), addr.sun_path.as_mut_ptr().offset(1) as *mut u8, namespace.len()); + ptr::copy_nonoverlapping( + namespace.as_ptr(), + addr.sun_path.as_mut_ptr().offset(1) as *mut u8, + namespace.len(), + ); let len = (sun_path_offset(&addr) + 1 + namespace.len()) as libc::socklen_t; SocketAddr::from_parts(addr, len) } diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index f52c8b0768b3b..0abcdca2c698f 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -136,7 +136,11 @@ impl UnixDatagram { pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result { unsafe { let socket = UnixDatagram::unbound()?; - cvt(libc::bind(*socket.0.as_inner(), &socket_addr.addr as *const _ as *const _, socket_addr.len as _))?; + cvt(libc::bind( + *socket.0.as_inner(), + &socket_addr.addr as *const _ as *const _, + socket_addr.len as _, + ))?; Ok(socket) } } @@ -245,7 +249,11 @@ impl UnixDatagram { #[unstable(feature = "unix_socket_abstract", issue = "42048")] pub fn connect_addr(&self, socket_addr: &SocketAddr) -> io::Result<()> { unsafe { - cvt(libc::connect(*self.0.as_inner(), &socket_addr.addr as *const _ as *const _, socket_addr.len))?; + cvt(libc::connect( + *self.0.as_inner(), + &socket_addr.addr as *const _ as *const _, + socket_addr.len, + ))?; } Ok(()) } diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs index 598d792d98d38..658f54989075b 100644 --- a/library/std/src/os/unix/net/listener.rs +++ b/library/std/src/os/unix/net/listener.rs @@ -107,14 +107,17 @@ impl UnixListener { pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result { unsafe { let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; - cvt(libc::bind(*inner.as_inner(), &socket_addr.addr as *const _ as *const _, socket_addr.len as _))?; + cvt(libc::bind( + *inner.as_inner(), + &socket_addr.addr as *const _ as *const _, + socket_addr.len as _, + ))?; cvt(libc::listen(*inner.as_inner(), 128))?; Ok(UnixListener(inner)) } } - /// Accepts a new incoming connection to this listener. /// /// This function will block the calling thread until a new Unix connection diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index 3f6e2e141057e..640caf8d9925b 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -130,7 +130,11 @@ impl UnixStream { pub fn connect_addr(socket_addr: &SocketAddr) -> io::Result { unsafe { let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; - cvt(libc::connect(*inner.as_inner(), &socket_addr.addr as *const _ as *const _, socket_addr.len))?; + cvt(libc::connect( + *inner.as_inner(), + &socket_addr.addr as *const _ as *const _, + socket_addr.len, + ))?; Ok(UnixStream(inner)) } } From 500503b16ca9f72a44028e27c1cc2fa76be49085 Mon Sep 17 00:00:00 2001 From: Aaron Hill Date: Sun, 16 May 2021 22:26:57 -0400 Subject: [PATCH 09/17] Suppress spurious errors inside `async fn` Fixes #73741 --- .../src/check/generator_interior.rs | 30 +++++++++++++------ .../ui/async-await/issue-73741-type-err.rs | 14 +++++++++ .../async-await/issue-73741-type-err.stderr | 11 +++++++ 3 files changed, 46 insertions(+), 9 deletions(-) create mode 100644 src/test/ui/async-await/issue-73741-type-err.rs create mode 100644 src/test/ui/async-await/issue-73741-type-err.stderr diff --git a/compiler/rustc_typeck/src/check/generator_interior.rs b/compiler/rustc_typeck/src/check/generator_interior.rs index e40aa91485858..5f26e701c0ab7 100644 --- a/compiler/rustc_typeck/src/check/generator_interior.rs +++ b/compiler/rustc_typeck/src/check/generator_interior.rs @@ -89,19 +89,31 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { if let Some((unresolved_type, unresolved_type_span)) = self.fcx.unresolved_type_vars(&ty) { - let note = format!( - "the type is part of the {} because of this {}", - self.kind, yield_data.source - ); - // If unresolved type isn't a ty_var then unresolved_type_span is None let span = self .prev_unresolved_span .unwrap_or_else(|| unresolved_type_span.unwrap_or(source_span)); - self.fcx - .need_type_info_err_in_generator(self.kind, span, unresolved_type) - .span_note(yield_data.span, &*note) - .emit(); + + // If we encounter an int/float variable, then inference fallback didn't + // finish due to some other error. Don't emit spurious additional errors. + if let ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(_)) = + unresolved_type.kind() + { + self.fcx + .tcx + .sess + .delay_span_bug(span, &format!("Encountered var {:?}", unresolved_type)); + } else { + let note = format!( + "the type is part of the {} because of this {}", + self.kind, yield_data.source + ); + + self.fcx + .need_type_info_err_in_generator(self.kind, span, unresolved_type) + .span_note(yield_data.span, &*note) + .emit(); + } } else { // Insert the type into the ordered set. let scope_span = scope.map(|s| s.span(self.fcx.tcx, self.region_scope_tree)); diff --git a/src/test/ui/async-await/issue-73741-type-err.rs b/src/test/ui/async-await/issue-73741-type-err.rs new file mode 100644 index 0000000000000..c5b9e34edf703 --- /dev/null +++ b/src/test/ui/async-await/issue-73741-type-err.rs @@ -0,0 +1,14 @@ +// edition:2018 +// +// Regression test for issue #73741 +// Ensures that we don't emit spurious errors when +// a type error ocurrs in an `async fn` + +async fn weird() { + 1 = 2; //~ ERROR invalid left-hand side + + let mut loop_count = 0; + async {}.await +} + +fn main() {} diff --git a/src/test/ui/async-await/issue-73741-type-err.stderr b/src/test/ui/async-await/issue-73741-type-err.stderr new file mode 100644 index 0000000000000..0b5343a98cfb4 --- /dev/null +++ b/src/test/ui/async-await/issue-73741-type-err.stderr @@ -0,0 +1,11 @@ +error[E0070]: invalid left-hand side of assignment + --> $DIR/issue-73741-type-err.rs:8:7 + | +LL | 1 = 2; + | - ^ + | | + | cannot assign to this expression + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0070`. From 7b301985faa73b1404cbc21ffe7c7f859a293448 Mon Sep 17 00:00:00 2001 From: Fabian Wolff Date: Mon, 17 May 2021 13:56:11 +0200 Subject: [PATCH 10/17] Two minor changes for readability and efficiency --- compiler/rustc_passes/src/lang_items.rs | 5 ++--- compiler/rustc_typeck/src/check/mod.rs | 7 ++----- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs index 9086e21579eda..a6306ad923dab 100644 --- a/compiler/rustc_passes/src/lang_items.rs +++ b/compiler/rustc_passes/src/lang_items.rs @@ -100,12 +100,11 @@ impl LanguageItemCollector<'tcx> { } fn collect_item(&mut self, item_index: usize, item_def_id: DefId) { - let lang_item = LangItem::from_u32(item_index as u32).unwrap(); - let name = lang_item.name(); - // Check for duplicates. if let Some(original_def_id) = self.items.items[item_index] { if original_def_id != item_def_id { + let lang_item = LangItem::from_u32(item_index as u32).unwrap(); + let name = lang_item.name(); let mut err = match self.tcx.hir().span_if_local(item_def_id) { Some(span) => struct_span_err!( self.tcx.sess, diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index 6f96bd544c06c..0b5387993c948 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -1194,13 +1194,10 @@ fn potentially_plural_count(count: usize, word: &str) -> String { fn has_expected_num_generic_args<'tcx>( tcx: TyCtxt<'tcx>, trait_did: Option, - mut expected: usize, + expected: usize, ) -> bool { trait_did.map_or(true, |trait_did| { let generics = tcx.generics_of(trait_did); - if generics.has_self { - expected += 1; - } - generics.count() == expected + generics.count() == expected + if generics.has_self { 1 } else { 0 } }) } From a4d048931fb5e670aa16cafeb1fa0079b6f20f7d Mon Sep 17 00:00:00 2001 From: Milan Landaverde Date: Mon, 17 May 2021 08:29:49 -0400 Subject: [PATCH 11/17] Update tracking issue in stability refs --- library/std/src/os/unix/net/addr.rs | 4 ++-- library/std/src/os/unix/net/datagram.rs | 6 +++--- library/std/src/os/unix/net/listener.rs | 2 +- library/std/src/os/unix/net/stream.rs | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/library/std/src/os/unix/net/addr.rs b/library/std/src/os/unix/net/addr.rs index 4d70b510b82f9..517e72eaf7e1c 100644 --- a/library/std/src/os/unix/net/addr.rs +++ b/library/std/src/os/unix/net/addr.rs @@ -215,7 +215,7 @@ impl SocketAddr { /// } /// ``` #[cfg(any(doc, target_os = "android", target_os = "linux",))] - #[unstable(feature = "unix_socket_abstract", issue = "42048")] + #[unstable(feature = "unix_socket_abstract", issue = "85410")] pub fn as_abstract_namespace(&self) -> Option<&[u8]> { if let AddressKind::Abstract(name) = self.address() { Some(name) } else { None } } @@ -272,7 +272,7 @@ impl SocketAddr { /// } /// ``` #[cfg(any(doc, target_os = "android", target_os = "linux",))] - #[unstable(feature = "unix_socket_abstract", issue = "42048")] + #[unstable(feature = "unix_socket_abstract", issue = "85410")] pub fn from_abstract_namespace(namespace: &[u8]) -> io::Result { unsafe { let mut addr: libc::sockaddr_un = mem::zeroed(); diff --git a/library/std/src/os/unix/net/datagram.rs b/library/std/src/os/unix/net/datagram.rs index 0abcdca2c698f..2e750be5e7a35 100644 --- a/library/std/src/os/unix/net/datagram.rs +++ b/library/std/src/os/unix/net/datagram.rs @@ -132,7 +132,7 @@ impl UnixDatagram { /// Ok(()) /// } /// ``` - #[unstable(feature = "unix_socket_abstract", issue = "42048")] + #[unstable(feature = "unix_socket_abstract", issue = "85410")] pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result { unsafe { let socket = UnixDatagram::unbound()?; @@ -246,7 +246,7 @@ impl UnixDatagram { /// Ok(()) /// } /// ``` - #[unstable(feature = "unix_socket_abstract", issue = "42048")] + #[unstable(feature = "unix_socket_abstract", issue = "85410")] pub fn connect_addr(&self, socket_addr: &SocketAddr) -> io::Result<()> { unsafe { cvt(libc::connect( @@ -558,7 +558,7 @@ impl UnixDatagram { /// Ok(()) /// } /// ``` - #[unstable(feature = "unix_socket_abstract", issue = "42048")] + #[unstable(feature = "unix_socket_abstract", issue = "85410")] pub fn send_to_addr(&self, buf: &[u8], socket_addr: &SocketAddr) -> io::Result { unsafe { let count = cvt(libc::sendto( diff --git a/library/std/src/os/unix/net/listener.rs b/library/std/src/os/unix/net/listener.rs index 658f54989075b..ecf121770c1ac 100644 --- a/library/std/src/os/unix/net/listener.rs +++ b/library/std/src/os/unix/net/listener.rs @@ -103,7 +103,7 @@ impl UnixListener { /// Ok(()) /// } /// ``` - #[unstable(feature = "unix_socket_abstract", issue = "42048")] + #[unstable(feature = "unix_socket_abstract", issue = "85410")] pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result { unsafe { let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index 640caf8d9925b..1ae6acd7bbf09 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -126,7 +126,7 @@ impl UnixStream { /// Ok(()) /// } /// ```` - #[unstable(feature = "unix_socket_abstract", issue = "42048")] + #[unstable(feature = "unix_socket_abstract", issue = "85410")] pub fn connect_addr(socket_addr: &SocketAddr) -> io::Result { unsafe { let inner = Socket::new_raw(libc::AF_UNIX, libc::SOCK_STREAM)?; From 572bb13ae537b198e7909911bf114d3cbb9f3d8e Mon Sep 17 00:00:00 2001 From: Fabian Wolff Date: Mon, 17 May 2021 22:58:09 +0200 Subject: [PATCH 12/17] Implement jackh726's suggestions --- .../src/traits/error_reporting/suggestions.rs | 18 ++---- .../imm-ref-trait-object-literal.stderr | 2 +- src/test/ui/suggestions/issue-84973-2.stderr | 2 +- .../ui/suggestions/issue-84973-blacklist.rs | 29 +++++++++ .../suggestions/issue-84973-blacklist.stderr | 64 +++++++++++++++++++ .../ui/suggestions/issue-84973-negative.rs | 12 ++++ .../suggestions/issue-84973-negative.stderr | 24 +++++++ 7 files changed, 137 insertions(+), 14 deletions(-) create mode 100644 src/test/ui/suggestions/issue-84973-blacklist.rs create mode 100644 src/test/ui/suggestions/issue-84973-blacklist.stderr create mode 100644 src/test/ui/suggestions/issue-84973-negative.rs create mode 100644 src/test/ui/suggestions/issue-84973-negative.stderr diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index d090120406126..8bbd2da537513 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -689,17 +689,11 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { // Blacklist traits for which it would be nonsensical to suggest borrowing. // For instance, immutable references are always Copy, so suggesting to // borrow would always succeed, but it's probably not what the user wanted. - let blacklist: Vec<_> = [ - LangItem::Copy, - LangItem::Clone, - LangItem::Pin, - LangItem::Unpin, - LangItem::Sized, - LangItem::Send, - ] - .iter() - .filter_map(|lang_item| self.tcx.lang_items().require(*lang_item).ok()) - .collect(); + let blacklist: Vec<_> = + [LangItem::Copy, LangItem::Clone, LangItem::Unpin, LangItem::Sized, LangItem::Send] + .iter() + .filter_map(|lang_item| self.tcx.lang_items().require(*lang_item).ok()) + .collect(); let span = obligation.cause.span; let param_env = obligation.param_env; @@ -771,7 +765,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { err.span_suggestion( span, &format!( - "consider borrowing{} here", + "consider{} borrowing here", if mtbl { " mutably" } else { "" } ), format!("&{}{}", if mtbl { "mut " } else { "" }, snippet), diff --git a/src/test/ui/suggestions/imm-ref-trait-object-literal.stderr b/src/test/ui/suggestions/imm-ref-trait-object-literal.stderr index 3cf744105695e..bd060c92cd469 100644 --- a/src/test/ui/suggestions/imm-ref-trait-object-literal.stderr +++ b/src/test/ui/suggestions/imm-ref-trait-object-literal.stderr @@ -24,7 +24,7 @@ LL | foo(s); | ^ | | | expected an implementor of trait `Trait` - | help: consider borrowing mutably here: `&mut s` + | help: consider mutably borrowing here: `&mut s` error: aborting due to 2 previous errors diff --git a/src/test/ui/suggestions/issue-84973-2.stderr b/src/test/ui/suggestions/issue-84973-2.stderr index 6394e58a2a910..b6ed437b5eefd 100644 --- a/src/test/ui/suggestions/issue-84973-2.stderr +++ b/src/test/ui/suggestions/issue-84973-2.stderr @@ -8,7 +8,7 @@ LL | foo(a); | ^ | | | expected an implementor of trait `Tr` - | help: consider borrowing mutably here: `&mut a` + | help: consider mutably borrowing here: `&mut a` error: aborting due to previous error diff --git a/src/test/ui/suggestions/issue-84973-blacklist.rs b/src/test/ui/suggestions/issue-84973-blacklist.rs new file mode 100644 index 0000000000000..db954530b1beb --- /dev/null +++ b/src/test/ui/suggestions/issue-84973-blacklist.rs @@ -0,0 +1,29 @@ +// Checks that certain traits for which we don't want to suggest borrowing +// are blacklisted and don't cause the suggestion to be issued. + +#![feature(generators)] + +fn f_copy(t: T) {} +fn f_clone(t: T) {} +fn f_unpin(t: T) {} +fn f_sized(t: T) {} +fn f_send(t: T) {} + +struct S; + +fn main() { + f_copy("".to_string()); //~ ERROR: the trait bound `String: Copy` is not satisfied [E0277] + f_clone(S); //~ ERROR: the trait bound `S: Clone` is not satisfied [E0277] + f_unpin(static || { yield; }); + //~^ ERROR: cannot be unpinned [E0277] + + let cl = || (); + let ref_cl: &dyn Fn() -> () = &cl; + f_sized(*ref_cl); + //~^ ERROR: the size for values of type `dyn Fn()` cannot be known at compilation time [E0277] + //~| ERROR: the size for values of type `dyn Fn()` cannot be known at compilation time [E0277] + + use std::rc::Rc; + let rc = Rc::new(0); + f_send(rc); //~ ERROR: `Rc<{integer}>` cannot be sent between threads safely [E0277] +} diff --git a/src/test/ui/suggestions/issue-84973-blacklist.stderr b/src/test/ui/suggestions/issue-84973-blacklist.stderr new file mode 100644 index 0000000000000..f1e6ef883ae90 --- /dev/null +++ b/src/test/ui/suggestions/issue-84973-blacklist.stderr @@ -0,0 +1,64 @@ +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/issue-84973-blacklist.rs:15:12 + | +LL | fn f_copy(t: T) {} + | ---- required by this bound in `f_copy` +... +LL | f_copy("".to_string()); + | ^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + +error[E0277]: the trait bound `S: Clone` is not satisfied + --> $DIR/issue-84973-blacklist.rs:16:13 + | +LL | fn f_clone(t: T) {} + | ----- required by this bound in `f_clone` +... +LL | f_clone(S); + | ^ the trait `Clone` is not implemented for `S` + +error[E0277]: `[static generator@$DIR/issue-84973-blacklist.rs:17:13: 17:33]` cannot be unpinned + --> $DIR/issue-84973-blacklist.rs:17:5 + | +LL | fn f_unpin(t: T) {} + | ----- required by this bound in `f_unpin` +... +LL | f_unpin(static || { yield; }); + | ^^^^^^^ the trait `Unpin` is not implemented for `[static generator@$DIR/issue-84973-blacklist.rs:17:13: 17:33]` + | + = note: consider using `Box::pin` + +error[E0277]: the size for values of type `dyn Fn()` cannot be known at compilation time + --> $DIR/issue-84973-blacklist.rs:22:13 + | +LL | fn f_sized(t: T) {} + | - required by this bound in `f_sized` +... +LL | f_sized(*ref_cl); + | ^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `dyn Fn()` + +error[E0277]: `Rc<{integer}>` cannot be sent between threads safely + --> $DIR/issue-84973-blacklist.rs:28:12 + | +LL | fn f_send(t: T) {} + | ---- required by this bound in `f_send` +... +LL | f_send(rc); + | ^^ `Rc<{integer}>` cannot be sent between threads safely + | + = help: the trait `Send` is not implemented for `Rc<{integer}>` + +error[E0277]: the size for values of type `dyn Fn()` cannot be known at compilation time + --> $DIR/issue-84973-blacklist.rs:22:5 + | +LL | f_sized(*ref_cl); + | ^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `dyn Fn()` + = note: all function arguments must have a statically known size + = help: unsized fn params are gated as an unstable feature + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/suggestions/issue-84973-negative.rs b/src/test/ui/suggestions/issue-84973-negative.rs new file mode 100644 index 0000000000000..f339251e57dab --- /dev/null +++ b/src/test/ui/suggestions/issue-84973-negative.rs @@ -0,0 +1,12 @@ +// Checks that we only suggest borrowing if &T actually implements the trait. + +trait Tr {} +impl Tr for &f32 {} +fn bar(t: T) {} + +fn main() { + let a = 0i32; + let b = 0.0f32; + bar(a); //~ ERROR: the trait bound `i32: Tr` is not satisfied [E0277] + bar(b); //~ ERROR: the trait bound `f32: Tr` is not satisfied [E0277] +} diff --git a/src/test/ui/suggestions/issue-84973-negative.stderr b/src/test/ui/suggestions/issue-84973-negative.stderr new file mode 100644 index 0000000000000..94513eca0bf09 --- /dev/null +++ b/src/test/ui/suggestions/issue-84973-negative.stderr @@ -0,0 +1,24 @@ +error[E0277]: the trait bound `i32: Tr` is not satisfied + --> $DIR/issue-84973-negative.rs:10:9 + | +LL | fn bar(t: T) {} + | -- required by this bound in `bar` +... +LL | bar(a); + | ^ the trait `Tr` is not implemented for `i32` + +error[E0277]: the trait bound `f32: Tr` is not satisfied + --> $DIR/issue-84973-negative.rs:11:9 + | +LL | fn bar(t: T) {} + | -- required by this bound in `bar` +... +LL | bar(b); + | ^ + | | + | expected an implementor of trait `Tr` + | help: consider borrowing here: `&b` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. From b574c67b93fbe0fc1194865441a1fa596b8922f1 Mon Sep 17 00:00:00 2001 From: Alexis Bourget Date: Mon, 28 Dec 2020 23:07:20 +0100 Subject: [PATCH 13/17] New rustdoc lint to respect -Dwarnings correctly This adds a new lint to `rustc` that is used in rustdoc when a code block is empty or cannot be parsed as valid Rust code. Previously this was unconditionally a warning. As such some documentation comments were (unknowingly) abusing this to pass despite the `-Dwarnings` used when compiling `rustc`, this should not be the case anymore. --- .../src/borrow_check/region_infer/mod.rs | 2 +- .../rustc_trait_selection/src/opaque_types.rs | 1 + compiler/rustc_typeck/src/check/upvar.rs | 4 +- src/doc/rustdoc/src/lints.md | 37 ++++++++ src/librustdoc/lint.rs | 13 +++ .../passes/check_code_block_syntax.rs | 92 ++++++++++++------- src/test/rustdoc-ui/ignore-block-help.stderr | 5 +- src/test/rustdoc-ui/invalid-syntax.stderr | 1 + 8 files changed, 115 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs b/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs index bbd512fd36050..9dc2e3d292359 100644 --- a/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs @@ -1241,7 +1241,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// it. However, it works pretty well in practice. In particular, /// this is needed to deal with projection outlives bounds like /// - /// ```ignore (internal compiler representation so lifetime syntax is invalid) + /// ```text (internal compiler representation so lifetime syntax is invalid) /// >::Item: '1 /// ``` /// diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs index fb4a8ce687c4d..7e67bc118ec1e 100644 --- a/compiler/rustc_trait_selection/src/opaque_types.rs +++ b/compiler/rustc_trait_selection/src/opaque_types.rs @@ -46,6 +46,7 @@ pub struct OpaqueTypeDecl<'tcx> { /// type Foo = impl Baz; /// fn bar() -> Foo { /// // ^^^ This is the span we are looking for! + /// } /// ``` /// /// In cases where the fn returns `(impl Trait, impl Trait)` or diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs index ff506ef8727b9..71e222c560a05 100644 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ b/compiler/rustc_typeck/src/check/upvar.rs @@ -323,7 +323,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// InferBorrowKind results in a structure like this: /// - /// ``` + /// ```text /// { /// Place(base: hir_id_s, projections: [], ....) -> { /// capture_kind_expr: hir_id_L5, @@ -348,7 +348,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// ``` /// /// After the min capture analysis, we get: - /// ``` + /// ```text /// { /// hir_id_s -> [ /// Place(base: hir_id_s, projections: [], ....) -> { diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md index a6626679a7d60..7088e60aa07da 100644 --- a/src/doc/rustdoc/src/lints.md +++ b/src/doc/rustdoc/src/lints.md @@ -294,6 +294,43 @@ warning: unclosed HTML tag `h1` warning: 2 warnings emitted ``` +## invalid_rust_codeblock + +This lint **warns by default**. It detects Rust code blocks in documentation +examples that are invalid (e.g. empty, not parsable as Rust). For example: + +```rust +/// Empty code blocks (with and without the `rust` marker): +/// +/// ```rust +/// ``` +/// +/// Unclosed code blocks (with and without the `rust` marker): +/// +/// ```rust +fn main() {} +``` + +Which will give: + +```text +warning: Rust code block is empty +--> src/lib.rs:3:5 +| +3 | /// ```rust +| _____^ +4 | | /// ``` +| |_______^ +| += note: `#[warn(rustdoc::invalid_rust_codeblock)]` on by default + +warning: Rust code block is empty +--> src/lib.rs:8:5 +| +8 | /// ```rust +| ^^^^^^^ +``` + ## bare_urls This lint is **warn-by-default**. It detects URLs which are not links. diff --git a/src/librustdoc/lint.rs b/src/librustdoc/lint.rs index 1b79811d4b075..597efed56e176 100644 --- a/src/librustdoc/lint.rs +++ b/src/librustdoc/lint.rs @@ -157,6 +157,18 @@ declare_rustdoc_lint! { "detects URLs that are not hyperlinks" } +declare_rustdoc_lint! { + /// The `invalid_rust_codeblock` lint detects Rust code blocks in + /// documentation examples that are invalid (e.g. empty, not parsable as + /// Rust code). This is a `rustdoc` only lint, see the documentation in the + /// [rustdoc book]. + /// + /// [rustdoc book]: ../../../rustdoc/lints.html#invalid_rust_codeblock + INVALID_RUST_CODEBLOCK, + Warn, + "codeblock could not be parsed as valid Rust or is empty" +} + crate static RUSTDOC_LINTS: Lazy> = Lazy::new(|| { vec![ BROKEN_INTRA_DOC_LINKS, @@ -164,6 +176,7 @@ crate static RUSTDOC_LINTS: Lazy> = Lazy::new(|| { MISSING_DOC_CODE_EXAMPLES, PRIVATE_DOC_TESTS, INVALID_CODEBLOCK_ATTRIBUTES, + INVALID_RUST_CODEBLOCK, INVALID_HTML_TAGS, BARE_URLS, MISSING_CRATE_LEVEL_DOCS, diff --git a/src/librustdoc/passes/check_code_block_syntax.rs b/src/librustdoc/passes/check_code_block_syntax.rs index 8d07cde51880c..293941391c8b8 100644 --- a/src/librustdoc/passes/check_code_block_syntax.rs +++ b/src/librustdoc/passes/check_code_block_syntax.rs @@ -1,5 +1,6 @@ use rustc_data_structures::sync::{Lock, Lrc}; use rustc_errors::{emitter::Emitter, Applicability, Diagnostic, Handler}; +use rustc_middle::lint::LintDiagnosticBuilder; use rustc_parse::parse_stream_from_source_str; use rustc_session::parse::ParseSess; use rustc_span::source_map::{FilePathMapping, SourceMap}; @@ -47,50 +48,65 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> { .unwrap_or(false); let buffer = buffer.borrow(); - if buffer.has_errors || is_empty { - let mut diag = if let Some(sp) = super::source_span_for_markdown_range( - self.cx.tcx, - &dox, - &code_block.range, - &item.attrs, - ) { - let (warning_message, suggest_using_text) = if buffer.has_errors { - ("could not parse code block as Rust code", true) + if !(buffer.has_errors || is_empty) { + // No errors in a non-empty program. + return; + } + + let local_id = match item.def_id.as_local() { + Some(id) => id, + // We don't need to check the syntax for other crates so returning + // without doing anything should not be a problem. + None => return, + }; + + let hir_id = self.cx.tcx.hir().local_def_id_to_hir_id(local_id); + let suggest_using_text = code_block.syntax.is_none() && code_block.is_fenced; + let is_ignore = code_block.is_ignore; + + // The span and whether it is precise or not. + let (sp, precise_span) = match super::source_span_for_markdown_range( + self.cx.tcx, + &dox, + &code_block.range, + &item.attrs, + ) { + Some(sp) => (sp, true), + None => (item.attr_span(self.cx.tcx), false), + }; + + // lambda that will use the lint to start a new diagnostic and add + // a suggestion to it when needed. + let diag_builder = |lint: LintDiagnosticBuilder<'_>| { + let mut diag = if precise_span { + let msg = if buffer.has_errors { + "could not parse code block as Rust code" } else { - ("Rust code block is empty", false) + "Rust code block is empty" }; - let mut diag = self.cx.sess().struct_span_warn(sp, warning_message); + let mut diag = lint.build(msg); + + if suggest_using_text { + let extended_msg = if is_ignore { + "`ignore` code blocks require valid Rust code for syntax highlighting. \ + Mark blocks that do not contain Rust code as text" + } else { + "mark blocks that do not contain Rust code as text" + }; - if code_block.syntax.is_none() && code_block.is_fenced { - let sp = sp.from_inner(InnerSpan::new(0, 3)); diag.span_suggestion( - sp, - "mark blocks that do not contain Rust code as text", + sp.from_inner(InnerSpan::new(0, 3)), + extended_msg, String::from("```text"), Applicability::MachineApplicable, ); - } else if suggest_using_text && code_block.is_ignore { - let sp = sp.from_inner(InnerSpan::new(0, 3)); - diag.span_suggestion( - sp, - "`ignore` code blocks require valid Rust code for syntax highlighting. \ - Mark blocks that do not contain Rust code as text", - String::from("```text,"), - Applicability::MachineApplicable, - ); } diag } else { - // We couldn't calculate the span of the markdown block that had the error, so our - // diagnostics are going to be a bit lacking. - let mut diag = self.cx.sess().struct_span_warn( - item.attr_span(self.cx.tcx), - "doc comment contains an invalid Rust code block", - ); - - if code_block.syntax.is_none() && code_block.is_fenced { + let mut diag = lint.build("doc comment contains an invalid Rust code block"); + if suggest_using_text { diag.help("mark blocks that do not contain Rust code as text: ```text"); } @@ -103,7 +119,17 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> { } diag.emit(); - } + }; + + // Finally build and emit the completed diagnostic. + // All points of divergence have been handled earlier so this can be + // done the same way whether the span is precise or not. + self.cx.tcx.struct_span_lint_hir( + crate::lint::INVALID_RUST_CODEBLOCK, + hir_id, + sp, + diag_builder, + ); } } diff --git a/src/test/rustdoc-ui/ignore-block-help.stderr b/src/test/rustdoc-ui/ignore-block-help.stderr index d45cd92d2d106..313b22c4c7c20 100644 --- a/src/test/rustdoc-ui/ignore-block-help.stderr +++ b/src/test/rustdoc-ui/ignore-block-help.stderr @@ -7,11 +7,8 @@ LL | | /// let heart = '❤️'; LL | | /// ``` | |_______^ | + = note: `#[warn(invalid_rust_codeblock)]` on by default = note: error from rustc: character literal may only contain one codepoint -help: `ignore` code blocks require valid Rust code for syntax highlighting. Mark blocks that do not contain Rust code as text - | -LL | /// ```text,ignore (to-prevent-tidy-error) - | ^^^^^^^^ warning: 1 warning emitted diff --git a/src/test/rustdoc-ui/invalid-syntax.stderr b/src/test/rustdoc-ui/invalid-syntax.stderr index 75acdc5ab5f26..67e093f9de20a 100644 --- a/src/test/rustdoc-ui/invalid-syntax.stderr +++ b/src/test/rustdoc-ui/invalid-syntax.stderr @@ -7,6 +7,7 @@ LL | | /// \__________pkt->size___________/ \_result->size_/ \__pkt->si LL | | /// ``` | |_______^ | + = note: `#[warn(invalid_rust_codeblock)]` on by default = note: error from rustc: unknown start of token: \ = note: error from rustc: unknown start of token: \ = note: error from rustc: unknown start of token: \ From 4120f7561c7df15e099386adcf262f1e2adbc2de Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 26 Apr 2021 09:11:08 -0400 Subject: [PATCH 14/17] Add back missing help for ignore blocks This also gives a better error message when a span is missing. --- src/doc/rustdoc/src/lints.md | 2 +- .../passes/check_code_block_syntax.rs | 54 +++++++++---------- src/test/rustdoc-ui/ignore-block-help.rs | 5 +- src/test/rustdoc-ui/ignore-block-help.stderr | 7 ++- src/test/rustdoc-ui/invalid-syntax.rs | 2 +- src/test/rustdoc-ui/invalid-syntax.stderr | 4 +- 6 files changed, 40 insertions(+), 34 deletions(-) diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md index 7088e60aa07da..46c4a8a41aca6 100644 --- a/src/doc/rustdoc/src/lints.md +++ b/src/doc/rustdoc/src/lints.md @@ -328,7 +328,7 @@ warning: Rust code block is empty --> src/lib.rs:8:5 | 8 | /// ```rust -| ^^^^^^^ +| ^^^^^^^ ``` ## bare_urls diff --git a/src/librustdoc/passes/check_code_block_syntax.rs b/src/librustdoc/passes/check_code_block_syntax.rs index 293941391c8b8..4cc16de6808f3 100644 --- a/src/librustdoc/passes/check_code_block_syntax.rs +++ b/src/librustdoc/passes/check_code_block_syntax.rs @@ -61,7 +61,7 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> { }; let hir_id = self.cx.tcx.hir().local_def_id_to_hir_id(local_id); - let suggest_using_text = code_block.syntax.is_none() && code_block.is_fenced; + let empty_block = code_block.syntax.is_none() && code_block.is_fenced; let is_ignore = code_block.is_ignore; // The span and whether it is precise or not. @@ -78,40 +78,38 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> { // lambda that will use the lint to start a new diagnostic and add // a suggestion to it when needed. let diag_builder = |lint: LintDiagnosticBuilder<'_>| { - let mut diag = if precise_span { - let msg = if buffer.has_errors { - "could not parse code block as Rust code" - } else { - "Rust code block is empty" - }; - - let mut diag = lint.build(msg); - - if suggest_using_text { - let extended_msg = if is_ignore { - "`ignore` code blocks require valid Rust code for syntax highlighting. \ - Mark blocks that do not contain Rust code as text" - } else { - "mark blocks that do not contain Rust code as text" - }; + let explanation = if is_ignore { + "`ignore` code blocks require valid Rust code for syntax highlighting; \ + mark blocks that do not contain Rust code as text" + } else { + "mark blocks that do not contain Rust code as text" + }; + let msg = if buffer.has_errors { + "could not parse code block as Rust code" + } else { + "Rust code block is empty" + }; + let mut diag = lint.build(msg); + if precise_span { + if is_ignore { + // giving an accurate suggestion is hard because `ignore` might not have come first in the list. + // just give a `help` instead. + diag.span_help( + sp.from_inner(InnerSpan::new(0, 3)), + &format!("{}: ```text", explanation), + ); + } else if empty_block { diag.span_suggestion( sp.from_inner(InnerSpan::new(0, 3)), - extended_msg, + explanation, String::from("```text"), Applicability::MachineApplicable, ); } - - diag - } else { - let mut diag = lint.build("doc comment contains an invalid Rust code block"); - if suggest_using_text { - diag.help("mark blocks that do not contain Rust code as text: ```text"); - } - - diag - }; + } else if empty_block || is_ignore { + diag.help(&format!("{}: ```text", explanation)); + } // FIXME(#67563): Provide more context for these errors by displaying the spans inline. for message in buffer.messages.iter() { diff --git a/src/test/rustdoc-ui/ignore-block-help.rs b/src/test/rustdoc-ui/ignore-block-help.rs index c22dddd11dffa..86f6a2868fb56 100644 --- a/src/test/rustdoc-ui/ignore-block-help.rs +++ b/src/test/rustdoc-ui/ignore-block-help.rs @@ -3,5 +3,8 @@ /// ```ignore (to-prevent-tidy-error) /// let heart = '❤️'; /// ``` -//~^^^ WARN +//~^^^ WARNING could not parse code block +//~| NOTE on by default +//~| NOTE character literal may only contain one codepoint +//~| HELP `ignore` code blocks require valid Rust code pub struct X; diff --git a/src/test/rustdoc-ui/ignore-block-help.stderr b/src/test/rustdoc-ui/ignore-block-help.stderr index 313b22c4c7c20..b809ece86e76a 100644 --- a/src/test/rustdoc-ui/ignore-block-help.stderr +++ b/src/test/rustdoc-ui/ignore-block-help.stderr @@ -7,7 +7,12 @@ LL | | /// let heart = '❤️'; LL | | /// ``` | |_______^ | - = note: `#[warn(invalid_rust_codeblock)]` on by default + = note: `#[warn(rustdoc::invalid_rust_codeblock)]` on by default +help: `ignore` code blocks require valid Rust code for syntax highlighting; mark blocks that do not contain Rust code as text: ```text + --> $DIR/ignore-block-help.rs:3:5 + | +LL | /// ```ignore (to-prevent-tidy-error) + | ^^^ = note: error from rustc: character literal may only contain one codepoint warning: 1 warning emitted diff --git a/src/test/rustdoc-ui/invalid-syntax.rs b/src/test/rustdoc-ui/invalid-syntax.rs index c395a8ef3d41a..b503d1093fda8 100644 --- a/src/test/rustdoc-ui/invalid-syntax.rs +++ b/src/test/rustdoc-ui/invalid-syntax.rs @@ -71,7 +71,7 @@ pub fn blargh() {} /// \_ #[doc = "```"] pub fn crazy_attrs() {} -//~^^^^ WARNING doc comment contains an invalid Rust code block +//~^^^^ WARNING could not parse code block /// ```rust /// ``` diff --git a/src/test/rustdoc-ui/invalid-syntax.stderr b/src/test/rustdoc-ui/invalid-syntax.stderr index 67e093f9de20a..7fcb9a0e9a9ab 100644 --- a/src/test/rustdoc-ui/invalid-syntax.stderr +++ b/src/test/rustdoc-ui/invalid-syntax.stderr @@ -7,7 +7,7 @@ LL | | /// \__________pkt->size___________/ \_result->size_/ \__pkt->si LL | | /// ``` | |_______^ | - = note: `#[warn(invalid_rust_codeblock)]` on by default + = note: `#[warn(rustdoc::invalid_rust_codeblock)]` on by default = note: error from rustc: unknown start of token: \ = note: error from rustc: unknown start of token: \ = note: error from rustc: unknown start of token: \ @@ -91,7 +91,7 @@ LL | | /// ``` | = note: error from rustc: unknown start of token: \ -warning: doc comment contains an invalid Rust code block +warning: could not parse code block as Rust code --> $DIR/invalid-syntax.rs:70:1 | LL | / #[doc = "```"] From b885d81a4a59fa9282ecb088e6eac8e30cb4a9bf Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Fri, 30 Apr 2021 21:28:32 -0400 Subject: [PATCH 15/17] Rename INVALID_RUST_CODEBLOCK{,S} --- src/doc/rustdoc/src/lints.md | 4 ++-- src/librustdoc/lint.rs | 8 ++++---- src/librustdoc/passes/check_code_block_syntax.rs | 2 +- src/test/rustdoc-ui/ignore-block-help.stderr | 2 +- src/test/rustdoc-ui/invalid-syntax.stderr | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md index 46c4a8a41aca6..9f1e1a5b88b2f 100644 --- a/src/doc/rustdoc/src/lints.md +++ b/src/doc/rustdoc/src/lints.md @@ -294,7 +294,7 @@ warning: unclosed HTML tag `h1` warning: 2 warnings emitted ``` -## invalid_rust_codeblock +## invalid_rust_codeblocks This lint **warns by default**. It detects Rust code blocks in documentation examples that are invalid (e.g. empty, not parsable as Rust). For example: @@ -322,7 +322,7 @@ warning: Rust code block is empty 4 | | /// ``` | |_______^ | -= note: `#[warn(rustdoc::invalid_rust_codeblock)]` on by default += note: `#[warn(rustdoc::invalid_rust_codeblocks)]` on by default warning: Rust code block is empty --> src/lib.rs:8:5 diff --git a/src/librustdoc/lint.rs b/src/librustdoc/lint.rs index 597efed56e176..376c83b1a6ea8 100644 --- a/src/librustdoc/lint.rs +++ b/src/librustdoc/lint.rs @@ -158,13 +158,13 @@ declare_rustdoc_lint! { } declare_rustdoc_lint! { - /// The `invalid_rust_codeblock` lint detects Rust code blocks in + /// The `invalid_rust_codeblocks` lint detects Rust code blocks in /// documentation examples that are invalid (e.g. empty, not parsable as /// Rust code). This is a `rustdoc` only lint, see the documentation in the /// [rustdoc book]. /// - /// [rustdoc book]: ../../../rustdoc/lints.html#invalid_rust_codeblock - INVALID_RUST_CODEBLOCK, + /// [rustdoc book]: ../../../rustdoc/lints.html#invalid_rust_codeblocks + INVALID_RUST_CODEBLOCKS, Warn, "codeblock could not be parsed as valid Rust or is empty" } @@ -176,7 +176,7 @@ crate static RUSTDOC_LINTS: Lazy> = Lazy::new(|| { MISSING_DOC_CODE_EXAMPLES, PRIVATE_DOC_TESTS, INVALID_CODEBLOCK_ATTRIBUTES, - INVALID_RUST_CODEBLOCK, + INVALID_RUST_CODEBLOCKS, INVALID_HTML_TAGS, BARE_URLS, MISSING_CRATE_LEVEL_DOCS, diff --git a/src/librustdoc/passes/check_code_block_syntax.rs b/src/librustdoc/passes/check_code_block_syntax.rs index 4cc16de6808f3..72d3e9544c411 100644 --- a/src/librustdoc/passes/check_code_block_syntax.rs +++ b/src/librustdoc/passes/check_code_block_syntax.rs @@ -123,7 +123,7 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> { // All points of divergence have been handled earlier so this can be // done the same way whether the span is precise or not. self.cx.tcx.struct_span_lint_hir( - crate::lint::INVALID_RUST_CODEBLOCK, + crate::lint::INVALID_RUST_CODEBLOCKS, hir_id, sp, diag_builder, diff --git a/src/test/rustdoc-ui/ignore-block-help.stderr b/src/test/rustdoc-ui/ignore-block-help.stderr index b809ece86e76a..9c02ff11d19c4 100644 --- a/src/test/rustdoc-ui/ignore-block-help.stderr +++ b/src/test/rustdoc-ui/ignore-block-help.stderr @@ -7,7 +7,7 @@ LL | | /// let heart = '❤️'; LL | | /// ``` | |_______^ | - = note: `#[warn(rustdoc::invalid_rust_codeblock)]` on by default + = note: `#[warn(rustdoc::invalid_rust_codeblocks)]` on by default help: `ignore` code blocks require valid Rust code for syntax highlighting; mark blocks that do not contain Rust code as text: ```text --> $DIR/ignore-block-help.rs:3:5 | diff --git a/src/test/rustdoc-ui/invalid-syntax.stderr b/src/test/rustdoc-ui/invalid-syntax.stderr index 7fcb9a0e9a9ab..82eac9bd68b21 100644 --- a/src/test/rustdoc-ui/invalid-syntax.stderr +++ b/src/test/rustdoc-ui/invalid-syntax.stderr @@ -7,7 +7,7 @@ LL | | /// \__________pkt->size___________/ \_result->size_/ \__pkt->si LL | | /// ``` | |_______^ | - = note: `#[warn(rustdoc::invalid_rust_codeblock)]` on by default + = note: `#[warn(rustdoc::invalid_rust_codeblocks)]` on by default = note: error from rustc: unknown start of token: \ = note: error from rustc: unknown start of token: \ = note: error from rustc: unknown start of token: \ From 18f6922d8cf13625ae067b9061c017f44b4427ea Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 3 May 2021 22:19:49 -0400 Subject: [PATCH 16/17] Address review comments - Simplify boolean expression - Give an example of invalid syntax - Remove explanation of why code block is text --- .../src/borrow_check/region_infer/mod.rs | 2 +- src/doc/rustdoc/src/lints.md | 33 +++++++++++-------- .../passes/check_code_block_syntax.rs | 2 +- 3 files changed, 22 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs b/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs index 9dc2e3d292359..f4d78ac04cb02 100644 --- a/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs +++ b/compiler/rustc_mir/src/borrow_check/region_infer/mod.rs @@ -1241,7 +1241,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// it. However, it works pretty well in practice. In particular, /// this is needed to deal with projection outlives bounds like /// - /// ```text (internal compiler representation so lifetime syntax is invalid) + /// ```text /// >::Item: '1 /// ``` /// diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md index 9f1e1a5b88b2f..16b091eb255b0 100644 --- a/src/doc/rustdoc/src/lints.md +++ b/src/doc/rustdoc/src/lints.md @@ -305,30 +305,37 @@ examples that are invalid (e.g. empty, not parsable as Rust). For example: /// ```rust /// ``` /// -/// Unclosed code blocks (with and without the `rust` marker): +/// Invalid syntax in code blocks: /// /// ```rust -fn main() {} +/// '< +/// ``` +pub fn foo() {} ``` Which will give: ```text warning: Rust code block is empty ---> src/lib.rs:3:5 -| + --> lint.rs:3:5 + | 3 | /// ```rust -| _____^ + | _____^ 4 | | /// ``` -| |_______^ -| -= note: `#[warn(rustdoc::invalid_rust_codeblocks)]` on by default + | |_______^ + | + = note: `#[warn(rustdoc::invalid_rust_codeblocks)]` on by default -warning: Rust code block is empty ---> src/lib.rs:8:5 -| -8 | /// ```rust -| ^^^^^^^ +warning: could not parse code block as Rust code + --> lint.rs:8:5 + | +8 | /// ```rust + | _____^ +9 | | /// '< +10 | | /// ``` + | |_______^ + | + = note: error from rustc: unterminated character literal ``` ## bare_urls diff --git a/src/librustdoc/passes/check_code_block_syntax.rs b/src/librustdoc/passes/check_code_block_syntax.rs index 72d3e9544c411..0b94a74eeef97 100644 --- a/src/librustdoc/passes/check_code_block_syntax.rs +++ b/src/librustdoc/passes/check_code_block_syntax.rs @@ -48,7 +48,7 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> { .unwrap_or(false); let buffer = buffer.borrow(); - if !(buffer.has_errors || is_empty) { + if !buffer.has_errors && !is_empty { // No errors in a non-empty program. return; } From 587c50452f99b437fc4dc946d58d490094e7ef66 Mon Sep 17 00:00:00 2001 From: Joshua Nelson Date: Mon, 17 May 2021 23:31:48 -0400 Subject: [PATCH 17/17] Fix rebase conflicts --- src/librustdoc/passes/check_code_block_syntax.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustdoc/passes/check_code_block_syntax.rs b/src/librustdoc/passes/check_code_block_syntax.rs index 0b94a74eeef97..7ccfdf29041d2 100644 --- a/src/librustdoc/passes/check_code_block_syntax.rs +++ b/src/librustdoc/passes/check_code_block_syntax.rs @@ -53,7 +53,7 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> { return; } - let local_id = match item.def_id.as_local() { + let local_id = match item.def_id.as_real().and_then(|x| x.as_local()) { Some(id) => id, // We don't need to check the syntax for other crates so returning // without doing anything should not be a problem.