1
Fork 0

Auto merge of #85437 - GuillaumeGomez:rollup-3jcirty, r=GuillaumeGomez

Rollup of 7 pull requests

Successful merges:

 - #84587 (rustdoc: Make "rust code block is empty" and "could not parse code block" warnings a lint (`INVALID_RUST_CODEBLOCKS`))
 - #85280 (Toggle-wrap items differently than top-doc.)
 - #85338 (Implement more Iterator methods on core::iter::Repeat)
 - #85339 (Report an error if a lang item has the wrong number of generic arguments)
 - #85369 (Suggest borrowing if a trait implementation is found for &/&mut <type>)
 - #85393 (Suppress spurious errors inside `async fn`)
 - #85415 (Clean up remnants of BorrowOfPackedField)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2021-05-18 14:48:53 +00:00
commit a5560a6a90
42 changed files with 798 additions and 131 deletions

View file

@ -32,7 +32,6 @@ pub enum UnsafetyViolationDetails {
UseOfInlineAssembly, UseOfInlineAssembly,
InitializingTypeWith, InitializingTypeWith,
CastOfPointerToInt, CastOfPointerToInt,
BorrowOfPackedField,
UseOfMutableStatic, UseOfMutableStatic,
UseOfExternStatic, UseOfExternStatic,
DerefOfRawPointer, DerefOfRawPointer,
@ -64,11 +63,6 @@ impl UnsafetyViolationDetails {
CastOfPointerToInt => { CastOfPointerToInt => {
("cast of pointer to int", "casting pointers to integers in constants") ("cast of pointer to int", "casting pointers to integers in constants")
} }
BorrowOfPackedField => (
"borrow of packed field",
"fields of packed structs might be misaligned: dereferencing a misaligned pointer \
or even just creating a misaligned reference is undefined behavior",
),
UseOfMutableStatic => ( UseOfMutableStatic => (
"use of mutable static", "use of mutable static",
"mutable statics can be mutated by multiple threads: aliasing violations or data \ "mutable statics can be mutated by multiple threads: aliasing violations or data \

View file

@ -1241,7 +1241,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
/// it. However, it works pretty well in practice. In particular, /// it. However, it works pretty well in practice. In particular,
/// this is needed to deal with projection outlives bounds like /// this is needed to deal with projection outlives bounds like
/// ///
/// ```ignore (internal compiler representation so lifetime syntax is invalid) /// ```text
/// <T as Foo<'0>>::Item: '1 /// <T as Foo<'0>>::Item: '1
/// ``` /// ```
/// ///

View file

@ -64,38 +64,30 @@ impl<'tcx> UnsafetyVisitor<'tcx> {
SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {} SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {}
SafetyContext::UnsafeFn => { SafetyContext::UnsafeFn => {
// unsafe_op_in_unsafe_fn is disallowed // unsafe_op_in_unsafe_fn is disallowed
if kind == BorrowOfPackedField { struct_span_err!(
// FIXME handle borrows of packed fields self.tcx.sess,
} else { span,
struct_span_err!( E0133,
self.tcx.sess, "{} is unsafe and requires unsafe block",
span, description,
E0133, )
"{} is unsafe and requires unsafe block", .span_label(span, description)
description, .note(note)
) .emit();
.span_label(span, description)
.note(note)
.emit();
}
} }
SafetyContext::Safe => { SafetyContext::Safe => {
if kind == BorrowOfPackedField { let fn_sugg = if unsafe_op_in_unsafe_fn_allowed { " function or" } else { "" };
// FIXME handle borrows of packed fields struct_span_err!(
} else { self.tcx.sess,
let fn_sugg = if unsafe_op_in_unsafe_fn_allowed { " function or" } else { "" }; span,
struct_span_err!( E0133,
self.tcx.sess, "{} is unsafe and requires unsafe{} block",
span, description,
E0133, fn_sugg,
"{} is unsafe and requires unsafe{} block", )
description, .span_label(span, description)
fn_sugg, .note(note)
) .emit();
.span_label(span, description)
.note(note)
.emit();
}
} }
} }
} }
@ -203,8 +195,6 @@ enum UnsafeOpKind {
#[allow(dead_code)] // FIXME #[allow(dead_code)] // FIXME
CastOfPointerToInt, CastOfPointerToInt,
#[allow(dead_code)] // FIXME #[allow(dead_code)] // FIXME
BorrowOfPackedField,
#[allow(dead_code)] // FIXME
UseOfMutableStatic, UseOfMutableStatic,
#[allow(dead_code)] // FIXME #[allow(dead_code)] // FIXME
UseOfExternStatic, UseOfExternStatic,
@ -244,11 +234,6 @@ impl UnsafeOpKind {
CastOfPointerToInt => { CastOfPointerToInt => {
("cast of pointer to int", "casting pointers to integers in constants") ("cast of pointer to int", "casting pointers to integers in constants")
} }
BorrowOfPackedField => (
"borrow of packed field",
"fields of packed structs might be misaligned: dereferencing a misaligned pointer \
or even just creating a misaligned reference is undefined behavior",
),
UseOfMutableStatic => ( UseOfMutableStatic => (
"use of mutable static", "use of mutable static",
"mutable statics can be mutated by multiple threads: aliasing violations or data \ "mutable statics can be mutated by multiple threads: aliasing violations or data \

View file

@ -13,12 +13,13 @@ use crate::weak_lang_items;
use rustc_middle::middle::cstore::ExternCrate; use rustc_middle::middle::cstore::ExternCrate;
use rustc_middle::ty::TyCtxt; 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 as hir;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::itemlikevisit::ItemLikeVisitor; use rustc_hir::itemlikevisit::ItemLikeVisitor;
use rustc_hir::lang_items::{extract, ITEM_REFS}; use rustc_hir::lang_items::{extract, ITEM_REFS};
use rustc_hir::{HirId, LangItem, LanguageItems, Target}; use rustc_hir::{HirId, LangItem, LanguageItems, Target};
use rustc_span::Span;
use rustc_middle::ty::query::Providers; use rustc_middle::ty::query::Providers;
@ -61,8 +62,7 @@ impl LanguageItemCollector<'tcx> {
match ITEM_REFS.get(&value).cloned() { match ITEM_REFS.get(&value).cloned() {
// Known lang item with attribute on correct target. // Known lang item with attribute on correct target.
Some((item_index, expected_target)) if actual_target == expected_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_extended(item_index, hir_id, span);
self.collect_item(item_index, def_id.to_def_id());
} }
// Known lang item with attribute on incorrect target. // Known lang item with attribute on incorrect target.
Some((_, expected_target)) => { Some((_, expected_target)) => {
@ -180,6 +180,127 @@ impl LanguageItemCollector<'tcx> {
self.items.groups[group as usize].push(item_def_id); self.items.groups[group as usize].push(item_def_id);
} }
} }
// 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();
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 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
| 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
// Miscellaneous
| LangItem::Unsize
| LangItem::CoerceUnsized
| LangItem::DispatchFromDyn
| LangItem::Fn
| LangItem::FnMut
| LangItem::FnOnce
| LangItem::Generator
| LangItem::PartialEq
| LangItem::PartialOrd
=> Some(1),
// 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,
};
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. /// Traverses and collects all the lang items in all crates.

View file

@ -46,6 +46,7 @@ pub struct OpaqueTypeDecl<'tcx> {
/// type Foo = impl Baz; /// type Foo = impl Baz;
/// fn bar() -> Foo { /// fn bar() -> Foo {
/// // ^^^ This is the span we are looking for! /// // ^^^ This is the span we are looking for!
/// }
/// ``` /// ```
/// ///
/// In cases where the fn returns `(impl Trait, impl Trait)` or /// In cases where the fn returns `(impl Trait, impl Trait)` or

View file

@ -686,17 +686,36 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
return false; 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::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 span = obligation.cause.span;
let param_env = obligation.param_env; let param_env = obligation.param_env;
let trait_ref = trait_ref.skip_binder(); let trait_ref = trait_ref.skip_binder();
if let ObligationCauseCode::ImplDerivedObligation(obligation) = &obligation.cause.code { let found_ty = trait_ref.self_ty();
// Try to apply the original trait binding obligation by borrowing. let found_ty_str = found_ty.to_string();
let self_ty = trait_ref.self_ty(); let imm_borrowed_found_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, found_ty);
let found = self_ty.to_string(); let imm_substs = self.tcx.mk_substs_trait(imm_borrowed_found_ty, &[]);
let new_self_ty = self.tcx.mk_imm_ref(self.tcx.lifetimes.re_static, self_ty); let mut_borrowed_found_ty = self.tcx.mk_mut_ref(self.tcx.lifetimes.re_static, found_ty);
let substs = self.tcx.mk_substs_trait(new_self_ty, &[]); let mut_substs = self.tcx.mk_substs_trait(mut_borrowed_found_ty, &[]);
let new_trait_ref = ty::TraitRef::new(obligation.parent_trait_ref.def_id(), substs);
// 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( let new_obligation = Obligation::new(
ObligationCause::dummy(), ObligationCause::dummy(),
param_env, param_env,
@ -713,8 +732,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
let msg = format!( let msg = format!(
"the trait bound `{}: {}` is not satisfied", "the trait bound `{}: {}` is not satisfied",
found, found_ty_str,
obligation.parent_trait_ref.skip_binder().print_only_trait_path(), expected_trait_ref.print_only_trait_path(),
); );
if has_custom_message { if has_custom_message {
err.note(&msg); err.note(&msg);
@ -730,7 +749,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
span, span,
&format!( &format!(
"expected an implementor of trait `{}`", "expected an implementor of trait `{}`",
obligation.parent_trait_ref.skip_binder().print_only_trait_path(), expected_trait_ref.print_only_trait_path(),
), ),
); );
@ -745,16 +764,52 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
err.span_suggestion( err.span_suggestion(
span, span,
"consider borrowing here", &format!(
format!("&{}", snippet), "consider{} borrowing here",
if mtbl { " mutably" } else { "" }
),
format!("&{}{}", if mtbl { "mut " } else { "" }, snippet),
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
); );
} }
return true; 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()`, /// Whenever references are used by mistake, like `for (i, e) in &vec.iter().enumerate()`,

View file

@ -89,19 +89,31 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
if let Some((unresolved_type, unresolved_type_span)) = if let Some((unresolved_type, unresolved_type_span)) =
self.fcx.unresolved_type_vars(&ty) 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 // If unresolved type isn't a ty_var then unresolved_type_span is None
let span = self let span = self
.prev_unresolved_span .prev_unresolved_span
.unwrap_or_else(|| unresolved_type_span.unwrap_or(source_span)); .unwrap_or_else(|| unresolved_type_span.unwrap_or(source_span));
self.fcx
.need_type_info_err_in_generator(self.kind, span, unresolved_type) // If we encounter an int/float variable, then inference fallback didn't
.span_note(yield_data.span, &*note) // finish due to some other error. Don't emit spurious additional errors.
.emit(); 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 { } else {
// Insert the type into the ordered set. // Insert the type into the ordered set.
let scope_span = scope.map(|s| s.span(self.fcx.tcx, self.region_scope_tree)); let scope_span = scope.map(|s| s.span(self.fcx.tcx, self.region_scope_tree));

View file

@ -303,8 +303,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
opt_input_types: Option<&[Ty<'tcx>]>, opt_input_types: Option<&[Ty<'tcx>]>,
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> { ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
debug!( debug!(
"lookup_in_trait_adjusted(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 self_ty, m_name, trait_def_id, opt_input_types
); );
// Construct a trait-reference `self_ty : Trait<input_tys>` // Construct a trait-reference `self_ty : Trait<input_tys>`

View file

@ -1187,3 +1187,14 @@ fn fatally_break_rust(sess: &Session) {
fn potentially_plural_count(count: usize, word: &str) -> String { fn potentially_plural_count(count: usize, word: &str) -> String {
format!("{} {}{}", count, word, pluralize!(count)) format!("{} {}{}", count, word, pluralize!(count))
} }
fn has_expected_num_generic_args<'tcx>(
tcx: TyCtxt<'tcx>,
trait_did: Option<DefId>,
expected: usize,
) -> bool {
trait_did.map_or(true, |trait_did| {
let generics = tcx.generics_of(trait_did);
generics.count() == expected + if generics.has_self { 1 } else { 0 }
})
}

View file

@ -1,7 +1,7 @@
//! Code related to processing overloaded binary and unary operators. //! Code related to processing overloaded binary and unary operators.
use super::method::MethodCallee; use super::method::MethodCallee;
use super::FnCtxt; use super::{has_expected_num_generic_args, FnCtxt};
use rustc_ast as ast; use rustc_ast as ast;
use rustc_errors::{self, struct_span_err, Applicability, DiagnosticBuilder}; use rustc_errors::{self, struct_span_err, Applicability, DiagnosticBuilder};
use rustc_hir as hir; use rustc_hir as hir;
@ -795,6 +795,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
lhs_ty, op, opname, trait_did 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 !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,
},
) {
return Err(());
}
let method = trait_did.and_then(|trait_did| { let method = trait_did.and_then(|trait_did| {
let opname = Ident::with_dummy_span(opname); let opname = Ident::with_dummy_span(opname);
self.lookup_method_in_trait(span, opname, trait_did, lhs_ty, Some(other_tys)) self.lookup_method_in_trait(span, opname, trait_did, lhs_ty, Some(other_tys))

View file

@ -1,5 +1,5 @@
use crate::check::method::MethodCallee; 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_hir as hir;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::InferOk; use rustc_infer::infer::InferOk;
@ -153,6 +153,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
PlaceOp::Deref => (self.tcx.lang_items().deref_trait(), sym::deref), PlaceOp::Deref => (self.tcx.lang_items().deref_trait(), sym::deref),
PlaceOp::Index => (self.tcx.lang_items().index_trait(), sym::index), 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 !has_expected_num_generic_args(
self.tcx,
imm_tr,
match op {
PlaceOp::Deref => 0,
PlaceOp::Index => 1,
},
) {
return None;
}
imm_tr.and_then(|trait_did| { imm_tr.and_then(|trait_did| {
self.lookup_method_in_trait( self.lookup_method_in_trait(
span, span,
@ -177,6 +192,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
PlaceOp::Deref => (self.tcx.lang_items().deref_mut_trait(), sym::deref_mut), PlaceOp::Deref => (self.tcx.lang_items().deref_mut_trait(), sym::deref_mut),
PlaceOp::Index => (self.tcx.lang_items().index_mut_trait(), sym::index_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 !has_expected_num_generic_args(
self.tcx,
mut_tr,
match op {
PlaceOp::Deref => 0,
PlaceOp::Index => 1,
},
) {
return None;
}
mut_tr.and_then(|trait_did| { mut_tr.and_then(|trait_did| {
self.lookup_method_in_trait( self.lookup_method_in_trait(
span, span,

View file

@ -323,7 +323,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// ///
/// InferBorrowKind results in a structure like this: /// InferBorrowKind results in a structure like this:
/// ///
/// ``` /// ```text
/// { /// {
/// Place(base: hir_id_s, projections: [], ....) -> { /// Place(base: hir_id_s, projections: [], ....) -> {
/// capture_kind_expr: hir_id_L5, /// capture_kind_expr: hir_id_L5,
@ -348,7 +348,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// ``` /// ```
/// ///
/// After the min capture analysis, we get: /// After the min capture analysis, we get:
/// ``` /// ```text
/// { /// {
/// hir_id_s -> [ /// hir_id_s -> [
/// Place(base: hir_id_s, projections: [], ....) -> { /// Place(base: hir_id_s, projections: [], ....) -> {

