mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-30 00:23:41 +00:00
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:
commit
a5560a6a90
@ -32,7 +32,6 @@ pub enum UnsafetyViolationDetails {
|
||||
UseOfInlineAssembly,
|
||||
InitializingTypeWith,
|
||||
CastOfPointerToInt,
|
||||
BorrowOfPackedField,
|
||||
UseOfMutableStatic,
|
||||
UseOfExternStatic,
|
||||
DerefOfRawPointer,
|
||||
@ -64,11 +63,6 @@ impl UnsafetyViolationDetails {
|
||||
CastOfPointerToInt => {
|
||||
("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 => (
|
||||
"use of mutable static",
|
||||
"mutable statics can be mutated by multiple threads: aliasing violations or data \
|
||||
|
@ -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
|
||||
/// <T as Foo<'0>>::Item: '1
|
||||
/// ```
|
||||
///
|
||||
|
@ -64,38 +64,30 @@ impl<'tcx> UnsafetyVisitor<'tcx> {
|
||||
SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {}
|
||||
SafetyContext::UnsafeFn => {
|
||||
// unsafe_op_in_unsafe_fn is disallowed
|
||||
if kind == BorrowOfPackedField {
|
||||
// FIXME handle borrows of packed fields
|
||||
} else {
|
||||
struct_span_err!(
|
||||
self.tcx.sess,
|
||||
span,
|
||||
E0133,
|
||||
"{} is unsafe and requires unsafe block",
|
||||
description,
|
||||
)
|
||||
.span_label(span, description)
|
||||
.note(note)
|
||||
.emit();
|
||||
}
|
||||
struct_span_err!(
|
||||
self.tcx.sess,
|
||||
span,
|
||||
E0133,
|
||||
"{} is unsafe and requires unsafe block",
|
||||
description,
|
||||
)
|
||||
.span_label(span, description)
|
||||
.note(note)
|
||||
.emit();
|
||||
}
|
||||
SafetyContext::Safe => {
|
||||
if kind == BorrowOfPackedField {
|
||||
// FIXME handle borrows of packed fields
|
||||
} else {
|
||||
let fn_sugg = if unsafe_op_in_unsafe_fn_allowed { " function or" } else { "" };
|
||||
struct_span_err!(
|
||||
self.tcx.sess,
|
||||
span,
|
||||
E0133,
|
||||
"{} is unsafe and requires unsafe{} block",
|
||||
description,
|
||||
fn_sugg,
|
||||
)
|
||||
.span_label(span, description)
|
||||
.note(note)
|
||||
.emit();
|
||||
}
|
||||
let fn_sugg = if unsafe_op_in_unsafe_fn_allowed { " function or" } else { "" };
|
||||
struct_span_err!(
|
||||
self.tcx.sess,
|
||||
span,
|
||||
E0133,
|
||||
"{} is unsafe and requires unsafe{} block",
|
||||
description,
|
||||
fn_sugg,
|
||||
)
|
||||
.span_label(span, description)
|
||||
.note(note)
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -203,8 +195,6 @@ enum UnsafeOpKind {
|
||||
#[allow(dead_code)] // FIXME
|
||||
CastOfPointerToInt,
|
||||
#[allow(dead_code)] // FIXME
|
||||
BorrowOfPackedField,
|
||||
#[allow(dead_code)] // FIXME
|
||||
UseOfMutableStatic,
|
||||
#[allow(dead_code)] // FIXME
|
||||
UseOfExternStatic,
|
||||
@ -244,11 +234,6 @@ impl UnsafeOpKind {
|
||||
CastOfPointerToInt => {
|
||||
("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 => (
|
||||
"use of mutable static",
|
||||
"mutable statics can be mutated by multiple threads: aliasing violations or data \
|
||||
|
@ -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;
|
||||
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)) => {
|
||||
@ -180,6 +180,127 @@ impl LanguageItemCollector<'tcx> {
|
||||
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.
|
||||
|
@ -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
|
||||
|
@ -686,17 +686,36 @@ 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::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 +732,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 +749,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 +764,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()`,
|
||||
|
@ -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));
|
||||
|
@ -303,8 +303,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
opt_input_types: Option<&[Ty<'tcx>]>,
|
||||
) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> {
|
||||
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<input_tys>`
|
||||
|
@ -1187,3 +1187,14 @@ 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<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 }
|
||||
})
|
||||
}
|
||||
|
@ -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;
|
||||
@ -795,6 +795,23 @@ 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 !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 opname = Ident::with_dummy_span(opname);
|
||||
self.lookup_method_in_trait(span, opname, trait_did, lhs_ty, Some(other_tys))
|
||||
|
@ -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;
|
||||
@ -153,6 +153,21 @@ 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 !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| {
|
||||
self.lookup_method_in_trait(
|
||||
span,
|
||||
@ -177,6 +192,21 @@ 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 !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| {
|
||||
self.lookup_method_in_trait(
|
||||
span,
|
||||
|
@ -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: [], ....) -> {
|
||||
|
@ -72,10 +72,32 @@ impl<A: Clone> Iterator for Repeat<A> {
|
||||
fn next(&mut self) -> Option<A> {
|
||||
Some(self.element.clone())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(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")]
|
||||
@ -84,6 +106,19 @@ impl<A: Clone> DoubleEndedIterator for Repeat<A> {
|
||||
fn next_back(&mut self) -> Option<A> {
|
||||
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")]
|
||||
|
@ -294,6 +294,50 @@ warning: unclosed HTML tag `h1`
|
||||
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
|
||||
|
||||
This lint is **warn-by-default**. It detects URLs which are not links.
|
||||
|
@ -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.
|
||||
|
@ -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, "<details class=\"rustdoc-toggle\" open><summary>");
|
||||
write!(w, "<h3 id=\"{id}\" class=\"method\"><code>", id = id,);
|
||||
render_assoc_item(w, m, AssocItemLink::Anchor(Some(&id)), ItemType::Impl, cx);
|
||||
w.write_str("</code>");
|
||||
render_stability_since(w, m, t, cx.tcx());
|
||||
write_srclink(cx, m, w);
|
||||
w.write_str("</h3>");
|
||||
w.write_str("</h3></summary>");
|
||||
document(w, cx, m, Some(t));
|
||||
}
|
||||
|
||||
|
@ -157,6 +157,18 @@ declare_rustdoc_lint! {
|
||||
"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(|| {
|
||||
vec![
|
||||
BROKEN_INTRA_DOC_LINKS,
|
||||
@ -164,6 +176,7 @@ crate static RUSTDOC_LINTS: Lazy<Vec<&'static Lint>> = Lazy::new(|| {
|
||||
MISSING_DOC_CODE_EXAMPLES,
|
||||
PRIVATE_DOC_TESTS,
|
||||
INVALID_CODEBLOCK_ATTRIBUTES,
|
||||
INVALID_RUST_CODEBLOCKS,
|
||||
INVALID_HTML_TAGS,
|
||||
BARE_URLS,
|
||||
MISSING_CRATE_LEVEL_DOCS,
|
||||
|
@ -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,55 +48,68 @@ 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)
|
||||
} else {
|
||||
("Rust code block is empty", false)
|
||||
};
|
||||
if !buffer.has_errors && !is_empty {
|
||||
// No errors in a non-empty program.
|
||||
return;
|
||||
}
|
||||
|
||||
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 sp = sp.from_inner(InnerSpan::new(0, 3));
|
||||
let hir_id = self.cx.tcx.hir().local_def_id_to_hir_id(local_id);
|
||||
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(
|
||||
sp,
|
||||
"mark blocks that do not contain Rust code as text",
|
||||
sp.from_inner(InnerSpan::new(0, 3)),
|
||||
explanation,
|
||||
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 {
|
||||
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() {
|
||||
@ -103,7 +117,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_CODEBLOCKS,
|
||||
hir_id,
|
||||
sp,
|
||||
diag_builder,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -7,11 +7,13 @@ LL | | /// let heart = '❤️';
|
||||
LL | | /// ```
|
||||
| |_______^
|
||||
|
|
||||
= 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
|
||||
= 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
|
||||
|
|
||||
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
|
||||
|
||||
|
@ -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
|
||||
/// ```
|
||||
|
@ -7,6 +7,7 @@ LL | | /// \__________pkt->size___________/ \_result->size_/ \__pkt->si
|
||||
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: \
|
||||
@ -90,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 = "```"]
|
||||
|
7
src/test/rustdoc/toggle-trait-fn.rs
Normal file
7
src/test/rustdoc/toggle-trait-fn.rs
Normal 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() -> ();
|
||||
}
|
14
src/test/ui/async-await/issue-73741-type-err.rs
Normal file
14
src/test/ui/async-await/issue-73741-type-err.rs
Normal 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() {}
|
11
src/test/ui/async-await/issue-73741-type-err.stderr
Normal file
11
src/test/ui/async-await/issue-73741-type-err.stderr
Normal 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`.
|
20
src/test/ui/lang-items/wrong-number-generic-args-add.rs
Normal file
20
src/test/ui/lang-items/wrong-number-generic-args-add.rs
Normal 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]
|
||||
}
|
20
src/test/ui/lang-items/wrong-number-generic-args-add.stderr
Normal file
20
src/test/ui/lang-items/wrong-number-generic-args-add.stderr
Normal 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`.
|
19
src/test/ui/lang-items/wrong-number-generic-args-index.rs
Normal file
19
src/test/ui/lang-items/wrong-number-generic-args-index.rs
Normal 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]
|
||||
}
|
@ -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`.
|
@ -21,10 +21,10 @@ LL | fn foo<X: Trait>(_: 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 mutably borrowing here: `&mut s`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
13
src/test/ui/suggestions/issue-84973-2.rs
Normal file
13
src/test/ui/suggestions/issue-84973-2.rs
Normal 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]
|
||||
}
|
15
src/test/ui/suggestions/issue-84973-2.stderr
Normal file
15
src/test/ui/suggestions/issue-84973-2.stderr
Normal 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`.
|
29
src/test/ui/suggestions/issue-84973-blacklist.rs
Normal file
29
src/test/ui/suggestions/issue-84973-blacklist.rs
Normal 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]
|
||||
}
|
64
src/test/ui/suggestions/issue-84973-blacklist.stderr
Normal file
64
src/test/ui/suggestions/issue-84973-blacklist.stderr
Normal 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`.
|
12
src/test/ui/suggestions/issue-84973-negative.rs
Normal file
12
src/test/ui/suggestions/issue-84973-negative.rs
Normal 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]
|
||||
}
|
24
src/test/ui/suggestions/issue-84973-negative.stderr
Normal file
24
src/test/ui/suggestions/issue-84973-negative.stderr
Normal 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`.
|
33
src/test/ui/suggestions/issue-84973.rs
Normal file
33
src/test/ui/suggestions/issue-84973.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
15
src/test/ui/suggestions/issue-84973.stderr
Normal file
15
src/test/ui/suggestions/issue-84973.stderr
Normal 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`.
|
Loading…
Reference in New Issue
Block a user