Auto merge of #106432 - compiler-errors:rollup-lzj0lnp, r=compiler-errors
Rollup of 8 pull requests Successful merges: - #104748 (Ensure `lld` is supported with `download-ci-llvm`) - #105541 (Simplify some iterator combinators) - #106045 (default OOM handler: use non-unwinding panic, to match std handler) - #106157 (Don't trim path for `unsafe_op_in_unsafe_fn` lints) - #106353 (Reduce spans for `unsafe impl` errors) - #106381 (Jsondoclint: Add `--verbose` and `--json-output` options) - #106411 (rustdoc: remove legacy font-feature-settings CSS) - #106414 (Add cuviper to the review rotation for libs) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
c361616c3c
19
Cargo.lock
19
Cargo.lock
@ -597,7 +597,7 @@ checksum = "23b71c3ce99b7611011217b366d923f1d0a7e07a92bb2dbf1e84508c673ca3bd"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_derive",
|
||||
"clap_derive 3.2.18",
|
||||
"clap_lex 0.2.2",
|
||||
"indexmap",
|
||||
"once_cell",
|
||||
@ -614,7 +614,9 @@ checksum = "6bf8832993da70a4c6d13c581f4463c2bdda27b9bf1c5498dc4365543abe6d6f"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
"clap_derive 4.0.13",
|
||||
"clap_lex 0.3.0",
|
||||
"once_cell",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
]
|
||||
@ -641,6 +643,19 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c42f169caba89a7d512b5418b09864543eeb4d497416c917d7137863bd2076ad"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.2"
|
||||
@ -2097,8 +2112,10 @@ name = "jsondoclint"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.15",
|
||||
"fs-err",
|
||||
"rustdoc-json-types",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
|
@ -527,26 +527,21 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
// that are *partially* initialized by assigning to a field of an uninitialized
|
||||
// binding. We differentiate between them for more accurate wording here.
|
||||
"isn't fully initialized"
|
||||
} else if spans
|
||||
.iter()
|
||||
.filter(|i| {
|
||||
// We filter these to avoid misleading wording in cases like the following,
|
||||
// where `x` has an `init`, but it is in the same place we're looking at:
|
||||
// ```
|
||||
// let x;
|
||||
// x += 1;
|
||||
// ```
|
||||
!i.contains(span)
|
||||
// We filter these to avoid incorrect main message on `match-cfg-fake-edges.rs`
|
||||
&& !visitor
|
||||
.errors
|
||||
.iter()
|
||||
.map(|(sp, _)| *sp)
|
||||
.any(|sp| span < sp && !sp.contains(span))
|
||||
})
|
||||
.count()
|
||||
== 0
|
||||
{
|
||||
} else if !spans.iter().any(|i| {
|
||||
// We filter these to avoid misleading wording in cases like the following,
|
||||
// where `x` has an `init`, but it is in the same place we're looking at:
|
||||
// ```
|
||||
// let x;
|
||||
// x += 1;
|
||||
// ```
|
||||
!i.contains(span)
|
||||
// We filter these to avoid incorrect main message on `match-cfg-fake-edges.rs`
|
||||
&& !visitor
|
||||
.errors
|
||||
.iter()
|
||||
.map(|(sp, _)| *sp)
|
||||
.any(|sp| span < sp && !sp.contains(span))
|
||||
}) {
|
||||
show_assign_sugg = true;
|
||||
"isn't initialized"
|
||||
} else {
|
||||
|
@ -4,9 +4,9 @@
|
||||
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::{self as hir, Item, ItemKind, Node};
|
||||
use rustc_infer::infer::{
|
||||
error_reporting::nice_region_error::{
|
||||
self, find_anon_type, find_param_with_region, suggest_adding_lifetime_params,
|
||||
@ -291,71 +291,6 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
outlives_suggestion.add_suggestion(self);
|
||||
}
|
||||
|
||||
fn get_impl_ident_and_self_ty_from_trait(
|
||||
&self,
|
||||
def_id: DefId,
|
||||
trait_objects: &FxIndexSet<DefId>,
|
||||
) -> Option<(Ident, &'tcx hir::Ty<'tcx>)> {
|
||||
let tcx = self.infcx.tcx;
|
||||
match tcx.hir().get_if_local(def_id) {
|
||||
Some(Node::ImplItem(impl_item)) => {
|
||||
match tcx.hir().find_by_def_id(tcx.hir().get_parent_item(impl_item.hir_id()).def_id)
|
||||
{
|
||||
Some(Node::Item(Item {
|
||||
kind: ItemKind::Impl(hir::Impl { self_ty, .. }),
|
||||
..
|
||||
})) => Some((impl_item.ident, self_ty)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
Some(Node::TraitItem(trait_item)) => {
|
||||
let trait_did = tcx.hir().get_parent_item(trait_item.hir_id());
|
||||
match tcx.hir().find_by_def_id(trait_did.def_id) {
|
||||
Some(Node::Item(Item { kind: ItemKind::Trait(..), .. })) => {
|
||||
// The method being called is defined in the `trait`, but the `'static`
|
||||
// obligation comes from the `impl`. Find that `impl` so that we can point
|
||||
// at it in the suggestion.
|
||||
let trait_did = trait_did.to_def_id();
|
||||
match tcx
|
||||
.hir()
|
||||
.trait_impls(trait_did)
|
||||
.iter()
|
||||
.filter_map(|&impl_did| {
|
||||
match tcx.hir().get_if_local(impl_did.to_def_id()) {
|
||||
Some(Node::Item(Item {
|
||||
kind: ItemKind::Impl(hir::Impl { self_ty, .. }),
|
||||
..
|
||||
})) if trait_objects.iter().all(|did| {
|
||||
// FIXME: we should check `self_ty` against the receiver
|
||||
// type in the `UnifyReceiver` context, but for now, use
|
||||
// this imperfect proxy. This will fail if there are
|
||||
// multiple `impl`s for the same trait like
|
||||
// `impl Foo for Box<dyn Bar>` and `impl Foo for dyn Bar`.
|
||||
// In that case, only the first one will get suggestions.
|
||||
let mut traits = vec![];
|
||||
let mut hir_v = HirTraitObjectVisitor(&mut traits, *did);
|
||||
hir_v.visit_ty(self_ty);
|
||||
!traits.is_empty()
|
||||
}) =>
|
||||
{
|
||||
Some(self_ty)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.next()
|
||||
{
|
||||
Some(self_ty) => Some((trait_item.ident, self_ty)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Report an error because the universal region `fr` was required to outlive
|
||||
/// `outlived_fr` but it is not known to do so. For example:
|
||||
///
|
||||
@ -850,7 +785,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
||||
visitor.visit_ty(param.param_ty);
|
||||
|
||||
let Some((ident, self_ty)) =
|
||||
self.get_impl_ident_and_self_ty_from_trait(instance.def_id(), &visitor.0) else {return};
|
||||
NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, instance.def_id(), &visitor.0) else { return; };
|
||||
|
||||
self.suggest_constrain_dyn_trait_in_impl(diag, &visitor.0, ident, self_ty);
|
||||
}
|
||||
|
@ -1244,7 +1244,7 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
|
||||
) -> RValue<'gcc> {
|
||||
// FIXME(antoyo): remove when having a proper API.
|
||||
let gcc_func = unsafe { std::mem::transmute(func) };
|
||||
let call = if self.functions.borrow().values().find(|value| **value == gcc_func).is_some() {
|
||||
let call = if self.functions.borrow().values().any(|value| *value == gcc_func) {
|
||||
self.function_call(func, args, funclet)
|
||||
}
|
||||
else {
|
||||
|
@ -253,7 +253,7 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
|
||||
|
||||
pub fn rvalue_as_function(&self, value: RValue<'gcc>) -> Function<'gcc> {
|
||||
let function: Function<'gcc> = unsafe { std::mem::transmute(value) };
|
||||
debug_assert!(self.functions.borrow().values().find(|value| **value == function).is_some(),
|
||||
debug_assert!(self.functions.borrow().values().any(|value| *value == function),
|
||||
"{:?} ({:?}) is not a function", value, value.get_type());
|
||||
function
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
|
||||
(Unsafety::Normal, None, Unsafety::Unsafe, hir::ImplPolarity::Positive) => {
|
||||
struct_span_err!(
|
||||
tcx.sess,
|
||||
item.span,
|
||||
tcx.def_span(def_id),
|
||||
E0199,
|
||||
"implementing the trait `{}` is not unsafe",
|
||||
trait_ref.print_only_trait_path()
|
||||
@ -38,7 +38,7 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
|
||||
(Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => {
|
||||
struct_span_err!(
|
||||
tcx.sess,
|
||||
item.span,
|
||||
tcx.def_span(def_id),
|
||||
E0200,
|
||||
"the trait `{}` requires an `unsafe impl` declaration",
|
||||
trait_ref.print_only_trait_path()
|
||||
@ -61,7 +61,7 @@ pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
|
||||
(Unsafety::Normal, Some(attr_name), Unsafety::Normal, hir::ImplPolarity::Positive) => {
|
||||
struct_span_err!(
|
||||
tcx.sess,
|
||||
item.span,
|
||||
tcx.def_span(def_id),
|
||||
E0569,
|
||||
"requires an `unsafe impl` declaration due to `#[{}]` attribute",
|
||||
attr_name
|
||||
|
@ -240,10 +240,8 @@ fn typeck_with_fallback<'tcx>(
|
||||
}),
|
||||
Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), .. })
|
||||
| Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), .. }) => {
|
||||
let operand_ty = asm
|
||||
.operands
|
||||
.iter()
|
||||
.filter_map(|(op, _op_sp)| match op {
|
||||
let operand_ty =
|
||||
asm.operands.iter().find_map(|(op, _op_sp)| match op {
|
||||
hir::InlineAsmOperand::Const { anon_const }
|
||||
if anon_const.hir_id == id =>
|
||||
{
|
||||
@ -259,8 +257,7 @@ fn typeck_with_fallback<'tcx>(
|
||||
}))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.next();
|
||||
});
|
||||
operand_ty.unwrap_or_else(fallback)
|
||||
}
|
||||
_ => fallback(),
|
||||
|
@ -341,8 +341,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// Find an identifier with which this trait was imported (note that `_` doesn't count).
|
||||
let any_id = import_items
|
||||
.iter()
|
||||
.filter_map(|item| if item.ident.name != Underscore { Some(item.ident) } else { None })
|
||||
.next();
|
||||
.find_map(|item| if item.ident.name != Underscore { Some(item.ident) } else { None });
|
||||
if let Some(any_id) = any_id {
|
||||
if any_id.name == Empty {
|
||||
// Glob import, so just use its name.
|
||||
|
@ -1111,7 +1111,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
||||
// a raw pointer
|
||||
!step.self_ty.references_error() && !step.from_unsafe_deref
|
||||
})
|
||||
.flat_map(|step| {
|
||||
.find_map(|step| {
|
||||
let InferOk { value: self_ty, obligations: _ } = self
|
||||
.fcx
|
||||
.probe_instantiate_query_response(
|
||||
@ -1147,7 +1147,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
|
||||
})
|
||||
})
|
||||
})
|
||||
.next()
|
||||
}
|
||||
|
||||
/// For each type `T` in the step list, this attempts to find a method where
|
||||
|
@ -257,9 +257,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
self.tcx
|
||||
.inherent_impls(adt_def.did())
|
||||
.iter()
|
||||
.filter_map(|def_id| self.associated_value(*def_id, item_name))
|
||||
.count()
|
||||
>= 1
|
||||
.any(|def_id| self.associated_value(*def_id, item_name).is_some())
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -239,7 +239,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
||||
let mut v = TraitObjectVisitor(FxIndexSet::default());
|
||||
v.visit_ty(param.param_ty);
|
||||
if let Some((ident, self_ty)) =
|
||||
self.get_impl_ident_and_self_ty_from_trait(item_def_id, &v.0)
|
||||
NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, item_def_id, &v.0)
|
||||
&& self.suggest_constrain_dyn_trait_in_impl(&mut err, &v.0, ident, self_ty)
|
||||
{
|
||||
override_error_code = Some(ident.name);
|
||||
@ -309,19 +309,12 @@ pub fn suggest_new_region_bound(
|
||||
let did = item_id.owner_id.to_def_id();
|
||||
let ty = tcx.mk_opaque(did, ty::InternalSubsts::identity_for_item(tcx, did));
|
||||
|
||||
if let Some(span) = opaque
|
||||
.bounds
|
||||
.iter()
|
||||
.filter_map(|arg| match arg {
|
||||
GenericBound::Outlives(Lifetime {
|
||||
res: LifetimeName::Static,
|
||||
ident,
|
||||
..
|
||||
}) => Some(ident.span),
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
{
|
||||
if let Some(span) = opaque.bounds.iter().find_map(|arg| match arg {
|
||||
GenericBound::Outlives(Lifetime {
|
||||
res: LifetimeName::Static, ident, ..
|
||||
}) => Some(ident.span),
|
||||
_ => None,
|
||||
}) {
|
||||
if let Some(explicit_static) = &explicit_static {
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
@ -338,20 +331,14 @@ pub fn suggest_new_region_bound(
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
} else if opaque
|
||||
.bounds
|
||||
.iter()
|
||||
.filter_map(|arg| match arg {
|
||||
GenericBound::Outlives(Lifetime { ident, .. })
|
||||
if ident.name.to_string() == lifetime_name =>
|
||||
{
|
||||
Some(ident.span)
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
.is_some()
|
||||
{
|
||||
} else if opaque.bounds.iter().any(|arg| match arg {
|
||||
GenericBound::Outlives(Lifetime { ident, .. })
|
||||
if ident.name.to_string() == lifetime_name =>
|
||||
{
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}) {
|
||||
} else {
|
||||
err.span_suggestion_verbose(
|
||||
fn_return.span.shrink_to_hi(),
|
||||
@ -403,66 +390,54 @@ pub fn suggest_new_region_bound(
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
||||
fn get_impl_ident_and_self_ty_from_trait(
|
||||
&self,
|
||||
pub fn get_impl_ident_and_self_ty_from_trait(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: DefId,
|
||||
trait_objects: &FxIndexSet<DefId>,
|
||||
) -> Option<(Ident, &'tcx hir::Ty<'tcx>)> {
|
||||
let tcx = self.tcx();
|
||||
match tcx.hir().get_if_local(def_id) {
|
||||
Some(Node::ImplItem(impl_item)) => {
|
||||
match tcx.hir().find_by_def_id(tcx.hir().get_parent_item(impl_item.hir_id()).def_id)
|
||||
match tcx.hir().get_if_local(def_id)? {
|
||||
Node::ImplItem(impl_item) => {
|
||||
let impl_did = tcx.hir().get_parent_item(impl_item.hir_id());
|
||||
if let hir::OwnerNode::Item(Item {
|
||||
kind: ItemKind::Impl(hir::Impl { self_ty, .. }),
|
||||
..
|
||||
}) = tcx.hir().owner(impl_did)
|
||||
{
|
||||
Some(Node::Item(Item {
|
||||
kind: ItemKind::Impl(hir::Impl { self_ty, .. }),
|
||||
..
|
||||
})) => Some((impl_item.ident, self_ty)),
|
||||
_ => None,
|
||||
Some((impl_item.ident, self_ty))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Some(Node::TraitItem(trait_item)) => {
|
||||
let trait_did = tcx.hir().get_parent_item(trait_item.hir_id());
|
||||
match tcx.hir().find_by_def_id(trait_did.def_id) {
|
||||
Some(Node::Item(Item { kind: ItemKind::Trait(..), .. })) => {
|
||||
// The method being called is defined in the `trait`, but the `'static`
|
||||
// obligation comes from the `impl`. Find that `impl` so that we can point
|
||||
// at it in the suggestion.
|
||||
let trait_did = trait_did.to_def_id();
|
||||
match tcx
|
||||
.hir()
|
||||
.trait_impls(trait_did)
|
||||
.iter()
|
||||
.filter_map(|&impl_did| {
|
||||
match tcx.hir().get_if_local(impl_did.to_def_id()) {
|
||||
Some(Node::Item(Item {
|
||||
kind: ItemKind::Impl(hir::Impl { self_ty, .. }),
|
||||
..
|
||||
})) if trait_objects.iter().all(|did| {
|
||||
// FIXME: we should check `self_ty` against the receiver
|
||||
// type in the `UnifyReceiver` context, but for now, use
|
||||
// this imperfect proxy. This will fail if there are
|
||||
// multiple `impl`s for the same trait like
|
||||
// `impl Foo for Box<dyn Bar>` and `impl Foo for dyn Bar`.
|
||||
// In that case, only the first one will get suggestions.
|
||||
let mut traits = vec![];
|
||||
let mut hir_v = HirTraitObjectVisitor(&mut traits, *did);
|
||||
hir_v.visit_ty(self_ty);
|
||||
!traits.is_empty()
|
||||
}) =>
|
||||
{
|
||||
Some(self_ty)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.next()
|
||||
{
|
||||
Some(self_ty) => Some((trait_item.ident, self_ty)),
|
||||
_ => None,
|
||||
}
|
||||
Node::TraitItem(trait_item) => {
|
||||
let trait_id = tcx.hir().get_parent_item(trait_item.hir_id());
|
||||
debug_assert_eq!(tcx.def_kind(trait_id.def_id), hir::def::DefKind::Trait);
|
||||
// The method being called is defined in the `trait`, but the `'static`
|
||||
// obligation comes from the `impl`. Find that `impl` so that we can point
|
||||
// at it in the suggestion.
|
||||
let trait_did = trait_id.to_def_id();
|
||||
tcx.hir().trait_impls(trait_did).iter().find_map(|&impl_did| {
|
||||
if let Node::Item(Item {
|
||||
kind: ItemKind::Impl(hir::Impl { self_ty, .. }),
|
||||
..
|
||||
}) = tcx.hir().find_by_def_id(impl_did)?
|
||||
&& trait_objects.iter().all(|did| {
|
||||
// FIXME: we should check `self_ty` against the receiver
|
||||
// type in the `UnifyReceiver` context, but for now, use
|
||||
// this imperfect proxy. This will fail if there are
|
||||
// multiple `impl`s for the same trait like
|
||||
// `impl Foo for Box<dyn Bar>` and `impl Foo for dyn Bar`.
|
||||
// In that case, only the first one will get suggestions.
|
||||
let mut traits = vec![];
|
||||
let mut hir_v = HirTraitObjectVisitor(&mut traits, *did);
|
||||
hir_v.visit_ty(self_ty);
|
||||
!traits.is_empty()
|
||||
})
|
||||
{
|
||||
Some((trait_item.ident, *self_ty))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
@ -493,7 +468,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
|
||||
|
||||
// Get the `Ident` of the method being called and the corresponding `impl` (to point at
|
||||
// `Bar` in `impl Foo for dyn Bar {}` and the definition of the method being called).
|
||||
let Some((ident, self_ty)) = self.get_impl_ident_and_self_ty_from_trait(instance.def_id(), &v.0) else {
|
||||
let Some((ident, self_ty)) = NiceRegionError::get_impl_ident_and_self_ty_from_trait(tcx, instance.def_id(), &v.0) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
|
@ -256,7 +256,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
|
||||
cx.tcx,
|
||||
cx.tcx.explicit_item_bounds(def).iter().cloned(),
|
||||
)
|
||||
.filter_map(|obligation| {
|
||||
.find_map(|obligation| {
|
||||
// We only look at the `DefId`, so it is safe to skip the binder here.
|
||||
if let ty::PredicateKind::Clause(ty::Clause::Trait(
|
||||
ref poly_trait_predicate,
|
||||
@ -270,22 +270,17 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
|
||||
}
|
||||
})
|
||||
.map(|inner| MustUsePath::Opaque(Box::new(inner)))
|
||||
.next()
|
||||
}
|
||||
ty::Dynamic(binders, _, _) => binders
|
||||
.iter()
|
||||
.filter_map(|predicate| {
|
||||
if let ty::ExistentialPredicate::Trait(ref trait_ref) =
|
||||
predicate.skip_binder()
|
||||
{
|
||||
let def_id = trait_ref.def_id;
|
||||
is_def_must_use(cx, def_id, span)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
.map(|inner| MustUsePath::TraitObject(Box::new(inner)))
|
||||
})
|
||||
.next(),
|
||||
ty::Dynamic(binders, _, _) => binders.iter().find_map(|predicate| {
|
||||
if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder()
|
||||
{
|
||||
let def_id = trait_ref.def_id;
|
||||
is_def_must_use(cx, def_id, span)
|
||||
.map(|inner| MustUsePath::TraitObject(Box::new(inner)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}),
|
||||
ty::Tuple(tys) => {
|
||||
let elem_exprs = if let hir::ExprKind::Tup(elem_exprs) = expr.kind {
|
||||
debug_assert_eq!(elem_exprs.len(), tys.len());
|
||||
|
@ -16,14 +16,13 @@ fn test_symbols() {
|
||||
let m: &syn::ItemMacro = file
|
||||
.items
|
||||
.iter()
|
||||
.filter_map(|i| {
|
||||
.find_map(|i| {
|
||||
if let syn::Item::Macro(m) = i {
|
||||
if m.mac.path == symbols_path { Some(m) } else { None }
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.next()
|
||||
.expect("did not find `symbols!` macro invocation.");
|
||||
|
||||
let body_tokens = m.mac.tokens.clone();
|
||||
|
@ -5,6 +5,7 @@ use rustc_middle::thir::visit::{self, Visitor};
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::mir::BorrowKind;
|
||||
use rustc_middle::thir::*;
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt};
|
||||
use rustc_session::lint::builtin::{UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE};
|
||||
use rustc_session::lint::Level;
|
||||
@ -524,17 +525,19 @@ impl UnsafeOpKind {
|
||||
hir_id: hir::HirId,
|
||||
span: Span,
|
||||
) {
|
||||
// FIXME: ideally we would want to trim the def paths, but this is not
|
||||
// feasible with the current lint emission API (see issue #106126).
|
||||
match self {
|
||||
CallToUnsafeFunction(did) if did.is_some() => tcx.emit_spanned_lint(
|
||||
CallToUnsafeFunction(Some(did)) => tcx.emit_spanned_lint(
|
||||
UNSAFE_OP_IN_UNSAFE_FN,
|
||||
hir_id,
|
||||
span,
|
||||
UnsafeOpInUnsafeFnCallToUnsafeFunctionRequiresUnsafe {
|
||||
span,
|
||||
function: &tcx.def_path_str(did.unwrap()),
|
||||
function: &with_no_trimmed_paths!(tcx.def_path_str(*did)),
|
||||
},
|
||||
),
|
||||
CallToUnsafeFunction(..) => tcx.emit_spanned_lint(
|
||||
CallToUnsafeFunction(None) => tcx.emit_spanned_lint(
|
||||
UNSAFE_OP_IN_UNSAFE_FN,
|
||||
hir_id,
|
||||
span,
|
||||
@ -594,7 +597,7 @@ impl UnsafeOpKind {
|
||||
span,
|
||||
UnsafeOpInUnsafeFnCallToFunctionWithRequiresUnsafe {
|
||||
span,
|
||||
function: &tcx.def_path_str(*did),
|
||||
function: &with_no_trimmed_paths!(tcx.def_path_str(*did)),
|
||||
},
|
||||
),
|
||||
}
|
||||
@ -607,24 +610,24 @@ impl UnsafeOpKind {
|
||||
unsafe_op_in_unsafe_fn_allowed: bool,
|
||||
) {
|
||||
match self {
|
||||
CallToUnsafeFunction(did) if did.is_some() && unsafe_op_in_unsafe_fn_allowed => {
|
||||
CallToUnsafeFunction(Some(did)) if unsafe_op_in_unsafe_fn_allowed => {
|
||||
tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafeUnsafeOpInUnsafeFnAllowed {
|
||||
span,
|
||||
function: &tcx.def_path_str(did.unwrap()),
|
||||
function: &tcx.def_path_str(*did),
|
||||
});
|
||||
}
|
||||
CallToUnsafeFunction(did) if did.is_some() => {
|
||||
CallToUnsafeFunction(Some(did)) => {
|
||||
tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafe {
|
||||
span,
|
||||
function: &tcx.def_path_str(did.unwrap()),
|
||||
function: &tcx.def_path_str(*did),
|
||||
});
|
||||
}
|
||||
CallToUnsafeFunction(..) if unsafe_op_in_unsafe_fn_allowed => {
|
||||
CallToUnsafeFunction(None) if unsafe_op_in_unsafe_fn_allowed => {
|
||||
tcx.sess.emit_err(
|
||||
CallToUnsafeFunctionRequiresUnsafeNamelessUnsafeOpInUnsafeFnAllowed { span },
|
||||
);
|
||||
}
|
||||
CallToUnsafeFunction(..) => {
|
||||
CallToUnsafeFunction(None) => {
|
||||
tcx.sess.emit_err(CallToUnsafeFunctionRequiresUnsafeNameless { span });
|
||||
}
|
||||
UseOfInlineAssembly if unsafe_op_in_unsafe_fn_allowed => {
|
||||
|
@ -820,13 +820,12 @@ impl<'a> Resolver<'a> {
|
||||
// binding if it exists. What we really want here is having two separate scopes in
|
||||
// a module - one for non-globs and one for globs, but until that's done use this
|
||||
// hack to avoid inconsistent resolution ICEs during import validation.
|
||||
let binding = [resolution.binding, resolution.shadowed_glob]
|
||||
.into_iter()
|
||||
.filter_map(|binding| match (binding, ignore_binding) {
|
||||
let binding = [resolution.binding, resolution.shadowed_glob].into_iter().find_map(
|
||||
|binding| match (binding, ignore_binding) {
|
||||
(Some(binding), Some(ignored)) if ptr::eq(binding, ignored) => None,
|
||||
_ => binding,
|
||||
})
|
||||
.next();
|
||||
},
|
||||
);
|
||||
let Some(binding) = binding else {
|
||||
return Err((Determined, Weak::No));
|
||||
};
|
||||
|
@ -402,7 +402,20 @@ pub mod __alloc_error_handler {
|
||||
// `#[alloc_error_handler]`.
|
||||
#[rustc_std_internal_symbol]
|
||||
pub unsafe fn __rdl_oom(size: usize, _align: usize) -> ! {
|
||||
panic!("memory allocation of {size} bytes failed")
|
||||
extern "Rust" {
|
||||
// This symbol is emitted by rustc next to __rust_alloc_error_handler.
|
||||
// Its value depends on the -Zoom={panic,abort} compiler option.
|
||||
static __rust_alloc_error_handler_should_panic: u8;
|
||||
}
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
if unsafe { __rust_alloc_error_handler_should_panic != 0 } {
|
||||
panic!("memory allocation of {size} bytes failed")
|
||||
} else {
|
||||
core::panicking::panic_nounwind_fmt(format_args!(
|
||||
"memory allocation of {size} bytes failed"
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,6 +110,7 @@
|
||||
#![feature(const_maybe_uninit_as_mut_ptr)]
|
||||
#![feature(const_refs_to_cell)]
|
||||
#![feature(core_intrinsics)]
|
||||
#![feature(core_panic)]
|
||||
#![feature(const_eval_select)]
|
||||
#![feature(const_pin)]
|
||||
#![feature(const_waker)]
|
||||
|
@ -64,13 +64,17 @@ pub const fn panic_fmt(fmt: fmt::Arguments<'_>) -> ! {
|
||||
unsafe { panic_impl(&pi) }
|
||||
}
|
||||
|
||||
/// Like `panic`, but without unwinding and track_caller to reduce the impact on codesize.
|
||||
/// (No `fmt` variant as a `fmt::Arguments` needs more space to be passed.)
|
||||
/// Like `panic_fmt`, but for non-unwinding panics.
|
||||
///
|
||||
/// Has to be a separate function so that it can carry the `rustc_nounwind` attribute.
|
||||
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
|
||||
#[cfg_attr(feature = "panic_immediate_abort", inline)]
|
||||
#[cfg_attr(not(bootstrap), lang = "panic_nounwind")] // needed by codegen for non-unwinding panics
|
||||
#[track_caller]
|
||||
// This attribute has the key side-effect that if the panic handler ignores `can_unwind`
|
||||
// and unwinds anyway, we will hit the "unwinding out of nounwind function" guard,
|
||||
// which causes a "panic in a function that cannot unwind".
|
||||
#[rustc_nounwind]
|
||||
pub fn panic_nounwind(msg: &'static str) -> ! {
|
||||
pub fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>) -> ! {
|
||||
if cfg!(feature = "panic_immediate_abort") {
|
||||
super::intrinsics::abort()
|
||||
}
|
||||
@ -83,8 +87,6 @@ pub fn panic_nounwind(msg: &'static str) -> ! {
|
||||
}
|
||||
|
||||
// PanicInfo with the `can_unwind` flag set to false forces an abort.
|
||||
let pieces = [msg];
|
||||
let fmt = fmt::Arguments::new_v1(&pieces, &[]);
|
||||
let pi = PanicInfo::internal_constructor(Some(&fmt), Location::caller(), false);
|
||||
|
||||
// SAFETY: `panic_impl` is defined in safe Rust code and thus is safe to call.
|
||||
@ -112,6 +114,15 @@ pub const fn panic(expr: &'static str) -> ! {
|
||||
panic_fmt(fmt::Arguments::new_v1(&[expr], &[]));
|
||||
}
|
||||
|
||||
/// Like `panic`, but without unwinding and track_caller to reduce the impact on codesize.
|
||||
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)]
|
||||
#[cfg_attr(feature = "panic_immediate_abort", inline)]
|
||||
#[cfg_attr(not(bootstrap), lang = "panic_nounwind")] // needed by codegen for non-unwinding panics
|
||||
#[rustc_nounwind]
|
||||
pub fn panic_nounwind(expr: &'static str) -> ! {
|
||||
panic_nounwind_fmt(fmt::Arguments::new_v1(&[expr], &[]));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
#[rustc_diagnostic_item = "panic_str"]
|
||||
|
@ -338,7 +338,7 @@ fn default_alloc_error_hook(layout: Layout) {
|
||||
|
||||
#[allow(unused_unsafe)]
|
||||
if unsafe { __rust_alloc_error_handler_should_panic != 0 } {
|
||||
panic!("memory allocation of {} bytes failed\n", layout.size());
|
||||
panic!("memory allocation of {} bytes failed", layout.size());
|
||||
} else {
|
||||
rtprintpanic!("memory allocation of {} bytes failed\n", layout.size());
|
||||
}
|
||||
|
@ -2067,6 +2067,9 @@ impl Step for RustDev {
|
||||
|
||||
builder.ensure(crate::native::Llvm { target });
|
||||
|
||||
// We want to package `lld` to use it with `download-ci-llvm`.
|
||||
builder.ensure(crate::native::Lld { target });
|
||||
|
||||
let src_bindir = builder.llvm_out(target).join("bin");
|
||||
// If updating this list, you likely want to change
|
||||
// src/bootstrap/download-ci-llvm-stamp as well, otherwise local users
|
||||
|
@ -1,4 +1,4 @@
|
||||
Change this file to make users of the `download-ci-llvm` configuration download
|
||||
a new version of LLVM from CI, even if the LLVM submodule hasn’t changed.
|
||||
|
||||
Last change is for: https://github.com/rust-lang/rust/pull/102790
|
||||
Last change is for: https://github.com/rust-lang/rust/pull/104748
|
||||
|
@ -63,13 +63,13 @@ impl LdFlags {
|
||||
}
|
||||
}
|
||||
|
||||
// This returns whether we've already previously built LLVM.
|
||||
//
|
||||
// It's used to avoid busting caches during x.py check -- if we've already built
|
||||
// LLVM, it's fine for us to not try to avoid doing so.
|
||||
//
|
||||
// This will return the llvm-config if it can get it (but it will not build it
|
||||
// if not).
|
||||
/// This returns whether we've already previously built LLVM.
|
||||
///
|
||||
/// It's used to avoid busting caches during x.py check -- if we've already built
|
||||
/// LLVM, it's fine for us to not try to avoid doing so.
|
||||
///
|
||||
/// This will return the llvm-config if it can get it (but it will not build it
|
||||
/// if not).
|
||||
pub fn prebuilt_llvm_config(
|
||||
builder: &Builder<'_>,
|
||||
target: TargetSelection,
|
||||
@ -823,8 +823,21 @@ impl Step for Lld {
|
||||
}
|
||||
let target = self.target;
|
||||
|
||||
let LlvmResult { llvm_config, llvm_cmake_dir } =
|
||||
builder.ensure(Llvm { target: self.target });
|
||||
let LlvmResult { llvm_config, llvm_cmake_dir } = builder.ensure(Llvm { target });
|
||||
|
||||
// The `dist` step packages LLD next to LLVM's binaries for download-ci-llvm. The root path
|
||||
// we usually expect here is `./build/$triple/ci-llvm/`, with the binaries in its `bin`
|
||||
// subfolder. We check if that's the case, and if LLD's binary already exists there next to
|
||||
// `llvm-config`: if so, we can use it instead of building LLVM/LLD from source.
|
||||
let ci_llvm_bin = llvm_config.parent().unwrap();
|
||||
if ci_llvm_bin.is_dir() && ci_llvm_bin.file_name().unwrap() == "bin" {
|
||||
let lld_path = ci_llvm_bin.join(exe("lld", target));
|
||||
if lld_path.exists() {
|
||||
// The following steps copying `lld` as `rust-lld` to the sysroot, expect it in the
|
||||
// `bin` subfolder of this step's out dir.
|
||||
return ci_llvm_bin.parent().unwrap().to_path_buf();
|
||||
}
|
||||
}
|
||||
|
||||
let out_dir = builder.lld_out(target);
|
||||
let done_stamp = out_dir.join("lld-finished-building");
|
||||
|
@ -108,11 +108,7 @@ body {
|
||||
/* Then override it with `anywhere`, which is required to make non-Safari browsers break
|
||||
more aggressively when we want them to. */
|
||||
overflow-wrap: anywhere;
|
||||
|
||||
-webkit-font-feature-settings: "kern", "liga";
|
||||
-moz-font-feature-settings: "kern", "liga";
|
||||
font-feature-settings: "kern", "liga";
|
||||
|
||||
background-color: var(--main-background-color);
|
||||
color: var(--main-color);
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ error[E0199]: implementing the trait `MySafeTrait` is not unsafe
|
||||
--> $DIR/coherence-default-trait-impl.rs:8:1
|
||||
|
|
||||
LL | unsafe impl MySafeTrait for Foo {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: remove `unsafe` from this trait implementation
|
||||
|
|
||||
@ -14,7 +14,7 @@ error[E0200]: the trait `MyUnsafeTrait` requires an `unsafe impl` declaration
|
||||
--> $DIR/coherence-default-trait-impl.rs:13:1
|
||||
|
|
||||
LL | impl MyUnsafeTrait for Foo {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: the trait `MyUnsafeTrait` enforces invariants that the compiler can't check. Review the trait documentation and make sure this implementation upholds those invariants before adding the `unsafe` keyword
|
||||
help: add `unsafe` to this trait implementation
|
||||
|
@ -1,13 +1,8 @@
|
||||
error[E0569]: requires an `unsafe impl` declaration due to `#[may_dangle]` attribute
|
||||
--> $DIR/dropck-eyepatch-implies-unsafe-impl.rs:21:1
|
||||
|
|
||||
LL | / impl<#[may_dangle] A, B: fmt::Debug> Drop for Pt<A, B> {
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | // (unsafe to access self.1 due to #[may_dangle] on A)
|
||||
LL | | fn drop(&mut self) { println!("drop {} {:?}", self.0, self.2); }
|
||||
LL | | }
|
||||
| |_^
|
||||
LL | impl<#[may_dangle] A, B: fmt::Debug> Drop for Pt<A, B> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: the trait `Drop` enforces invariants that the compiler can't check. Review the trait documentation and make sure this implementation upholds those invariants before adding the `unsafe` keyword
|
||||
help: add `unsafe` to this trait implementation
|
||||
@ -18,13 +13,8 @@ LL | unsafe impl<#[may_dangle] A, B: fmt::Debug> Drop for Pt<A, B> {
|
||||
error[E0569]: requires an `unsafe impl` declaration due to `#[may_dangle]` attribute
|
||||
--> $DIR/dropck-eyepatch-implies-unsafe-impl.rs:27:1
|
||||
|
|
||||
LL | / impl<#[may_dangle] 'a, 'b, B: fmt::Debug> Drop for Pr<'a, 'b, B> {
|
||||
LL | |
|
||||
LL | |
|
||||
LL | | // (unsafe to access self.1 due to #[may_dangle] on 'a)
|
||||
LL | | fn drop(&mut self) { println!("drop {} {:?}", self.0, self.2); }
|
||||
LL | | }
|
||||
| |_^
|
||||
LL | impl<#[may_dangle] 'a, 'b, B: fmt::Debug> Drop for Pr<'a, 'b, B> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: the trait `Drop` enforces invariants that the compiler can't check. Review the trait documentation and make sure this implementation upholds those invariants before adding the `unsafe` keyword
|
||||
help: add `unsafe` to this trait implementation
|
||||
|
@ -2,7 +2,7 @@ error[E0199]: implementing the trait `Bar` is not unsafe
|
||||
--> $DIR/E0199.rs:6:1
|
||||
|
|
||||
LL | unsafe impl Bar for Foo { }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: remove `unsafe` from this trait implementation
|
||||
|
|
||||
|
@ -2,7 +2,7 @@ error[E0200]: the trait `Bar` requires an `unsafe impl` declaration
|
||||
--> $DIR/E0200.rs:5:1
|
||||
|
|
||||
LL | impl Bar for Foo { }
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: the trait `Bar` enforces invariants that the compiler can't check. Review the trait documentation and make sure this implementation upholds those invariants before adding the `unsafe` keyword
|
||||
help: add `unsafe` to this trait implementation
|
||||
|
@ -1,12 +1,8 @@
|
||||
error[E0200]: the trait `Foo` requires an `unsafe impl` declaration
|
||||
--> $DIR/safety-trait-impl-cc.rs:9:1
|
||||
|
|
||||
LL | / impl lib::Foo for Bar {
|
||||
LL | | fn foo(&self) -> isize {
|
||||
LL | | panic!();
|
||||
LL | | }
|
||||
LL | | }
|
||||
| |_^
|
||||
LL | impl lib::Foo for Bar {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: the trait `Foo` enforces invariants that the compiler can't check. Review the trait documentation and make sure this implementation upholds those invariants before adding the `unsafe` keyword
|
||||
help: add `unsafe` to this trait implementation
|
||||
|
@ -2,7 +2,7 @@ error[E0200]: the trait `UnsafeTrait` requires an `unsafe impl` declaration
|
||||
--> $DIR/safety-trait-impl.rs:14:1
|
||||
|
|
||||
LL | impl UnsafeTrait for u16 { }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: the trait `UnsafeTrait` enforces invariants that the compiler can't check. Review the trait documentation and make sure this implementation upholds those invariants before adding the `unsafe` keyword
|
||||
help: add `unsafe` to this trait implementation
|
||||
@ -14,7 +14,7 @@ error[E0199]: implementing the trait `SafeTrait` is not unsafe
|
||||
--> $DIR/safety-trait-impl.rs:16:1
|
||||
|
|
||||
LL | unsafe impl SafeTrait for u32 { }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: remove `unsafe` from this trait implementation
|
||||
|
|
||||
|
9
src/test/ui/unsafe/auxiliary/issue-106126.rs
Normal file
9
src/test/ui/unsafe/auxiliary/issue-106126.rs
Normal file
@ -0,0 +1,9 @@
|
||||
#[macro_export]
|
||||
macro_rules! foo {
|
||||
() => {
|
||||
unsafe fn __unsf() {}
|
||||
unsafe fn __foo() {
|
||||
__unsf();
|
||||
}
|
||||
};
|
||||
}
|
12
src/test/ui/unsafe/issue-106126-good-path-bug.rs
Normal file
12
src/test/ui/unsafe/issue-106126-good-path-bug.rs
Normal file
@ -0,0 +1,12 @@
|
||||
// Regression test for #106126.
|
||||
// check-pass
|
||||
// aux-build:issue-106126.rs
|
||||
|
||||
#![deny(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate issue_106126;
|
||||
|
||||
foo!();
|
||||
|
||||
fn main() {}
|
@ -7,6 +7,8 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.62"
|
||||
clap = { version = "4.0.15", features = ["derive"] }
|
||||
fs-err = "2.8.1"
|
||||
rustdoc-json-types = { version = "0.1.0", path = "../../rustdoc-json-types" }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0.85"
|
||||
|
@ -1,8 +1,9 @@
|
||||
use std::fmt::Write;
|
||||
|
||||
use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
|
||||
pub enum SelectorPart {
|
||||
Field(String),
|
||||
Index(usize),
|
||||
|
@ -1,59 +1,103 @@
|
||||
use std::env;
|
||||
use std::io::{BufWriter, Write};
|
||||
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use anyhow::{bail, Result};
|
||||
use clap::Parser;
|
||||
use fs_err as fs;
|
||||
use rustdoc_json_types::{Crate, Id, FORMAT_VERSION};
|
||||
use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
|
||||
pub(crate) mod item_kind;
|
||||
mod json_find;
|
||||
mod validator;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Clone)]
|
||||
struct Error {
|
||||
kind: ErrorKind,
|
||||
id: Id,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq, Serialize, Clone)]
|
||||
enum ErrorKind {
|
||||
NotFound,
|
||||
NotFound(Vec<json_find::Selector>),
|
||||
Custom(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct JsonOutput {
|
||||
path: String,
|
||||
errors: Vec<Error>,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
struct Cli {
|
||||
/// The path to the json file to be linted
|
||||
path: String,
|
||||
|
||||
/// Show verbose output
|
||||
#[arg(long)]
|
||||
verbose: bool,
|
||||
|
||||
#[arg(long)]
|
||||
json_output: Option<String>,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let path = env::args().nth(1).ok_or_else(|| anyhow!("no path given"))?;
|
||||
let Cli { path, verbose, json_output } = Cli::parse();
|
||||
|
||||
let contents = fs::read_to_string(&path)?;
|
||||
let krate: Crate = serde_json::from_str(&contents)?;
|
||||
assert_eq!(krate.format_version, FORMAT_VERSION);
|
||||
|
||||
let mut validator = validator::Validator::new(&krate);
|
||||
let krate_json: Value = serde_json::from_str(&contents)?;
|
||||
|
||||
let mut validator = validator::Validator::new(&krate, krate_json);
|
||||
validator.check_crate();
|
||||
|
||||
if let Some(json_output) = json_output {
|
||||
let output = JsonOutput { path: path.clone(), errors: validator.errs.clone() };
|
||||
let mut f = BufWriter::new(fs::File::create(json_output)?);
|
||||
serde_json::to_writer(&mut f, &output)?;
|
||||
f.flush()?;
|
||||
}
|
||||
|
||||
if !validator.errs.is_empty() {
|
||||
for err in validator.errs {
|
||||
match err.kind {
|
||||
ErrorKind::NotFound => {
|
||||
let krate_json: Value = serde_json::from_str(&contents)?;
|
||||
|
||||
let sels =
|
||||
json_find::find_selector(&krate_json, &Value::String(err.id.0.clone()));
|
||||
match &sels[..] {
|
||||
[] => unreachable!(
|
||||
"id must be in crate, or it wouldn't be reported as not found"
|
||||
),
|
||||
[sel] => eprintln!(
|
||||
"{} not in index or paths, but refered to at '{}'",
|
||||
err.id.0,
|
||||
json_find::to_jsonpath(&sel)
|
||||
),
|
||||
[sel, ..] => eprintln!(
|
||||
"{} not in index or paths, but refered to at '{}' and more",
|
||||
err.id.0,
|
||||
json_find::to_jsonpath(&sel)
|
||||
),
|
||||
ErrorKind::NotFound(sels) => match &sels[..] {
|
||||
[] => {
|
||||
unreachable!(
|
||||
"id {:?} must be in crate, or it wouldn't be reported as not found",
|
||||
err.id
|
||||
)
|
||||
}
|
||||
}
|
||||
[sel] => eprintln!(
|
||||
"{} not in index or paths, but refered to at '{}'",
|
||||
err.id.0,
|
||||
json_find::to_jsonpath(&sel)
|
||||
),
|
||||
[sel, ..] => {
|
||||
if verbose {
|
||||
let sels = sels
|
||||
.iter()
|
||||
.map(json_find::to_jsonpath)
|
||||
.map(|i| format!("'{i}'"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
eprintln!(
|
||||
"{} not in index or paths, but refered to at {sels}",
|
||||
err.id.0
|
||||
);
|
||||
} else {
|
||||
eprintln!(
|
||||
"{} not in index or paths, but refered to at '{}' and {} more",
|
||||
err.id.0,
|
||||
json_find::to_jsonpath(&sel),
|
||||
sels.len() - 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
ErrorKind::Custom(msg) => eprintln!("{}: {}", err.id.0, msg),
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,9 @@ use rustdoc_json_types::{
|
||||
Primitive, ProcMacro, Static, Struct, StructKind, Term, Trait, TraitAlias, Type, TypeBinding,
|
||||
TypeBindingKind, Typedef, Union, Variant, VariantKind, WherePredicate,
|
||||
};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::{item_kind::Kind, Error, ErrorKind};
|
||||
use crate::{item_kind::Kind, json_find, Error, ErrorKind};
|
||||
|
||||
/// The Validator walks over the JSON tree, and ensures it is well formed.
|
||||
/// It is made of several parts.
|
||||
@ -22,6 +23,7 @@ use crate::{item_kind::Kind, Error, ErrorKind};
|
||||
pub struct Validator<'a> {
|
||||
pub(crate) errs: Vec<Error>,
|
||||
krate: &'a Crate,
|
||||
krate_json: Value,
|
||||
/// Worklist of Ids to check.
|
||||
todo: HashSet<&'a Id>,
|
||||
/// Ids that have already been visited, so don't need to be checked again.
|
||||
@ -39,9 +41,10 @@ enum PathKind {
|
||||
}
|
||||
|
||||
impl<'a> Validator<'a> {
|
||||
pub fn new(krate: &'a Crate) -> Self {
|
||||
pub fn new(krate: &'a Crate, krate_json: Value) -> Self {
|
||||
Self {
|
||||
krate,
|
||||
krate_json,
|
||||
errs: Vec::new(),
|
||||
seen_ids: HashSet::new(),
|
||||
todo: HashSet::new(),
|
||||
@ -373,7 +376,11 @@ impl<'a> Validator<'a> {
|
||||
} else {
|
||||
if !self.missing_ids.contains(id) {
|
||||
self.missing_ids.insert(id);
|
||||
self.fail(id, ErrorKind::NotFound)
|
||||
|
||||
let sels = json_find::find_selector(&self.krate_json, &Value::String(id.0.clone()));
|
||||
assert_ne!(sels.len(), 0);
|
||||
|
||||
self.fail(id, ErrorKind::NotFound(sels))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,16 @@ use std::collections::HashMap;
|
||||
|
||||
use rustdoc_json_types::{Crate, Item, Visibility};
|
||||
|
||||
use crate::json_find::SelectorPart;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[track_caller]
|
||||
fn check(krate: &Crate, errs: &[Error]) {
|
||||
let mut validator = Validator::new(krate);
|
||||
let krate_string = serde_json::to_string(krate).unwrap();
|
||||
let krate_json = serde_json::from_str(&krate_string).unwrap();
|
||||
|
||||
let mut validator = Validator::new(krate, krate_json);
|
||||
validator.check_crate();
|
||||
|
||||
assert_eq!(errs, &validator.errs[..]);
|
||||
@ -46,5 +51,16 @@ fn errors_on_missing_links() {
|
||||
format_version: rustdoc_json_types::FORMAT_VERSION,
|
||||
};
|
||||
|
||||
check(&k, &[Error { kind: ErrorKind::NotFound, id: id("1") }]);
|
||||
check(
|
||||
&k,
|
||||
&[Error {
|
||||
kind: ErrorKind::NotFound(vec![vec![
|
||||
SelectorPart::Field("index".to_owned()),
|
||||
SelectorPart::Field("0".to_owned()),
|
||||
SelectorPart::Field("links".to_owned()),
|
||||
SelectorPart::Field("Not Found".to_owned()),
|
||||
]]),
|
||||
id: id("1"),
|
||||
}],
|
||||
);
|
||||
}
|
||||
|
@ -479,6 +479,7 @@ compiler = [
|
||||
"compiler-team-contributors",
|
||||
]
|
||||
libs = [
|
||||
"@cuviper",
|
||||
"@joshtriplett",
|
||||
"@Mark-Simulacrum",
|
||||
"@m-ou-se",
|
||||
|
Loading…
x
Reference in New Issue
Block a user