View file

@ -72,10 +72,32 @@ impl<A: Clone> Iterator for Repeat<A> {
fn next(&mut self) -> Option<A> { fn next(&mut self) -> Option<A> {
Some(self.element.clone()) Some(self.element.clone())
} }
#[inline] #[inline]
fn size_hint(&self) -> (usize, Option<usize>) { fn size_hint(&self) -> (usize, Option<usize>) {
(usize::MAX, None) (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<A> {
let _ = n;
Some(self.element.clone())
}
fn last(self) -> Option<A> {
loop {}
}
fn count(self) -> usize {
loop {}
}
} }
#[stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")]
@ -84,6 +106,19 @@ impl<A: Clone> DoubleEndedIterator for Repeat<A> {
fn next_back(&mut self) -> Option<A> { fn next_back(&mut self) -> Option<A> {
Some(self.element.clone()) 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<A> {
let _ = n;
Some(self.element.clone())
}
} }
#[stable(feature = "fused", since = "1.26.0")] #[stable(feature = "fused", since = "1.26.0")]

View file

@ -294,6 +294,50 @@ warning: unclosed HTML tag `h1`
warning: 2 warnings emitted warning: 2 warnings emitted
``` ```
## 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:
```rust
/// Empty code blocks (with and without the `rust` marker):
///
/// ```rust
/// ```
///
/// Invalid syntax in code blocks:
///
/// ```rust
/// '<
/// ```
pub fn foo() {}
```
Which will give:
```text
warning: Rust code block is empty
--> lint.rs:3:5
|
3 | /// ```rust
| _____^
4 | | /// ```
| |_______^
|
= note: `#[warn(rustdoc::invalid_rust_codeblocks)]` on by default
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 ## bare_urls
This lint is **warn-by-default**. It detects URLs which are not links. This lint is **warn-by-default**. It detects URLs which are not links.

View file

@ -509,7 +509,11 @@ fn document(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, parent: Option
info!("Documenting {}", name); info!("Documenting {}", name);
} }
document_item_info(w, cx, item, parent); 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. /// Render md_text as markdown.

View file

@ -578,12 +578,13 @@ fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Tra
info!("Documenting {} on {:?}", name, t.name); info!("Documenting {} on {:?}", name, t.name);
let item_type = m.type_(); let item_type = m.type_();
let id = cx.derive_id(format!("{}.{}", item_type, name)); let id = cx.derive_id(format!("{}.{}", item_type, name));
write!(w, "<details class=\"rustdoc-toggle\" open><summary>");
write!(w, "<h3 id=\"{id}\" class=\"method\"><code>", id = id,); write!(w, "<h3 id=\"{id}\" class=\"method\"><code>", id = id,);
render_assoc_item(w, m, AssocItemLink::Anchor(Some(&id)), ItemType::Impl, cx); render_assoc_item(w, m, AssocItemLink::Anchor(Some(&id)), ItemType::Impl, cx);
w.write_str("</code>"); w.write_str("</code>");
render_stability_since(w, m, t, cx.tcx()); render_stability_since(w, m, t, cx.tcx());
write_srclink(cx, m, w); write_srclink(cx, m, w);
w.write_str("</h3>"); w.write_str("</h3></summary>");
document(w, cx, m, Some(t)); document(w, cx, m, Some(t));
} }

