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,
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 \

View File

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

View File

@ -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 \

View File

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

View File

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

View File

@ -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()`,

View File

@ -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));

View File

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

View File

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

View File

@ -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))

View File

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

View File

@ -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: [], ....) -> {

View File

@ -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")]

View File

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

View File

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

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);
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));
}

View File

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

View File

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

View File

@ -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;

View File

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

View File

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

View File

@ -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 = "```"]

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

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