View file

@ -157,6 +157,18 @@ declare_rustdoc_lint! {
"detects URLs that are not hyperlinks" "detects URLs that are not hyperlinks"
} }
declare_rustdoc_lint! {
/// 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_codeblocks
INVALID_RUST_CODEBLOCKS,
Warn,
"codeblock could not be parsed as valid Rust or is empty"
}
crate static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| { crate static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| {
vec![ vec![
BROKEN_INTRA_DOC_LINKS, BROKEN_INTRA_DOC_LINKS,
@ -164,6 +176,7 @@ crate static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| {
MISSING_DOC_CODE_EXAMPLES, MISSING_DOC_CODE_EXAMPLES,
PRIVATE_DOC_TESTS, PRIVATE_DOC_TESTS,
INVALID_CODEBLOCK_ATTRIBUTES, INVALID_CODEBLOCK_ATTRIBUTES,
INVALID_RUST_CODEBLOCKS,
INVALID_HTML_TAGS, INVALID_HTML_TAGS,
BARE_URLS, BARE_URLS,
MISSING_CRATE_LEVEL_DOCS, MISSING_CRATE_LEVEL_DOCS,

View file

@ -1,5 +1,6 @@
use rustc_data_structures::sync::{Lock, Lrc}; use rustc_data_structures::sync::{Lock, Lrc};
use rustc_errors::{emitter::Emitter, Applicability, Diagnostic, Handler}; use rustc_errors::{emitter::Emitter, Applicability, Diagnostic, Handler};
use rustc_middle::lint::LintDiagnosticBuilder;
use rustc_parse::parse_stream_from_source_str; use rustc_parse::parse_stream_from_source_str;
use rustc_session::parse::ParseSess; use rustc_session::parse::ParseSess;
use rustc_span::source_map::{FilePathMapping, SourceMap}; use rustc_span::source_map::{FilePathMapping, SourceMap};
@ -47,55 +48,68 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> {
.unwrap_or(false); .unwrap_or(false);
let buffer = buffer.borrow(); let buffer = buffer.borrow();
if buffer.has_errors || is_empty { if !buffer.has_errors && !is_empty {
let mut diag = if let Some(sp) = super::source_span_for_markdown_range( // No errors in a non-empty program.
self.cx.tcx, return;
&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)
} else {
("Rust code block is empty", false)
};
let mut diag = self.cx.sess().struct_span_warn(sp, warning_message); 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.
None => return,
};
if code_block.syntax.is_none() && code_block.is_fenced { let hir_id = self.cx.tcx.hir().local_def_id_to_hir_id(local_id);
let sp = sp.from_inner(InnerSpan::new(0, 3)); 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.
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 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( diag.span_suggestion(
sp, sp.from_inner(InnerSpan::new(0, 3)),
"mark blocks that do not contain Rust code as text", explanation,
String::from("```text"), String::from("```text"),
Applicability::MachineApplicable, 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,
);
} }
} else if empty_block || is_ignore {
diag diag.help(&format!("{}: ```text", explanation));
} 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 {
diag.help("mark blocks that do not contain Rust code as text: ```text");
}
diag
};
// FIXME(#67563): Provide more context for these errors by displaying the spans inline. // FIXME(#67563): Provide more context for these errors by displaying the spans inline.
for message in buffer.messages.iter() { for message in buffer.messages.iter() {
@ -103,7 +117,17 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> {
} }
diag.emit(); 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_CODEBLOCKS,
hir_id,
sp,
diag_builder,
);
} }
} }

View file

@ -3,5 +3,8 @@
/// ```ignore (to-prevent-tidy-error) /// ```ignore (to-prevent-tidy-error)
/// let heart = '❤️'; /// 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; pub struct X;

View file

@ -7,11 +7,13 @@ LL | | /// let heart = '❤️';
LL | | /// ``` LL | | /// ```
| |_______^ | |_______^
| |
= note: error from rustc: character literal may only contain one codepoint = 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 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 | /// ```text,ignore (to-prevent-tidy-error) LL | /// ```ignore (to-prevent-tidy-error)
| ^^^^^^^^ | ^^^
= note: error from rustc: character literal may only contain one codepoint
warning: 1 warning emitted warning: 1 warning emitted

View file

@ -71,7 +71,7 @@ pub fn blargh() {}
/// \_ /// \_
#[doc = "```"] #[doc = "```"]
pub fn crazy_attrs() {} pub fn crazy_attrs() {}
//~^^^^ WARNING doc comment contains an invalid Rust code block //~^^^^ WARNING could not parse code block
/// ```rust /// ```rust
/// ``` /// ```

View file

@ -7,6 +7,7 @@ LL | | /// \__________pkt->size___________/ \_result->size_/ \__pkt->si
LL | | /// ``` LL | | /// ```
| |_______^ | |_______^
| |
= 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: \ = note: error from rustc: unknown start of token: \
= note: error from rustc: unknown start of token: \ = note: error from rustc: unknown start of token: \
@ -90,7 +91,7 @@ LL | | /// ```
| |
= note: error from rustc: unknown start of token: \ = 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 --> $DIR/invalid-syntax.rs:70:1
| |
LL | / #[doc = "```"] LL | / #[doc = "```"]

View file

@ -0,0 +1,7 @@
#![crate_name = "foo"]
// @has foo/trait.Foo.html
// @has - '//details[@class="rustdoc-toggle"]//code' 'bar'
pub trait Foo {
fn bar() -> ();
}

View file

@ -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() {}

View file

@ -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`.

View file

@ -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]
}

View file

@ -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`.

View file

@ -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]
}

View file

@ -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`.

View file

@ -21,10 +21,10 @@ LL | fn foo<X: Trait>(_: X) {}
| ----- required by this bound in `foo` | ----- required by this bound in `foo`
... ...
LL | foo(s); LL | foo(s);
| ^ the trait `Trait` is not implemented for `S` | ^
| | |
= help: the following implementations were found: | expected an implementor of trait `Trait`
<&'a mut S as Trait> | help: consider mutably borrowing here: `&mut s`
error: aborting due to 2 previous errors error: aborting due to 2 previous errors

View file

@ -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<T: Tr>(i: T) {}
fn main() {
let a: i32 = 32;
foo(a);
//~^ ERROR: the trait bound `i32: Tr` is not satisfied [E0277]
}

View file

@ -0,0 +1,15 @@
error[E0277]: the trait bound `i32: Tr` is not satisfied
--> $DIR/issue-84973-2.rs:11:9
|
LL | fn foo<T: Tr>(i: T) {}
| -- required by this bound in `foo`
...
LL | foo(a);
| ^
| |
| expected an implementor of trait `Tr`
| help: consider mutably borrowing here: `&mut a`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.

View file

@ -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: Copy>(t: T) {}
fn f_clone<T: Clone>(t: T) {}
fn f_unpin<T: Unpin>(t: T) {}
fn f_sized<T: Sized>(t: T) {}
fn f_send<T: 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]
}

View file

@ -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: 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: 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: 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: 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: 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`.

View file

@ -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: Tr>(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]
}

View file

@ -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: Tr>(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: Tr>(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`.

View file

@ -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,
}
}
}

View file

@ -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`.