mirror of https://github.com/rust-lang/rust.git
Auto merge of #105365 - matthiaskrgr:rollup-g0mrrt7, r=matthiaskrgr
Rollup of 11 pull requests Successful merges: - #104439 (Add prototype to generate `COPYRIGHT` from REUSE metadata) - #105005 (On E0195 point at where clause lifetime bounds) - #105098 (propagate the error from parsing enum variant to the parser and emit out) - #105243 (remove no-op 'let _ = ') - #105254 (Recurse into nested impl-trait when computing variance.) - #105287 (Synthesize substitutions for bad auto traits in dyn types) - #105310 (Be more careful about unresolved exprs in suggestion) - #105318 (Make `get_impl_future_output_ty` work with AFIT) - #105339 (support `ConstKind::Expr` in `is_const_evaluatable` and `WfPredicates::compute`) - #105340 (Avoid ICE by accounting for missing type) - #105342 (Make `note_obligation_cause_code` take a `impl ToPredicate` for predicate) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
b6852428a8
77
Cargo.lock
77
Cargo.lock
|
@ -581,6 +581,7 @@ dependencies = [
|
|||
"libc",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"time",
|
||||
"winapi",
|
||||
]
|
||||
|
@ -730,6 +731,16 @@ dependencies = [
|
|||
"rustc-semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "collect-license-metadata"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"spdx-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "color-eyre"
|
||||
version = "0.6.2"
|
||||
|
@ -1552,6 +1563,15 @@ dependencies = [
|
|||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generate-copyright"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.4"
|
||||
|
@ -4864,6 +4884,35 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spdx-expression"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d7ac03c67c572d85049d6db815e20a4a19b41b3d5cca732ac582342021ad77"
|
||||
dependencies = [
|
||||
"nom",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spdx-rs"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3c02f6eb7e7b4100c272f685a9ccaccaab302324e8c7ec3e2ee72340fb29ff3"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"log",
|
||||
"nom",
|
||||
"serde",
|
||||
"spdx-expression",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"thiserror",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
|
@ -4967,6 +5016,25 @@ version = "0.10.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.24.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.102"
|
||||
|
@ -5596,6 +5664,15 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||
dependencies = [
|
||||
"getrandom 0.2.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "valuable"
|
||||
version = "0.1.0"
|
||||
|
|
|
@ -39,6 +39,8 @@ members = [
|
|||
"src/tools/bump-stage0",
|
||||
"src/tools/replace-version-placeholder",
|
||||
"src/tools/lld-wrapper",
|
||||
"src/tools/collect-license-metadata",
|
||||
"src/tools/generate-copyright",
|
||||
]
|
||||
|
||||
exclude = [
|
||||
|
|
|
@ -121,9 +121,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
|
||||
pub(super) fn prove_predicates(
|
||||
&mut self,
|
||||
predicates: impl IntoIterator<
|
||||
Item = impl ToPredicate<'tcx, ty::Predicate<'tcx>> + std::fmt::Debug,
|
||||
>,
|
||||
predicates: impl IntoIterator<Item = impl ToPredicate<'tcx> + std::fmt::Debug>,
|
||||
locations: Locations,
|
||||
category: ConstraintCategory<'tcx>,
|
||||
) {
|
||||
|
@ -135,7 +133,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
|||
#[instrument(skip(self), level = "debug")]
|
||||
pub(super) fn prove_predicate(
|
||||
&mut self,
|
||||
predicate: impl ToPredicate<'tcx, ty::Predicate<'tcx>> + std::fmt::Debug,
|
||||
predicate: impl ToPredicate<'tcx> + std::fmt::Debug,
|
||||
locations: Locations,
|
||||
category: ConstraintCategory<'tcx>,
|
||||
) {
|
||||
|
|
|
@ -17,6 +17,8 @@ hir_analysis_lifetimes_or_bounds_mismatch_on_trait =
|
|||
lifetime parameters or bounds on {$item_kind} `{$ident}` do not match the trait declaration
|
||||
.label = lifetimes do not match {$item_kind} in trait
|
||||
.generics_label = lifetimes in impl do not match this {$item_kind} in trait
|
||||
.where_label = this `where` clause might not match the one in the trait
|
||||
.bounds_label = this bound might be missing in the impl
|
||||
|
||||
hir_analysis_drop_impl_on_wrong_item =
|
||||
the `Drop` trait may only be implemented for local structs, enums, and unions
|
||||
|
|
|
@ -751,17 +751,45 @@ fn check_region_bounds_on_impl_item<'tcx>(
|
|||
.get_generics(impl_m.def_id.expect_local())
|
||||
.expect("expected impl item to have generics or else we can't compare them")
|
||||
.span;
|
||||
let generics_span = if let Some(local_def_id) = trait_m.def_id.as_local() {
|
||||
Some(
|
||||
tcx.hir()
|
||||
.get_generics(local_def_id)
|
||||
.expect("expected trait item to have generics or else we can't compare them")
|
||||
.span,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut generics_span = None;
|
||||
let mut bounds_span = vec![];
|
||||
let mut where_span = None;
|
||||
if let Some(trait_node) = tcx.hir().get_if_local(trait_m.def_id)
|
||||
&& let Some(trait_generics) = trait_node.generics()
|
||||
{
|
||||
generics_span = Some(trait_generics.span);
|
||||
// FIXME: we could potentially look at the impl's bounds to not point at bounds that
|
||||
// *are* present in the impl.
|
||||
for p in trait_generics.predicates {
|
||||
if let hir::WherePredicate::BoundPredicate(pred) = p {
|
||||
for b in pred.bounds {
|
||||
if let hir::GenericBound::Outlives(lt) = b {
|
||||
bounds_span.push(lt.ident.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(impl_node) = tcx.hir().get_if_local(impl_m.def_id)
|
||||
&& let Some(impl_generics) = impl_node.generics()
|
||||
{
|
||||
let mut impl_bounds = 0;
|
||||
for p in impl_generics.predicates {
|
||||
if let hir::WherePredicate::BoundPredicate(pred) = p {
|
||||
for b in pred.bounds {
|
||||
if let hir::GenericBound::Outlives(_) = b {
|
||||
impl_bounds += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if impl_bounds == bounds_span.len() {
|
||||
bounds_span = vec![];
|
||||
} else if impl_generics.has_where_clause_predicates {
|
||||
where_span = Some(impl_generics.where_clause_span);
|
||||
}
|
||||
}
|
||||
}
|
||||
let reported = tcx
|
||||
.sess
|
||||
.create_err(LifetimesOrBoundsMismatchOnTrait {
|
||||
|
@ -769,9 +797,10 @@ fn check_region_bounds_on_impl_item<'tcx>(
|
|||
item_kind: assoc_item_kind_str(impl_m),
|
||||
ident: impl_m.ident(tcx),
|
||||
generics_span,
|
||||
bounds_span,
|
||||
where_span,
|
||||
})
|
||||
.emit_unless(delay);
|
||||
|
||||
return Err(reported);
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,10 @@ pub struct LifetimesOrBoundsMismatchOnTrait {
|
|||
pub span: Span,
|
||||
#[label(generics_label)]
|
||||
pub generics_span: Option<Span>,
|
||||
#[label(where_label)]
|
||||
pub where_span: Option<Span>,
|
||||
#[label(bounds_label)]
|
||||
pub bounds_span: Vec<Span>,
|
||||
pub item_kind: &'static str,
|
||||
pub ident: Ident,
|
||||
}
|
||||
|
|
|
@ -7,7 +7,8 @@ use rustc_arena::DroplessArena;
|
|||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_middle::ty::query::Providers;
|
||||
use rustc_middle::ty::{self, CrateVariancesMap, TyCtxt, TypeSuperVisitable, TypeVisitable};
|
||||
use rustc_middle::ty::{self, CrateVariancesMap, SubstsRef, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{DefIdTree, TypeSuperVisitable, TypeVisitable};
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
/// Defines the `TermsContext` basically houses an arena where we can
|
||||
|
@ -75,11 +76,30 @@ fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Varianc
|
|||
// type Foo<'a, 'b, 'c> = impl Trait<'a> + 'b;
|
||||
// ```
|
||||
// we may not use `'c` in the hidden type.
|
||||
struct OpaqueTypeLifetimeCollector {
|
||||
struct OpaqueTypeLifetimeCollector<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
root_def_id: DefId,
|
||||
variances: Vec<ty::Variance>,
|
||||
}
|
||||
|
||||
impl<'tcx> ty::TypeVisitor<'tcx> for OpaqueTypeLifetimeCollector {
|
||||
impl<'tcx> OpaqueTypeLifetimeCollector<'tcx> {
|
||||
#[instrument(level = "trace", skip(self), ret)]
|
||||
fn visit_opaque(&mut self, def_id: DefId, substs: SubstsRef<'tcx>) -> ControlFlow<!> {
|
||||
if def_id != self.root_def_id && self.tcx.is_descendant_of(def_id, self.root_def_id) {
|
||||
let child_variances = self.tcx.variances_of(def_id);
|
||||
for (a, v) in substs.iter().zip(child_variances) {
|
||||
if *v != ty::Bivariant {
|
||||
a.visit_with(self)?;
|
||||
}
|
||||
}
|
||||
ControlFlow::CONTINUE
|
||||
} else {
|
||||
substs.visit_with(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ty::TypeVisitor<'tcx> for OpaqueTypeLifetimeCollector<'tcx> {
|
||||
#[instrument(level = "trace", skip(self), ret)]
|
||||
fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
if let ty::RegionKind::ReEarlyBound(ebr) = r.kind() {
|
||||
|
@ -87,6 +107,19 @@ fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Varianc
|
|||
}
|
||||
r.super_visit_with(self)
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(self), ret)]
|
||||
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
match t.kind() {
|
||||
ty::Opaque(def_id, substs) => self.visit_opaque(*def_id, substs),
|
||||
ty::Projection(proj)
|
||||
if self.tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder =>
|
||||
{
|
||||
self.visit_opaque(proj.item_def_id, proj.substs)
|
||||
}
|
||||
_ => t.super_visit_with(self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// By default, RPIT are invariant wrt type and const generics, but they are bivariant wrt
|
||||
|
@ -111,7 +144,8 @@ fn variance_of_opaque(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Varianc
|
|||
}
|
||||
}
|
||||
|
||||
let mut collector = OpaqueTypeLifetimeCollector { variances };
|
||||
let mut collector =
|
||||
OpaqueTypeLifetimeCollector { tcx, root_def_id: item_def_id.to_def_id(), variances };
|
||||
let id_substs = ty::InternalSubsts::identity_for_item(tcx, item_def_id.to_def_id());
|
||||
for pred in tcx.bound_explicit_item_bounds(item_def_id.to_def_id()).transpose_iter() {
|
||||
let pred = pred.map_bound(|(pred, _)| *pred).subst(tcx, id_substs);
|
||||
|
|
|
@ -1482,15 +1482,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
ident_name: Symbol,
|
||||
}
|
||||
|
||||
// FIXME: This really should be taking scoping, etc into account.
|
||||
impl<'v> Visitor<'v> for LetVisitor<'v> {
|
||||
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
|
||||
if let hir::StmtKind::Local(hir::Local { pat, init, .. }) = &ex.kind {
|
||||
if let Binding(_, _, ident, ..) = pat.kind &&
|
||||
ident.name == self.ident_name {
|
||||
self.result = *init;
|
||||
}
|
||||
if let hir::StmtKind::Local(hir::Local { pat, init, .. }) = &ex.kind
|
||||
&& let Binding(_, _, ident, ..) = pat.kind
|
||||
&& ident.name == self.ident_name
|
||||
{
|
||||
self.result = *init;
|
||||
} else {
|
||||
hir::intravisit::walk_stmt(self, ex);
|
||||
}
|
||||
hir::intravisit::walk_stmt(self, ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1498,9 +1500,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
visitor.visit_body(&body);
|
||||
|
||||
let parent = self.tcx.hir().get_parent_node(seg1.hir_id);
|
||||
if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent) &&
|
||||
let Some(expr) = visitor.result {
|
||||
let self_ty = self.node_ty(expr.hir_id);
|
||||
if let Some(Node::Expr(call_expr)) = self.tcx.hir().find(parent)
|
||||
&& let Some(expr) = visitor.result
|
||||
&& let Some(self_ty) = self.node_ty_opt(expr.hir_id)
|
||||
{
|
||||
let probe = self.lookup_probe(
|
||||
seg2.ident,
|
||||
self_ty,
|
||||
|
@ -1513,7 +1516,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
sm.span_extend_while(seg1.ident.span.shrink_to_hi(), |c| c == ':').unwrap(),
|
||||
"you may have meant to call an instance method",
|
||||
".".to_string(),
|
||||
Applicability::MaybeIncorrect
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -341,7 +341,15 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>(
|
|||
|
||||
impl<'tcx> InferCtxt<'tcx> {
|
||||
pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
|
||||
let ty::Opaque(def_id, substs) = *ty.kind() else { return None; };
|
||||
let (def_id, substs) = match *ty.kind() {
|
||||
ty::Opaque(def_id, substs) => (def_id, substs),
|
||||
ty::Projection(data)
|
||||
if self.tcx.def_kind(data.item_def_id) == DefKind::ImplTraitPlaceholder =>
|
||||
{
|
||||
(data.item_def_id, data.substs)
|
||||
}
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
|
||||
let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
|
||||
|
|
|
@ -101,6 +101,20 @@ impl GenericParamDef {
|
|||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_error<'tcx>(
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
preceding_substs: &[ty::GenericArg<'tcx>],
|
||||
) -> ty::GenericArg<'tcx> {
|
||||
match &self.kind {
|
||||
ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_static.into(),
|
||||
ty::GenericParamDefKind::Type { .. } => tcx.ty_error().into(),
|
||||
ty::GenericParamDefKind::Const { .. } => {
|
||||
tcx.const_error(tcx.bound_type_of(self.def_id).subst(tcx, preceding_substs)).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
|
|
@ -1150,8 +1150,8 @@ impl<'tcx> ToPolyTraitRef<'tcx> for PolyTraitPredicate<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait ToPredicate<'tcx, Predicate> {
|
||||
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate;
|
||||
pub trait ToPredicate<'tcx, P = Predicate<'tcx>> {
|
||||
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> P;
|
||||
}
|
||||
|
||||
impl<'tcx, T> ToPredicate<'tcx, T> for T {
|
||||
|
@ -1160,21 +1160,21 @@ impl<'tcx, T> ToPredicate<'tcx, T> for T {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for Binder<'tcx, PredicateKind<'tcx>> {
|
||||
impl<'tcx> ToPredicate<'tcx> for Binder<'tcx, PredicateKind<'tcx>> {
|
||||
#[inline(always)]
|
||||
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
|
||||
tcx.mk_predicate(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for Clause<'tcx> {
|
||||
impl<'tcx> ToPredicate<'tcx> for Clause<'tcx> {
|
||||
#[inline(always)]
|
||||
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
|
||||
tcx.mk_predicate(ty::Binder::dummy(ty::PredicateKind::Clause(self)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for Binder<'tcx, TraitRef<'tcx>> {
|
||||
impl<'tcx> ToPredicate<'tcx> for Binder<'tcx, TraitRef<'tcx>> {
|
||||
#[inline(always)]
|
||||
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
|
||||
let pred: PolyTraitPredicate<'tcx> = self.to_predicate(tcx);
|
||||
|
@ -1193,25 +1193,25 @@ impl<'tcx> ToPredicate<'tcx, PolyTraitPredicate<'tcx>> for Binder<'tcx, TraitRef
|
|||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for PolyTraitPredicate<'tcx> {
|
||||
impl<'tcx> ToPredicate<'tcx> for PolyTraitPredicate<'tcx> {
|
||||
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
|
||||
self.map_bound(|p| PredicateKind::Clause(Clause::Trait(p))).to_predicate(tcx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for PolyRegionOutlivesPredicate<'tcx> {
|
||||
impl<'tcx> ToPredicate<'tcx> for PolyRegionOutlivesPredicate<'tcx> {
|
||||
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
|
||||
self.map_bound(|p| PredicateKind::Clause(Clause::RegionOutlives(p))).to_predicate(tcx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for PolyTypeOutlivesPredicate<'tcx> {
|
||||
impl<'tcx> ToPredicate<'tcx> for PolyTypeOutlivesPredicate<'tcx> {
|
||||
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
|
||||
self.map_bound(|p| PredicateKind::Clause(Clause::TypeOutlives(p))).to_predicate(tcx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> ToPredicate<'tcx, Predicate<'tcx>> for PolyProjectionPredicate<'tcx> {
|
||||
impl<'tcx> ToPredicate<'tcx> for PolyProjectionPredicate<'tcx> {
|
||||
fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> {
|
||||
self.map_bound(|p| PredicateKind::Clause(Clause::Projection(p))).to_predicate(tcx)
|
||||
}
|
||||
|
|
|
@ -722,8 +722,17 @@ impl<'tcx> PolyExistentialPredicate<'tcx> {
|
|||
self.rebind(p.with_self_ty(tcx, self_ty)).to_predicate(tcx)
|
||||
}
|
||||
ExistentialPredicate::AutoTrait(did) => {
|
||||
let trait_ref = self.rebind(tcx.mk_trait_ref(did, [self_ty]));
|
||||
trait_ref.without_const().to_predicate(tcx)
|
||||
let generics = tcx.generics_of(did);
|
||||
let trait_ref = if generics.params.len() == 1 {
|
||||
tcx.mk_trait_ref(did, [self_ty])
|
||||
} else {
|
||||
// If this is an ill-formed auto trait, then synthesize
|
||||
// new error substs for the missing generics.
|
||||
let err_substs =
|
||||
ty::InternalSubsts::extend_with_error(tcx, did, &[self_ty.into()]);
|
||||
tcx.mk_trait_ref(did, err_substs)
|
||||
};
|
||||
self.rebind(trait_ref).without_const().to_predicate(tcx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -352,6 +352,22 @@ impl<'tcx> InternalSubsts<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
// Extend an `original_substs` list to the full number of substs expected by `def_id`,
|
||||
// filling in the missing parameters with error ty/ct or 'static regions.
|
||||
pub fn extend_with_error(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: DefId,
|
||||
original_substs: &[GenericArg<'tcx>],
|
||||
) -> SubstsRef<'tcx> {
|
||||
ty::InternalSubsts::for_item(tcx, def_id, |def, substs| {
|
||||
if let Some(subst) = original_substs.get(def.index as usize) {
|
||||
*subst
|
||||
} else {
|
||||
def.to_error(tcx, substs)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn types(&'tcx self) -> impl DoubleEndedIterator<Item = Ty<'tcx>> + 'tcx {
|
||||
self.iter()
|
||||
|
|
|
@ -1414,7 +1414,10 @@ impl<'a> Parser<'a> {
|
|||
|
||||
Ok((Some(vr), TrailingToken::MaybeComma))
|
||||
},
|
||||
)
|
||||
).map_err(|mut err|{
|
||||
err.help("enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`");
|
||||
err
|
||||
})
|
||||
}
|
||||
|
||||
/// Parses `struct Foo { ... }`.
|
||||
|
|
|
@ -943,6 +943,10 @@ impl<'a> Parser<'a> {
|
|||
Err(e) => {
|
||||
// Parsing failed, therefore it must be something more serious
|
||||
// than just a missing separator.
|
||||
for xx in &e.children {
|
||||
// propagate the help message from sub error 'e' to main error 'expect_err;
|
||||
expect_err.children.push(xx.clone());
|
||||
}
|
||||
expect_err.emit();
|
||||
|
||||
e.cancel();
|
||||
|
|
|
@ -70,7 +70,7 @@ pub fn codegen_select_candidate<'tcx>(
|
|||
// `rustc_ty_utils::resolve_associated_item` doesn't return `None` post-monomorphization.
|
||||
for err in errors {
|
||||
if let FulfillmentErrorCode::CodeCycle(cycle) = err.code {
|
||||
infcx.err_ctxt().report_overflow_error_cycle(&cycle);
|
||||
infcx.err_ctxt().report_overflow_obligation_cycle(&cycle);
|
||||
}
|
||||
}
|
||||
return Err(CodegenObligationError::FulfillmentError);
|
||||
|
|
|
@ -25,15 +25,13 @@ use crate::traits::ObligationCtxt;
|
|||
#[instrument(skip(infcx), level = "debug")]
|
||||
pub fn is_const_evaluatable<'tcx>(
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
ct: ty::Const<'tcx>,
|
||||
unexpanded_ct: ty::Const<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
span: Span,
|
||||
) -> Result<(), NotConstEvaluatable> {
|
||||
let tcx = infcx.tcx;
|
||||
let uv = match ct.kind() {
|
||||
ty::ConstKind::Unevaluated(uv) => uv,
|
||||
// FIXME(generic_const_exprs): this seems wrong but I couldn't find a way to get this to trigger
|
||||
ty::ConstKind::Expr(_) => bug!("unexpected expr in `is_const_evaluatable: {ct:?}"),
|
||||
match unexpanded_ct.kind() {
|
||||
ty::ConstKind::Unevaluated(_) | ty::ConstKind::Expr(_) => (),
|
||||
ty::ConstKind::Param(_)
|
||||
| ty::ConstKind::Bound(_, _)
|
||||
| ty::ConstKind::Placeholder(_)
|
||||
|
@ -43,7 +41,7 @@ pub fn is_const_evaluatable<'tcx>(
|
|||
};
|
||||
|
||||
if tcx.features().generic_const_exprs {
|
||||
let ct = tcx.expand_abstract_consts(ct);
|
||||
let ct = tcx.expand_abstract_consts(unexpanded_ct);
|
||||
|
||||
let is_anon_ct = if let ty::ConstKind::Unevaluated(uv) = ct.kind() {
|
||||
tcx.def_kind(uv.def.did) == DefKind::AnonConst
|
||||
|
@ -62,18 +60,40 @@ pub fn is_const_evaluatable<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
|
||||
match concrete {
|
||||
Err(ErrorHandled::TooGeneric) => Err(NotConstEvaluatable::Error(
|
||||
infcx
|
||||
.tcx
|
||||
.sess
|
||||
.delay_span_bug(span, "Missing value for constant, but no error reported?"),
|
||||
)),
|
||||
Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)),
|
||||
Ok(_) => Ok(()),
|
||||
match unexpanded_ct.kind() {
|
||||
ty::ConstKind::Expr(_) => {
|
||||
// FIXME(generic_const_exprs): we have a `ConstKind::Expr` which is fully concrete, but
|
||||
// currently it is not possible to evaluate `ConstKind::Expr` so we are unable to tell if it
|
||||
// is evaluatable or not. For now we just ICE until this is implemented this.
|
||||
Err(NotConstEvaluatable::Error(tcx.sess.delay_span_bug(
|
||||
span,
|
||||
"evaluating `ConstKind::Expr` is not currently supported",
|
||||
)))
|
||||
}
|
||||
ty::ConstKind::Unevaluated(uv) => {
|
||||
let concrete = infcx.const_eval_resolve(param_env, uv, Some(span));
|
||||
match concrete {
|
||||
Err(ErrorHandled::TooGeneric) => {
|
||||
Err(NotConstEvaluatable::Error(infcx.tcx.sess.delay_span_bug(
|
||||
span,
|
||||
"Missing value for constant, but no error reported?",
|
||||
)))
|
||||
}
|
||||
Err(ErrorHandled::Reported(e)) => Err(NotConstEvaluatable::Error(e)),
|
||||
Ok(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
_ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"),
|
||||
}
|
||||
} else {
|
||||
let uv = match unexpanded_ct.kind() {
|
||||
ty::ConstKind::Unevaluated(uv) => uv,
|
||||
ty::ConstKind::Expr(_) => {
|
||||
bug!("`ConstKind::Expr` without `feature(generic_const_exprs)` enabled")
|
||||
}
|
||||
_ => bug!("unexpected constkind in `is_const_evalautable: {unexpanded_ct:?}`"),
|
||||
};
|
||||
|
||||
// FIXME: We should only try to evaluate a given constant here if it is fully concrete
|
||||
// as we don't want to allow things like `[u8; std::mem::size_of::<*mut T>()]`.
|
||||
//
|
||||
|
@ -92,7 +112,7 @@ pub fn is_const_evaluatable<'tcx>(
|
|||
&& satisfied_from_param_env(
|
||||
tcx,
|
||||
infcx,
|
||||
tcx.expand_abstract_consts(ct),
|
||||
tcx.expand_abstract_consts(unexpanded_ct),
|
||||
param_env,
|
||||
) =>
|
||||
{
|
||||
|
@ -152,6 +172,7 @@ fn satisfied_from_param_env<'tcx>(
|
|||
impl<'a, 'tcx> TypeVisitor<'tcx> for Visitor<'a, 'tcx> {
|
||||
type BreakTy = ();
|
||||
fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
|
||||
debug!("is_const_evaluatable: candidate={:?}", c);
|
||||
if let Ok(()) = self.infcx.commit_if_ok(|_| {
|
||||
let ocx = ObligationCtxt::new_in_snapshot(self.infcx);
|
||||
if let Ok(()) = ocx.eq(&ObligationCause::dummy(), self.param_env, c.ty(), self.ct.ty())
|
||||
|
@ -187,7 +208,7 @@ fn satisfied_from_param_env<'tcx>(
|
|||
let result = b_ct.visit_with(&mut v);
|
||||
|
||||
if let ControlFlow::Break(()) = result {
|
||||
debug!("is_const_evaluatable: abstract_const ~~> ok");
|
||||
debug!("is_const_evaluatable: yes");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -195,5 +216,6 @@ fn satisfied_from_param_env<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
debug!("is_const_evaluatable: no");
|
||||
false
|
||||
}
|
||||
|
|
|
@ -99,16 +99,12 @@ pub trait InferCtxtExt<'tcx> {
|
|||
}
|
||||
|
||||
pub trait TypeErrCtxtExt<'tcx> {
|
||||
fn report_fulfillment_errors(
|
||||
&self,
|
||||
errors: &[FulfillmentError<'tcx>],
|
||||
body_id: Option<hir::BodyId>,
|
||||
) -> ErrorGuaranteed;
|
||||
|
||||
fn report_overflow_error<T>(
|
||||
&self,
|
||||
obligation: &Obligation<'tcx, T>,
|
||||
predicate: &T,
|
||||
span: Span,
|
||||
suggest_increasing_limit: bool,
|
||||
mutate: impl FnOnce(&mut Diagnostic),
|
||||
) -> !
|
||||
where
|
||||
T: fmt::Display
|
||||
|
@ -116,9 +112,23 @@ pub trait TypeErrCtxtExt<'tcx> {
|
|||
+ Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
|
||||
<T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug;
|
||||
|
||||
fn report_fulfillment_errors(
|
||||
&self,
|
||||
errors: &[FulfillmentError<'tcx>],
|
||||
body_id: Option<hir::BodyId>,
|
||||
) -> ErrorGuaranteed;
|
||||
|
||||
fn report_overflow_obligation<T>(
|
||||
&self,
|
||||
obligation: &Obligation<'tcx, T>,
|
||||
suggest_increasing_limit: bool,
|
||||
) -> !
|
||||
where
|
||||
T: ToPredicate<'tcx> + Clone;
|
||||
|
||||
fn suggest_new_overflow_limit(&self, err: &mut Diagnostic);
|
||||
|
||||
fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> !;
|
||||
fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> !;
|
||||
|
||||
/// The `root_obligation` parameter should be the `root_obligation` field
|
||||
/// from a `FulfillmentError`. If no `FulfillmentError` is available,
|
||||
|
@ -458,8 +468,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
/// occurrences in any case.
|
||||
fn report_overflow_error<T>(
|
||||
&self,
|
||||
obligation: &Obligation<'tcx, T>,
|
||||
predicate: &T,
|
||||
span: Span,
|
||||
suggest_increasing_limit: bool,
|
||||
mutate: impl FnOnce(&mut Diagnostic),
|
||||
) -> !
|
||||
where
|
||||
T: fmt::Display
|
||||
|
@ -467,8 +479,9 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
+ Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
|
||||
<T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug,
|
||||
{
|
||||
let predicate = self.resolve_vars_if_possible(obligation.predicate.clone());
|
||||
let predicate = self.resolve_vars_if_possible(predicate.clone());
|
||||
let mut pred_str = predicate.to_string();
|
||||
|
||||
if pred_str.len() > 50 {
|
||||
// We don't need to save the type to a file, we will be talking about this type already
|
||||
// in a separate note when we explain the obligation, so it will be available that way.
|
||||
|
@ -483,7 +496,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
}
|
||||
let mut err = struct_span_err!(
|
||||
self.tcx.sess,
|
||||
obligation.cause.span,
|
||||
span,
|
||||
E0275,
|
||||
"overflow evaluating the requirement `{}`",
|
||||
pred_str,
|
||||
|
@ -493,20 +506,46 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
self.suggest_new_overflow_limit(&mut err);
|
||||
}
|
||||
|
||||
self.note_obligation_cause_code(
|
||||
&mut err,
|
||||
&obligation.predicate,
|
||||
obligation.param_env,
|
||||
obligation.cause.code(),
|
||||
&mut vec![],
|
||||
&mut Default::default(),
|
||||
);
|
||||
mutate(&mut err);
|
||||
|
||||
err.emit();
|
||||
self.tcx.sess.abort_if_errors();
|
||||
bug!();
|
||||
}
|
||||
|
||||
/// Reports that an overflow has occurred and halts compilation. We
|
||||
/// halt compilation unconditionally because it is important that
|
||||
/// overflows never be masked -- they basically represent computations
|
||||
/// whose result could not be truly determined and thus we can't say
|
||||
/// if the program type checks or not -- and they are unusual
|
||||
/// occurrences in any case.
|
||||
fn report_overflow_obligation<T>(
|
||||
&self,
|
||||
obligation: &Obligation<'tcx, T>,
|
||||
suggest_increasing_limit: bool,
|
||||
) -> !
|
||||
where
|
||||
T: ToPredicate<'tcx> + Clone,
|
||||
{
|
||||
let predicate = obligation.predicate.clone().to_predicate(self.tcx);
|
||||
let predicate = self.resolve_vars_if_possible(predicate);
|
||||
self.report_overflow_error(
|
||||
&predicate,
|
||||
obligation.cause.span,
|
||||
suggest_increasing_limit,
|
||||
|err| {
|
||||
self.note_obligation_cause_code(
|
||||
err,
|
||||
&predicate,
|
||||
obligation.param_env,
|
||||
obligation.cause.code(),
|
||||
&mut vec![],
|
||||
&mut Default::default(),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn suggest_new_overflow_limit(&self, err: &mut Diagnostic) {
|
||||
let suggested_limit = match self.tcx.recursion_limit() {
|
||||
Limit(0) => Limit(2),
|
||||
|
@ -521,11 +560,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
}
|
||||
|
||||
/// Reports that a cycle was detected which led to overflow and halts
|
||||
/// compilation. This is equivalent to `report_overflow_error` except
|
||||
/// compilation. This is equivalent to `report_overflow_obligation` except
|
||||
/// that we can give a more helpful error message (and, in particular,
|
||||
/// we do not suggest increasing the overflow limit, which is not
|
||||
/// going to help).
|
||||
fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! {
|
||||
fn report_overflow_obligation_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> ! {
|
||||
let cycle = self.resolve_vars_if_possible(cycle.to_owned());
|
||||
assert!(!cycle.is_empty());
|
||||
|
||||
|
@ -533,7 +572,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
|
||||
// The 'deepest' obligation is most likely to have a useful
|
||||
// cause 'backtrace'
|
||||
self.report_overflow_error(cycle.iter().max_by_key(|p| p.recursion_depth).unwrap(), false);
|
||||
self.report_overflow_obligation(
|
||||
cycle.iter().max_by_key(|p| p.recursion_depth).unwrap(),
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
fn report_selection_error(
|
||||
|
@ -1554,7 +1596,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
diag.emit();
|
||||
}
|
||||
FulfillmentErrorCode::CodeCycle(ref cycle) => {
|
||||
self.report_overflow_error_cycle(cycle);
|
||||
self.report_overflow_obligation_cycle(cycle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1607,7 +1649,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
| ObligationCauseCode::ObjectCastObligation(..)
|
||||
| ObligationCauseCode::OpaqueType
|
||||
);
|
||||
let expected_ty = data.term.ty().unwrap();
|
||||
let expected_ty = data.term.ty().unwrap_or_else(|| self.tcx.ty_error());
|
||||
|
||||
// constrain inference variables a bit more to nested obligations from normalize so
|
||||
// we can have more helpful errors.
|
||||
|
|
|
@ -298,7 +298,7 @@ pub trait TypeErrCtxtExt<'tcx> {
|
|||
obligated_types: &mut Vec<Ty<'tcx>>,
|
||||
seen_requirements: &mut FxHashSet<DefId>,
|
||||
) where
|
||||
T: fmt::Display + ToPredicate<'tcx, T>;
|
||||
T: fmt::Display + ToPredicate<'tcx>;
|
||||
|
||||
/// Suggest to await before try: future? => future.await?
|
||||
fn suggest_await_before_try(
|
||||
|
@ -2353,7 +2353,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
obligated_types: &mut Vec<Ty<'tcx>>,
|
||||
seen_requirements: &mut FxHashSet<DefId>,
|
||||
) where
|
||||
T: fmt::Display,
|
||||
T: fmt::Display + ToPredicate<'tcx>,
|
||||
{
|
||||
let tcx = self.tcx;
|
||||
match *cause_code {
|
||||
|
|
|
@ -150,7 +150,7 @@ pub fn type_known_to_meet_bound_modulo_regions<'tcx>(
|
|||
fn pred_known_to_hold_modulo_regions<'tcx>(
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
pred: impl ToPredicate<'tcx, ty::Predicate<'tcx>> + TypeVisitable<'tcx>,
|
||||
pred: impl ToPredicate<'tcx> + TypeVisitable<'tcx>,
|
||||
span: Span,
|
||||
) -> bool {
|
||||
let has_non_region_infer = pred.has_non_region_infer();
|
||||
|
|
|
@ -504,14 +504,12 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> {
|
|||
Reveal::All => {
|
||||
let recursion_limit = self.tcx().recursion_limit();
|
||||
if !recursion_limit.value_within_limit(self.depth) {
|
||||
let obligation = Obligation::with_depth(
|
||||
self.tcx(),
|
||||
self.cause.clone(),
|
||||
recursion_limit.0,
|
||||
self.param_env,
|
||||
ty,
|
||||
self.selcx.infcx.err_ctxt().report_overflow_error(
|
||||
&ty,
|
||||
self.cause.span,
|
||||
true,
|
||||
|_| {},
|
||||
);
|
||||
self.selcx.infcx.err_ctxt().report_overflow_error(&obligation, true);
|
||||
}
|
||||
|
||||
let substs = substs.fold_with(self);
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::infer::canonical::OriginalQueryValues;
|
|||
use crate::infer::{InferCtxt, InferOk};
|
||||
use crate::traits::error_reporting::TypeErrCtxtExt;
|
||||
use crate::traits::project::{needs_normalization, BoundVarReplacer, PlaceholderReplacer};
|
||||
use crate::traits::{Obligation, ObligationCause, PredicateObligation, Reveal};
|
||||
use crate::traits::{ObligationCause, PredicateObligation, Reveal};
|
||||
use rustc_data_structures::sso::SsoHashMap;
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_infer::traits::Normalized;
|
||||
|
@ -214,14 +214,12 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> {
|
|||
let substs = substs.try_fold_with(self)?;
|
||||
let recursion_limit = self.tcx().recursion_limit();
|
||||
if !recursion_limit.value_within_limit(self.anon_depth) {
|
||||
let obligation = Obligation::with_depth(
|
||||
self.tcx(),
|
||||
self.cause.clone(),
|
||||
recursion_limit.0,
|
||||
self.param_env,
|
||||
ty,
|
||||
self.infcx.err_ctxt().report_overflow_error(
|
||||
&ty,
|
||||
self.cause.span,
|
||||
true,
|
||||
|_| {},
|
||||
);
|
||||
self.infcx.err_ctxt().report_overflow_error(&obligation, true);
|
||||
}
|
||||
|
||||
let generic_ty = self.tcx().bound_type_of(def_id);
|
||||
|
|
|
@ -43,7 +43,6 @@ use rustc_middle::mir::interpret::ErrorHandled;
|
|||
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
|
||||
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
|
||||
use rustc_middle::ty::fold::BottomUpFolder;
|
||||
use rustc_middle::ty::print::{FmtPrinter, Print};
|
||||
use rustc_middle::ty::relate::TypeRelation;
|
||||
use rustc_middle::ty::SubstsRef;
|
||||
use rustc_middle::ty::{self, EarlyBinder, PolyProjectionPredicate, ToPolyTraitRef, ToPredicate};
|
||||
|
@ -1313,10 +1312,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
error_obligation: &Obligation<'tcx, T>,
|
||||
) -> Result<(), OverflowError>
|
||||
where
|
||||
T: fmt::Display
|
||||
+ TypeFoldable<'tcx>
|
||||
+ Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
|
||||
<T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug,
|
||||
T: ToPredicate<'tcx> + Clone,
|
||||
{
|
||||
if !self.infcx.tcx.recursion_limit().value_within_limit(depth) {
|
||||
match self.query_mode {
|
||||
|
@ -1324,7 +1320,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
if let Some(e) = self.infcx.tainted_by_errors() {
|
||||
return Err(OverflowError::Error(e));
|
||||
}
|
||||
self.infcx.err_ctxt().report_overflow_error(error_obligation, true);
|
||||
self.infcx.err_ctxt().report_overflow_obligation(error_obligation, true);
|
||||
}
|
||||
TraitQueryMode::Canonical => {
|
||||
return Err(OverflowError::Canonical);
|
||||
|
@ -1345,10 +1341,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
error_obligation: &Obligation<'tcx, V>,
|
||||
) -> Result<(), OverflowError>
|
||||
where
|
||||
V: fmt::Display
|
||||
+ TypeFoldable<'tcx>
|
||||
+ Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
|
||||
<V as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug,
|
||||
V: ToPredicate<'tcx> + Clone,
|
||||
{
|
||||
self.check_recursion_depth(obligation.recursion_depth, error_obligation)
|
||||
}
|
||||
|
|
|
@ -476,9 +476,24 @@ impl<'tcx> WfPredicates<'tcx> {
|
|||
ty::Binder::dummy(ty::PredicateKind::WellFormed(ct.into())),
|
||||
));
|
||||
}
|
||||
// FIXME(generic_const_exprs): This seems wrong but I could not find a way to get this to trigger
|
||||
ty::ConstKind::Expr(_) => {
|
||||
bug!("checking wfness of `ConstKind::Expr` is unsupported")
|
||||
// FIXME(generic_const_exprs): this doesnt verify that given `Expr(N + 1)` the
|
||||
// trait bound `typeof(N): Add<typeof(1)>` holds. This is currently unnecessary
|
||||
// as `ConstKind::Expr` is only produced via normalization of `ConstKind::Unevaluated`
|
||||
// which means that the `DefId` would have been typeck'd elsewhere. However in
|
||||
// the future we may allow directly lowering to `ConstKind::Expr` in which case
|
||||
// we would not be proving bounds we should.
|
||||
|
||||
let predicate =
|
||||
ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ct));
|
||||
let cause = self.cause(traits::WellFormed(None));
|
||||
self.out.push(traits::Obligation::with_depth(
|
||||
self.tcx(),
|
||||
cause,
|
||||
self.recursion_depth,
|
||||
self.param_env,
|
||||
predicate,
|
||||
));
|
||||
}
|
||||
|
||||
ty::ConstKind::Error(_)
|
||||
|
|
|
@ -255,6 +255,16 @@ changelog-seen = 2
|
|||
# Defaults to the Python interpreter used to execute x.py
|
||||
#python = "python"
|
||||
|
||||
# The path to the REUSE executable to use. Note that REUSE is not required in
|
||||
# most cases, as our tooling relies on a cached (and shrinked) copy of the
|
||||
# REUSE output present in the git repository and in our source tarballs.
|
||||
#
|
||||
# REUSE is only needed if your changes caused the overral licensing of the
|
||||
# repository to change, and the cached copy has to be regenerated.
|
||||
#
|
||||
# Defaults to the "reuse" command in the system path.
|
||||
#reuse = "reuse"
|
||||
|
||||
# Force Cargo to check that Cargo.lock describes the precise dependency
|
||||
# set that all the Cargo.toml files create, instead of updating it.
|
||||
#locked-deps = false
|
||||
|
|
|
@ -629,9 +629,7 @@ impl<T> Clone for Sender<T> {
|
|||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> Drop for Sender<T> {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.inner;
|
||||
}
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
|
||||
#[stable(feature = "mpsc_debug", since = "1.8.0")]
|
||||
|
@ -751,9 +749,7 @@ impl<T> Clone for SyncSender<T> {
|
|||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> Drop for SyncSender<T> {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.inner;
|
||||
}
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
|
||||
#[stable(feature = "mpsc_debug", since = "1.8.0")]
|
||||
|
@ -1094,9 +1090,7 @@ impl<T> IntoIterator for Receiver<T> {
|
|||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
impl<T> Drop for Receiver<T> {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.inner;
|
||||
}
|
||||
fn drop(&mut self) {}
|
||||
}
|
||||
|
||||
#[stable(feature = "mpsc_debug", since = "1.8.0")]
|
||||
|
|
|
@ -754,6 +754,8 @@ impl<'a> Builder<'a> {
|
|||
run::BumpStage0,
|
||||
run::ReplaceVersionPlaceholder,
|
||||
run::Miri,
|
||||
run::CollectLicenseMetadata,
|
||||
run::GenerateCopyright,
|
||||
),
|
||||
// These commands either don't use paths, or they're special-cased in Build::build()
|
||||
Kind::Clean | Kind::Format | Kind::Setup => vec![],
|
||||
|
|
|
@ -213,6 +213,7 @@ pub struct Config {
|
|||
pub npm: Option<PathBuf>,
|
||||
pub gdb: Option<PathBuf>,
|
||||
pub python: Option<PathBuf>,
|
||||
pub reuse: Option<PathBuf>,
|
||||
pub cargo_native_static: bool,
|
||||
pub configure_args: Vec<String>,
|
||||
|
||||
|
@ -611,6 +612,7 @@ define_config! {
|
|||
nodejs: Option<String> = "nodejs",
|
||||
npm: Option<String> = "npm",
|
||||
python: Option<String> = "python",
|
||||
reuse: Option<String> = "reuse",
|
||||
locked_deps: Option<bool> = "locked-deps",
|
||||
vendor: Option<bool> = "vendor",
|
||||
full_bootstrap: Option<bool> = "full-bootstrap",
|
||||
|
@ -1004,6 +1006,7 @@ impl Config {
|
|||
config.npm = build.npm.map(PathBuf::from);
|
||||
config.gdb = build.gdb.map(PathBuf::from);
|
||||
config.python = build.python.map(PathBuf::from);
|
||||
config.reuse = build.reuse.map(PathBuf::from);
|
||||
config.submodules = build.submodules;
|
||||
set(&mut config.low_priority, build.low_priority);
|
||||
set(&mut config.compiler_docs, build.compiler_docs);
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use std::path::PathBuf;
|
||||
use std::process::Command;
|
||||
|
||||
use crate::builder::{Builder, RunConfig, ShouldRun, Step};
|
||||
|
@ -189,3 +190,65 @@ impl Step for Miri {
|
|||
builder.run(&mut miri);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct CollectLicenseMetadata;
|
||||
|
||||
impl Step for CollectLicenseMetadata {
|
||||
type Output = PathBuf;
|
||||
const ONLY_HOSTS: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
|
||||
run.path("src/tools/collect-license-metadata")
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig<'_>) {
|
||||
run.builder.ensure(CollectLicenseMetadata);
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder<'_>) -> Self::Output {
|
||||
let Some(reuse) = &builder.config.reuse else {
|
||||
panic!("REUSE is required to collect the license metadata");
|
||||
};
|
||||
|
||||
// Temporary location, it will be moved to src/etc once it's accurate.
|
||||
let dest = builder.out.join("license-metadata.json");
|
||||
|
||||
let mut cmd = builder.tool_cmd(Tool::CollectLicenseMetadata);
|
||||
cmd.env("REUSE_EXE", reuse);
|
||||
cmd.env("DEST", &dest);
|
||||
builder.run(&mut cmd);
|
||||
|
||||
dest
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
|
||||
pub struct GenerateCopyright;
|
||||
|
||||
impl Step for GenerateCopyright {
|
||||
type Output = PathBuf;
|
||||
const ONLY_HOSTS: bool = true;
|
||||
|
||||
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
|
||||
run.path("src/tools/generate-copyright")
|
||||
}
|
||||
|
||||
fn make_run(run: RunConfig<'_>) {
|
||||
run.builder.ensure(GenerateCopyright);
|
||||
}
|
||||
|
||||
fn run(self, builder: &Builder<'_>) -> Self::Output {
|
||||
let license_metadata = builder.ensure(CollectLicenseMetadata);
|
||||
|
||||
// Temporary location, it will be moved to the proper one once it's accurate.
|
||||
let dest = builder.out.join("COPYRIGHT.md");
|
||||
|
||||
let mut cmd = builder.tool_cmd(Tool::GenerateCopyright);
|
||||
cmd.env("LICENSE_METADATA", &license_metadata);
|
||||
cmd.env("DEST", &dest);
|
||||
builder.run(&mut cmd);
|
||||
|
||||
dest
|
||||
}
|
||||
}
|
||||
|
|
|
@ -140,6 +140,13 @@ than building it.
|
|||
.map(|p| cmd_finder.must_have(p))
|
||||
.or_else(|| cmd_finder.maybe_have("gdb"));
|
||||
|
||||
build.config.reuse = build
|
||||
.config
|
||||
.reuse
|
||||
.take()
|
||||
.map(|p| cmd_finder.must_have(p))
|
||||
.or_else(|| cmd_finder.maybe_have("reuse"));
|
||||
|
||||
// We're gonna build some custom C code here and there, host triples
|
||||
// also build some C++ shims for LLVM so we need a C++ compiler.
|
||||
for target in &build.targets {
|
||||
|
|
|
@ -380,6 +380,8 @@ bootstrap_tool!(
|
|||
HtmlChecker, "src/tools/html-checker", "html-checker";
|
||||
BumpStage0, "src/tools/bump-stage0", "bump-stage0";
|
||||
ReplaceVersionPlaceholder, "src/tools/replace-version-placeholder", "replace-version-placeholder";
|
||||
CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata";
|
||||
GenerateCopyright, "src/tools/generate-copyright", "generate-copyright";
|
||||
);
|
||||
|
||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
// edition: 2021
|
||||
|
||||
#![feature(async_fn_in_trait)]
|
||||
//~^ WARN the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
|
||||
trait A {
|
||||
async fn e() {
|
||||
Ok(())
|
||||
//~^ ERROR mismatched types
|
||||
//~| HELP consider using a semicolon here
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,23 @@
|
|||
warning: the feature `async_fn_in_trait` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||
--> $DIR/return-type-suggestion.rs:3:12
|
||||
|
|
||||
LL | #![feature(async_fn_in_trait)]
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #91611 <https://github.com/rust-lang/rust/issues/91611> for more information
|
||||
= note: `#[warn(incomplete_features)]` on by default
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/return-type-suggestion.rs:8:9
|
||||
|
|
||||
LL | Ok(())
|
||||
| ^^^^^^- help: consider using a semicolon here: `;`
|
||||
| |
|
||||
| expected `()`, found enum `Result`
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found enum `Result<(), _>`
|
||||
|
||||
error: aborting due to previous error; 1 warning emitted
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
|
@ -0,0 +1,11 @@
|
|||
#![feature(auto_traits)]
|
||||
|
||||
auto trait Trait1<'a> {}
|
||||
//~^ ERROR auto traits cannot have generic parameters
|
||||
|
||||
fn f<'a>(x: &dyn Trait1<'a>)
|
||||
{}
|
||||
|
||||
fn main() {
|
||||
f(&1);
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
error[E0567]: auto traits cannot have generic parameters
|
||||
--> $DIR/bad-generics-on-dyn.rs:3:18
|
||||
|
|
||||
LL | auto trait Trait1<'a> {}
|
||||
| ------^^^^ help: remove the parameters
|
||||
| |
|
||||
| auto trait cannot have generic parameters
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0567`.
|
|
@ -0,0 +1,22 @@
|
|||
#![feature(generic_const_exprs, generic_arg_infer)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
// minimized repro for #105205
|
||||
//
|
||||
// the `foo::<_, L>` call results in a `WellFormed(_)` obligation and a
|
||||
// `ConstEvaluatable(Unevaluated(_ + 1 + L))` obligation. Attempting to fulfill the latter
|
||||
// unifies the `_` with `Expr(L - 1)` from the paramenv which turns the `WellFormed`
|
||||
// obligation into `WellFormed(Expr(L - 1))`
|
||||
|
||||
fn foo<const N: usize, const M: usize>(_: [(); N + 1 + M]) {}
|
||||
|
||||
fn ice<const L: usize>()
|
||||
where
|
||||
[(); (L - 1) + 1 + L]:,
|
||||
{
|
||||
foo::<_, L>([(); L + 1 + L]);
|
||||
//~^ ERROR: mismatched types
|
||||
//~^^ ERROR: unconstrained generic constant
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,20 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/wf_obligation.rs:17:17
|
||||
|
|
||||
LL | foo::<_, L>([(); L + 1 + L]);
|
||||
| ^^^^^^^^^^^^^^^ expected `N + 1 + M`, found `L + 1 + L`
|
||||
|
|
||||
= note: expected constant `N + 1 + M`
|
||||
found constant `L + 1 + L`
|
||||
|
||||
error: unconstrained generic constant
|
||||
--> $DIR/wf_obligation.rs:17:22
|
||||
|
|
||||
LL | foo::<_, L>([(); L + 1 + L]);
|
||||
| ^^^^^^^^^
|
||||
|
|
||||
= help: try adding a `where` bound using this expression: `where [(); L + 1 + L]:`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
|
@ -0,0 +1,8 @@
|
|||
// edition: 2021
|
||||
|
||||
fn test<'s: 's>(s: &'s str) -> impl std::future::Future<Output = impl Sized> {
|
||||
async move { let _s = s; }
|
||||
//~^ ERROR hidden type for `impl Future<Output = impl Sized>` captures lifetime that does not appear in bounds
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,20 @@
|
|||
error[E0700]: hidden type for `impl Future<Output = impl Sized>` captures lifetime that does not appear in bounds
|
||||
--> $DIR/nested-return-type4.rs:4:5
|
||||
|
|
||||
LL | fn test<'s: 's>(s: &'s str) -> impl std::future::Future<Output = impl Sized> {
|
||||
| -- hidden type `[async block@$DIR/nested-return-type4.rs:4:5: 4:31]` captures the lifetime `'s` as defined here
|
||||
LL | async move { let _s = s; }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: to declare that `impl Future<Output = impl Sized>` captures `'s`, you can add an explicit `'s` lifetime bound
|
||||
|
|
||||
LL | fn test<'s: 's>(s: &'s str) -> impl std::future::Future<Output = impl Sized> + 's {
|
||||
| ++++
|
||||
help: to declare that `impl Sized` captures `'s`, you can add an explicit `'s` lifetime bound
|
||||
|
|
||||
LL | fn test<'s: 's>(s: &'s str) -> impl std::future::Future<Output = impl Sized + 's> {
|
||||
| ++++
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0700`.
|
|
@ -0,0 +1,21 @@
|
|||
pub trait TraitWAssocConst {
|
||||
const A: usize;
|
||||
}
|
||||
pub struct Demo {}
|
||||
|
||||
impl TraitWAssocConst for impl Demo { //~ ERROR E0404
|
||||
//~^ ERROR E0562
|
||||
pubconst A: str = 32; //~ ERROR expected one of
|
||||
}
|
||||
|
||||
fn foo<A: TraitWAssocConst<A=32>>() { //~ ERROR E0658
|
||||
foo::<Demo>()(); //~ ERROR E0271
|
||||
//~^ ERROR E0618
|
||||
//~| ERROR E0277
|
||||
}
|
||||
|
||||
fn main<A: TraitWAssocConst<A=32>>() { //~ ERROR E0131
|
||||
//~^ ERROR E0658
|
||||
foo::<Demo>(); //~ ERROR E0277
|
||||
//~^ ERROR E0271
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
error: expected one of `!` or `::`, found `A`
|
||||
--> $DIR/issue-105330.rs:8:14
|
||||
|
|
||||
LL | impl TraitWAssocConst for impl Demo {
|
||||
| - while parsing this item list starting here
|
||||
LL |
|
||||
LL | pubconst A: str = 32;
|
||||
| ^ expected one of `!` or `::`
|
||||
LL | }
|
||||
| - the item list ends here
|
||||
|
||||
error[E0404]: expected trait, found struct `Demo`
|
||||
--> $DIR/issue-105330.rs:6:32
|
||||
|
|
||||
LL | impl TraitWAssocConst for impl Demo {
|
||||
| ^^^^ not a trait
|
||||
|
||||
error[E0658]: associated const equality is incomplete
|
||||
--> $DIR/issue-105330.rs:11:28
|
||||
|
|
||||
LL | fn foo<A: TraitWAssocConst<A=32>>() {
|
||||
| ^^^^
|
||||
|
|
||||
= note: see issue #92827 <https://github.com/rust-lang/rust/issues/92827> for more information
|
||||
= help: add `#![feature(associated_const_equality)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: associated const equality is incomplete
|
||||
--> $DIR/issue-105330.rs:17:29
|
||||
|
|
||||
LL | fn main<A: TraitWAssocConst<A=32>>() {
|
||||
| ^^^^
|
||||
|
|
||||
= note: see issue #92827 <https://github.com/rust-lang/rust/issues/92827> for more information
|
||||
= help: add `#![feature(associated_const_equality)]` to the crate attributes to enable
|
||||
|
||||
error[E0562]: `impl Trait` only allowed in function and inherent method return types, not in type
|
||||
--> $DIR/issue-105330.rs:6:27
|
||||
|
|
||||
LL | impl TraitWAssocConst for impl Demo {
|
||||
| ^^^^^^^^^
|
||||
|
||||
error[E0277]: the trait bound `Demo: TraitWAssocConst` is not satisfied
|
||||
--> $DIR/issue-105330.rs:12:11
|
||||
|
|
||||
LL | foo::<Demo>()();
|
||||
| ^^^^ the trait `TraitWAssocConst` is not implemented for `Demo`
|
||||
|
|
||||
note: required by a bound in `foo`
|
||||
--> $DIR/issue-105330.rs:11:11
|
||||
|
|
||||
LL | fn foo<A: TraitWAssocConst<A=32>>() {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `foo`
|
||||
|
||||
error[E0271]: type mismatch resolving `<Demo as TraitWAssocConst>::A == 32`
|
||||
--> $DIR/issue-105330.rs:12:11
|
||||
|
|
||||
LL | foo::<Demo>()();
|
||||
| ^^^^ types differ
|
||||
|
|
||||
note: required by a bound in `foo`
|
||||
--> $DIR/issue-105330.rs:11:28
|
||||
|
|
||||
LL | fn foo<A: TraitWAssocConst<A=32>>() {
|
||||
| ^^^^ required by this bound in `foo`
|
||||
|
||||
error[E0618]: expected function, found `()`
|
||||
--> $DIR/issue-105330.rs:12:5
|
||||
|
|
||||
LL | fn foo<A: TraitWAssocConst<A=32>>() {
|
||||
| ----------------------------------- `foo::<Demo>` defined here returns `()`
|
||||
LL | foo::<Demo>()();
|
||||
| ^^^^^^^^^^^^^--
|
||||
| |
|
||||
| call expression requires function
|
||||
|
||||
error[E0277]: the trait bound `Demo: TraitWAssocConst` is not satisfied
|
||||
--> $DIR/issue-105330.rs:19:11
|
||||
|
|
||||
LL | foo::<Demo>();
|
||||
| ^^^^ the trait `TraitWAssocConst` is not implemented for `Demo`
|
||||
|
|
||||
note: required by a bound in `foo`
|
||||
--> $DIR/issue-105330.rs:11:11
|
||||
|
|
||||
LL | fn foo<A: TraitWAssocConst<A=32>>() {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `foo`
|
||||
|
||||
error[E0271]: type mismatch resolving `<Demo as TraitWAssocConst>::A == 32`
|
||||
--> $DIR/issue-105330.rs:19:11
|
||||
|
|
||||
LL | foo::<Demo>();
|
||||
| ^^^^ types differ
|
||||
|
|
||||
note: required by a bound in `foo`
|
||||
--> $DIR/issue-105330.rs:11:28
|
||||
|
|
||||
LL | fn foo<A: TraitWAssocConst<A=32>>() {
|
||||
| ^^^^ required by this bound in `foo`
|
||||
|
||||
error[E0131]: `main` function is not allowed to have generic parameters
|
||||
--> $DIR/issue-105330.rs:17:8
|
||||
|
|
||||
LL | fn main<A: TraitWAssocConst<A=32>>() {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ `main` cannot have generic parameters
|
||||
|
||||
error: aborting due to 11 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0131, E0271, E0277, E0404, E0562, E0618, E0658.
|
||||
For more information about an error, try `rustc --explain E0131`.
|
|
@ -7,6 +7,7 @@ LL | $token $($inner)? = $value,
|
|||
LL | values!(STRING(1) as (String) => cfg(test),);
|
||||
| -------------------------------------------- in this macro invocation
|
||||
|
|
||||
= help: enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`
|
||||
= note: this error originates in the macro `values` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: macro expansion ignores token `(String)` and any following
|
||||
|
|
|
@ -3,6 +3,8 @@ error: unexpected `==`
|
|||
|
|
||||
LL | B == 2
|
||||
| ^^ help: try using `=` instead
|
||||
|
|
||||
= help: enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`
|
||||
|
||||
error: expected item, found `==`
|
||||
--> $DIR/issue-101477-enum.rs:6:7
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
enum VecOrMap{
|
||||
vec: Vec<usize>,
|
||||
//~^ ERROR expected one of `(`, `,`, `=`, `{`, or `}`, found `:`
|
||||
//~| HELP: enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`
|
||||
//~| ERROR expected item, found `:`
|
||||
map: HashMap<String,usize>
|
||||
}
|
||||
|
||||
fn main() {}
|
|
@ -0,0 +1,16 @@
|
|||
error: expected one of `(`, `,`, `=`, `{`, or `}`, found `:`
|
||||
--> $DIR/issue-103869.rs:2:8
|
||||
|
|
||||
LL | vec: Vec<usize>,
|
||||
| ^ expected one of `(`, `,`, `=`, `{`, or `}`
|
||||
|
|
||||
= help: enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`
|
||||
|
||||
error: expected item, found `:`
|
||||
--> $DIR/issue-103869.rs:2:8
|
||||
|
|
||||
LL | vec: Vec<usize>,
|
||||
| ^ expected item
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
|
@ -9,6 +9,7 @@ LL | $( $t, )*
|
|||
LL | test_macro!(String,);
|
||||
| -------------------- in this macro invocation
|
||||
|
|
||||
= help: enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`
|
||||
= note: this error originates in the macro `test_macro` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
error: aborting due to previous error
|
||||
|
|
|
@ -28,6 +28,7 @@ enum E {
|
|||
//~^ ERROR functions are not allowed in enum definitions
|
||||
//~| HELP unlike in C++, Java, and C#, functions are declared in `impl` blocks
|
||||
//~| HELP see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information
|
||||
//~| HELP enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
|
|
@ -33,6 +33,7 @@ LL | fn foo() {}
|
|||
|
|
||||
= help: unlike in C++, Java, and C#, functions are declared in `impl` blocks
|
||||
= help: see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information
|
||||
= help: enum variants can be `Variant`, `Variant = <integer>`, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
trait Trait<T> {
|
||||
fn foo<'a, K>(self, _: T, _: K) where T: 'a, K: 'a;
|
||||
}
|
||||
|
||||
impl Trait<()> for () {
|
||||
fn foo<'a, K>(self, _: (), _: K) where { //~ ERROR E0195
|
||||
todo!();
|
||||
}
|
||||
}
|
||||
|
||||
struct State;
|
||||
|
||||
trait Foo<T> {
|
||||
fn foo<'a>(&self, state: &'a State) -> &'a T
|
||||
where
|
||||
T: 'a;
|
||||
}
|
||||
|
||||
impl<F, T> Foo<T> for F
|
||||
where
|
||||
F: Fn(&State) -> &T,
|
||||
{
|
||||
fn foo<'a>(&self, state: &'a State) -> &'a T { //~ ERROR E0195
|
||||
self(state)
|
||||
}
|
||||
}
|
||||
|
||||
trait Bar {
|
||||
fn foo<'a>(&'a self) {}
|
||||
}
|
||||
|
||||
impl Bar for () {
|
||||
fn foo<'a: 'a>(&'a self) {} //~ ERROR E0195
|
||||
}
|
||||
|
||||
fn main() {
|
||||
().foo((), ());
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
error[E0195]: lifetime parameters or bounds on method `foo` do not match the trait declaration
|
||||
--> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:6:11
|
||||
|
|
||||
LL | fn foo<'a, K>(self, _: T, _: K) where T: 'a, K: 'a;
|
||||
| ------- -- -- this bound might be missing in the impl
|
||||
| | |
|
||||
| | this bound might be missing in the impl
|
||||
| lifetimes in impl do not match this method in trait
|
||||
...
|
||||
LL | fn foo<'a, K>(self, _: (), _: K) where {
|
||||
| ^^^^^^^ lifetimes do not match method in trait
|
||||
|
||||
error[E0195]: lifetime parameters or bounds on method `foo` do not match the trait declaration
|
||||
--> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:23:11
|
||||
|
|
||||
LL | fn foo<'a>(&self, state: &'a State) -> &'a T
|
||||
| ---- lifetimes in impl do not match this method in trait
|
||||
LL | where
|
||||
LL | T: 'a;
|
||||
| -- this bound might be missing in the impl
|
||||
...
|
||||
LL | fn foo<'a>(&self, state: &'a State) -> &'a T {
|
||||
| ^^^^ lifetimes do not match method in trait
|
||||
|
||||
error[E0195]: lifetime parameters or bounds on method `foo` do not match the trait declaration
|
||||
--> $DIR/impl-missing-where-clause-lifetimes-from-trait.rs:33:11
|
||||
|
|
||||
LL | fn foo<'a>(&'a self) {}
|
||||
| ---- lifetimes in impl do not match this method in trait
|
||||
...
|
||||
LL | fn foo<'a: 'a>(&'a self) {}
|
||||
| ^^^^^^^^ lifetimes do not match method in trait
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0195`.
|
|
@ -0,0 +1,4 @@
|
|||
fn main() {
|
||||
let page_size = page_size::get();
|
||||
//~^ ERROR failed to resolve: use of undeclared crate or module `page_size`
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
error[E0433]: failed to resolve: use of undeclared crate or module `page_size`
|
||||
--> $DIR/path-to-method-sugg-unresolved-expr.rs:2:21
|
||||
|
|
||||
LL | let page_size = page_size::get();
|
||||
| ^^^^^^^^^ use of undeclared crate or module `page_size`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0433`.
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "collect-license-metadata"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.65"
|
||||
serde = { version = "1.0.147", features = ["derive"] }
|
||||
serde_json = "1.0.85"
|
||||
spdx-rs = "0.5.1"
|
|
@ -0,0 +1,65 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
const COPYRIGHT_PREFIXES: &[&str] = &["SPDX-FileCopyrightText:", "Copyright", "(c)", "(C)", "©"];
|
||||
|
||||
pub(crate) struct LicensesInterner {
|
||||
by_id: Vec<License>,
|
||||
by_struct: HashMap<License, usize>,
|
||||
}
|
||||
|
||||
impl LicensesInterner {
|
||||
pub(crate) fn new() -> Self {
|
||||
LicensesInterner { by_id: Vec::new(), by_struct: HashMap::new() }
|
||||
}
|
||||
|
||||
pub(crate) fn intern(&mut self, mut license: License) -> LicenseId {
|
||||
license.simplify();
|
||||
if let Some(id) = self.by_struct.get(&license) {
|
||||
LicenseId(*id)
|
||||
} else {
|
||||
let id = self.by_id.len();
|
||||
self.by_id.push(license.clone());
|
||||
self.by_struct.insert(license, id);
|
||||
LicenseId(id)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn resolve(&self, id: LicenseId) -> &License {
|
||||
&self.by_id[id.0]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)]
|
||||
#[serde(transparent)]
|
||||
pub(crate) struct LicenseId(usize);
|
||||
|
||||
#[derive(Clone, Hash, PartialEq, Eq, serde::Serialize)]
|
||||
pub(crate) struct License {
|
||||
pub(crate) spdx: String,
|
||||
pub(crate) copyright: Vec<String>,
|
||||
}
|
||||
|
||||
impl License {
|
||||
fn simplify(&mut self) {
|
||||
self.remove_copyright_prefixes();
|
||||
self.copyright.sort();
|
||||
self.copyright.dedup();
|
||||
}
|
||||
|
||||
fn remove_copyright_prefixes(&mut self) {
|
||||
for copyright in &mut self.copyright {
|
||||
let mut stripped = copyright.trim();
|
||||
let mut previous_stripped;
|
||||
loop {
|
||||
previous_stripped = stripped;
|
||||
for pattern in COPYRIGHT_PREFIXES {
|
||||
stripped = stripped.trim_start_matches(pattern).trim_start();
|
||||
}
|
||||
if stripped == previous_stripped {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*copyright = stripped.into();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
mod licenses;
|
||||
mod path_tree;
|
||||
mod reuse;
|
||||
|
||||
use crate::licenses::LicensesInterner;
|
||||
use anyhow::Error;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
let reuse_exe: PathBuf = std::env::var_os("REUSE_EXE").expect("Missing REUSE_EXE").into();
|
||||
let dest: PathBuf = std::env::var_os("DEST").expect("Missing DEST").into();
|
||||
|
||||
let mut interner = LicensesInterner::new();
|
||||
let paths = crate::reuse::collect(&reuse_exe, &mut interner)?;
|
||||
|
||||
let mut tree = crate::path_tree::build(paths);
|
||||
tree.simplify();
|
||||
|
||||
if let Some(parent) = dest.parent() {
|
||||
std::fs::create_dir_all(parent)?;
|
||||
}
|
||||
std::fs::write(
|
||||
&dest,
|
||||
&serde_json::to_vec_pretty(&serde_json::json!({
|
||||
"files": crate::path_tree::expand_interned_licenses(tree, &interner),
|
||||
}))?,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,294 @@
|
|||
//! Tools like REUSE output per-file licensing information, but we need to condense it in the
|
||||
//! minimum amount of data that still represents the same licensing metadata. This module is
|
||||
//! responsible for that, by turning the list of paths into a tree and executing simplification
|
||||
//! passes over the tree to remove redundant information.
|
||||
|
||||
use crate::licenses::{License, LicenseId, LicensesInterner};
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
#[serde(rename_all = "kebab-case", tag = "type")]
|
||||
pub(crate) enum Node<L> {
|
||||
Root { childs: Vec<Node<L>> },
|
||||
Directory { name: PathBuf, childs: Vec<Node<L>>, license: Option<L> },
|
||||
File { name: PathBuf, license: L },
|
||||
FileGroup { names: Vec<PathBuf>, license: L },
|
||||
Empty,
|
||||
}
|
||||
|
||||
impl Node<LicenseId> {
|
||||
pub(crate) fn simplify(&mut self) {
|
||||
self.merge_directories();
|
||||
self.collapse_in_licensed_directories();
|
||||
self.merge_directory_licenses();
|
||||
self.merge_file_groups();
|
||||
self.remove_empty();
|
||||
}
|
||||
|
||||
/// Initially, the build() function constructs a list of separate paths from the file
|
||||
/// system root down to each file, like so:
|
||||
///
|
||||
/// ```text
|
||||
/// ┌─► ./ ──► compiler/ ──► rustc/ ──► src/ ──► main.rs
|
||||
/// │
|
||||
/// <root> ─┼─► ./ ──► compiler/ ──► rustc/ ──► Cargo.toml
|
||||
/// │
|
||||
/// └─► ./ ──► library/ ───► std/ ──► Cargo.toml
|
||||
/// ```
|
||||
///
|
||||
/// This pass is responsible for turning that into a proper directory tree:
|
||||
///
|
||||
/// ```text
|
||||
/// ┌─► compiler/ ──► rustc/ ──┬─► src/ ──► main.rs
|
||||
/// │ │
|
||||
/// <root> ──► ./ ──┤ └─► Cargo.toml
|
||||
/// │
|
||||
/// └─► library/ ───► std/ ──► Cargo.toml
|
||||
/// ```
|
||||
fn merge_directories(&mut self) {
|
||||
match self {
|
||||
Node::Root { childs } | Node::Directory { childs, license: None, .. } => {
|
||||
let mut directories = BTreeMap::new();
|
||||
let mut files = Vec::new();
|
||||
|
||||
for child in childs.drain(..) {
|
||||
match child {
|
||||
Node::Directory { name, mut childs, license: None } => {
|
||||
directories.entry(name).or_insert_with(Vec::new).append(&mut childs);
|
||||
}
|
||||
file @ Node::File { .. } => {
|
||||
files.push(file);
|
||||
}
|
||||
Node::Empty => {}
|
||||
Node::Root { .. } => {
|
||||
panic!("can't have a root inside another element");
|
||||
}
|
||||
Node::FileGroup { .. } => {
|
||||
panic!("FileGroup should not be present at this stage");
|
||||
}
|
||||
Node::Directory { license: Some(_), .. } => {
|
||||
panic!("license should not be set at this stage");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
childs.extend(directories.into_iter().map(|(name, childs)| Node::Directory {
|
||||
name,
|
||||
childs,
|
||||
license: None,
|
||||
}));
|
||||
childs.append(&mut files);
|
||||
|
||||
for child in &mut *childs {
|
||||
child.merge_directories();
|
||||
}
|
||||
}
|
||||
Node::Empty => {}
|
||||
Node::File { .. } => {}
|
||||
Node::FileGroup { .. } => {
|
||||
panic!("FileGroup should not be present at this stage");
|
||||
}
|
||||
Node::Directory { license: Some(_), .. } => {
|
||||
panic!("license should not be set at this stage");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// In our codebase, most files in a directory have the same license as the other files in that
|
||||
/// same directory, so it's redundant to store licensing metadata for all the files. Instead,
|
||||
/// we can add a license for a whole directory, and only record the exceptions to a directory
|
||||
/// licensing metadata.
|
||||
///
|
||||
/// We cannot instead record only the difference to Rust's standard licensing, as the majority
|
||||
/// of the files in our repository are *not* licensed under Rust's standard licensing due to
|
||||
/// our inclusion of LLVM.
|
||||
fn collapse_in_licensed_directories(&mut self) {
|
||||
match self {
|
||||
Node::Directory { childs, license, .. } => {
|
||||
for child in &mut *childs {
|
||||
child.collapse_in_licensed_directories();
|
||||
}
|
||||
|
||||
let mut licenses_count = BTreeMap::new();
|
||||
for child in &*childs {
|
||||
let Some(license) = child.license() else { continue };
|
||||
*licenses_count.entry(license).or_insert(0) += 1;
|
||||
}
|
||||
|
||||
let most_popular_license = licenses_count
|
||||
.into_iter()
|
||||
.max_by_key(|(_, count)| *count)
|
||||
.map(|(license, _)| license);
|
||||
|
||||
if let Some(most_popular_license) = most_popular_license {
|
||||
childs.retain(|child| child.license() != Some(most_popular_license));
|
||||
*license = Some(most_popular_license);
|
||||
}
|
||||
}
|
||||
Node::Root { childs } => {
|
||||
for child in &mut *childs {
|
||||
child.collapse_in_licensed_directories();
|
||||
}
|
||||
}
|
||||
Node::File { .. } => {}
|
||||
Node::FileGroup { .. } => {}
|
||||
Node::Empty => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reduce the depth of the tree by merging subdirectories with the same license as their
|
||||
/// parent directory into their parent, and adjusting the paths of the childs accordingly.
|
||||
fn merge_directory_licenses(&mut self) {
|
||||
match self {
|
||||
Node::Root { childs } => {
|
||||
for child in &mut *childs {
|
||||
child.merge_directory_licenses();
|
||||
}
|
||||
}
|
||||
Node::Directory { childs, license, .. } => {
|
||||
let mut to_add = Vec::new();
|
||||
for child in &mut *childs {
|
||||
child.merge_directory_licenses();
|
||||
|
||||
let Node::Directory {
|
||||
name: child_name,
|
||||
childs: child_childs,
|
||||
license: child_license,
|
||||
} = child else { continue };
|
||||
|
||||
if child_license != license {
|
||||
continue;
|
||||
}
|
||||
for mut child_child in child_childs.drain(..) {
|
||||
match &mut child_child {
|
||||
Node::Root { .. } => {
|
||||
panic!("can't have a root inside another element");
|
||||
}
|
||||
Node::FileGroup { .. } => {
|
||||
panic!("FileGroup should not be present at this stage");
|
||||
}
|
||||
Node::Directory { name: child_child_name, .. } => {
|
||||
*child_child_name = child_name.join(&child_child_name);
|
||||
}
|
||||
Node::File { name: child_child_name, .. } => {
|
||||
*child_child_name = child_name.join(&child_child_name);
|
||||
}
|
||||
Node::Empty => {}
|
||||
}
|
||||
to_add.push(child_child);
|
||||
}
|
||||
|
||||
*child = Node::Empty;
|
||||
}
|
||||
childs.append(&mut to_add);
|
||||
}
|
||||
Node::Empty => {}
|
||||
Node::File { .. } => {}
|
||||
Node::FileGroup { .. } => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// This pass groups multiple files in a directory with the same license into a single
|
||||
/// "FileGroup", so that the license of all those files can be reported as a group.
|
||||
///
|
||||
/// Crucially this pass runs after collapse_in_licensed_directories, so the most common license
|
||||
/// will already be marked as the directory's license and won't be turned into a group.
|
||||
fn merge_file_groups(&mut self) {
|
||||
match self {
|
||||
Node::Root { childs } | Node::Directory { childs, .. } => {
|
||||
let mut grouped = BTreeMap::new();
|
||||
|
||||
for child in &mut *childs {
|
||||
child.merge_file_groups();
|
||||
if let Node::File { name, license } = child {
|
||||
grouped.entry(*license).or_insert_with(Vec::new).push(name.clone());
|
||||
*child = Node::Empty;
|
||||
}
|
||||
}
|
||||
|
||||
for (license, mut names) in grouped.into_iter() {
|
||||
if names.len() == 1 {
|
||||
childs.push(Node::File { license, name: names.pop().unwrap() });
|
||||
} else {
|
||||
childs.push(Node::FileGroup { license, names });
|
||||
}
|
||||
}
|
||||
}
|
||||
Node::File { .. } => {}
|
||||
Node::FileGroup { .. } => panic!("FileGroup should not be present at this stage"),
|
||||
Node::Empty => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Some nodes were replaced with Node::Empty to mark them for deletion. As the last step, make
|
||||
/// sure to remove them from the tree.
|
||||
fn remove_empty(&mut self) {
|
||||
match self {
|
||||
Node::Root { childs } | Node::Directory { childs, .. } => {
|
||||
for child in &mut *childs {
|
||||
child.remove_empty();
|
||||
}
|
||||
childs.retain(|child| !matches!(child, Node::Empty));
|
||||
}
|
||||
Node::FileGroup { .. } => {}
|
||||
Node::File { .. } => {}
|
||||
Node::Empty => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn license(&self) -> Option<LicenseId> {
|
||||
match self {
|
||||
Node::Directory { childs, license: Some(license), .. } if childs.is_empty() => {
|
||||
Some(*license)
|
||||
}
|
||||
Node::File { license, .. } => Some(*license),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn build(mut input: Vec<(PathBuf, LicenseId)>) -> Node<LicenseId> {
|
||||
let mut childs = Vec::new();
|
||||
|
||||
// Ensure reproducibility of all future steps.
|
||||
input.sort();
|
||||
|
||||
for (path, license) in input {
|
||||
let mut node = Node::File { name: path.file_name().unwrap().into(), license };
|
||||
for component in path.parent().unwrap_or_else(|| Path::new(".")).components().rev() {
|
||||
node = Node::Directory {
|
||||
name: component.as_os_str().into(),
|
||||
childs: vec![node],
|
||||
license: None,
|
||||
};
|
||||
}
|
||||
|
||||
childs.push(node);
|
||||
}
|
||||
|
||||
Node::Root { childs }
|
||||
}
|
||||
|
||||
/// Convert a `Node<LicenseId>` into a `Node<&License>`, expanding all interned license IDs with a
|
||||
/// reference to the actual license metadata.
|
||||
pub(crate) fn expand_interned_licenses(
|
||||
node: Node<LicenseId>,
|
||||
interner: &LicensesInterner,
|
||||
) -> Node<&License> {
|
||||
match node {
|
||||
Node::Root { childs } => Node::Root {
|
||||
childs: childs.into_iter().map(|child| strip_interning(child, interner)).collect(),
|
||||
},
|
||||
Node::Directory { name, childs, license } => Node::Directory {
|
||||
childs: childs.into_iter().map(|child| strip_interning(child, interner)).collect(),
|
||||
license: license.map(|license| interner.resolve(license)),
|
||||
name,
|
||||
},
|
||||
Node::File { name, license } => Node::File { name, license: interner.resolve(license) },
|
||||
Node::FileGroup { names, license } => {
|
||||
Node::FileGroup { names, license: interner.resolve(license) }
|
||||
}
|
||||
Node::Empty => Node::Empty,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
use crate::licenses::{License, LicenseId, LicensesInterner};
|
||||
use anyhow::Error;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Stdio};
|
||||
use std::time::Instant;
|
||||
|
||||
pub(crate) fn collect(
|
||||
reuse_exe: &Path,
|
||||
interner: &mut LicensesInterner,
|
||||
) -> Result<Vec<(PathBuf, LicenseId)>, Error> {
|
||||
eprintln!("gathering license information from REUSE");
|
||||
let start = Instant::now();
|
||||
let raw = &obtain_spdx_document(reuse_exe)?;
|
||||
eprintln!("finished gathering the license information from REUSE in {:.2?}", start.elapsed());
|
||||
|
||||
let document = spdx_rs::parsers::spdx_from_tag_value(&raw)?;
|
||||
|
||||
let mut result = Vec::new();
|
||||
for file in document.file_information {
|
||||
let license = interner.intern(License {
|
||||
spdx: file.concluded_license.to_string(),
|
||||
copyright: file.copyright_text.split('\n').map(|s| s.into()).collect(),
|
||||
});
|
||||
|
||||
result.push((file.file_name.into(), license));
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn obtain_spdx_document(reuse_exe: &Path) -> Result<String, Error> {
|
||||
let output = Command::new(reuse_exe)
|
||||
.args(&["spdx", "--add-license-concluded", "--creator-person=bors"])
|
||||
.stdout(Stdio::piped())
|
||||
.spawn()?
|
||||
.wait_with_output()?;
|
||||
|
||||
if !output.status.success() {
|
||||
eprintln!();
|
||||
eprintln!("Note that Rust requires some REUSE features that might not be present in the");
|
||||
eprintln!("release you're using. Make sure your REUSE release includes these PRs:");
|
||||
eprintln!();
|
||||
eprintln!(" - https://github.com/fsfe/reuse-tool/pull/623");
|
||||
eprintln!();
|
||||
anyhow::bail!("collecting licensing information with REUSE failed");
|
||||
}
|
||||
|
||||
Ok(String::from_utf8(output.stdout)?)
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
name = "generate-copyright"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.65"
|
||||
serde = { version = "1.0.147", features = ["derive"] }
|
||||
serde_json = "1.0.85"
|
|
@ -0,0 +1,94 @@
|
|||
use anyhow::Error;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
let dest = env_path("DEST")?;
|
||||
let license_metadata = env_path("LICENSE_METADATA")?;
|
||||
|
||||
let metadata: Metadata = serde_json::from_slice(&std::fs::read(&license_metadata)?)?;
|
||||
|
||||
let mut buffer = Vec::new();
|
||||
render_recursive(&metadata.files, &mut buffer, 0)?;
|
||||
|
||||
std::fs::write(&dest, &buffer)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn render_recursive(node: &Node, buffer: &mut Vec<u8>, depth: usize) -> Result<(), Error> {
|
||||
let prefix = std::iter::repeat("> ").take(depth + 1).collect::<String>();
|
||||
|
||||
match node {
|
||||
Node::Root { childs } => {
|
||||
for child in childs {
|
||||
render_recursive(child, buffer, depth)?;
|
||||
}
|
||||
}
|
||||
Node::Directory { name, childs, license } => {
|
||||
render_license(&prefix, std::iter::once(name), license, buffer)?;
|
||||
if !childs.is_empty() {
|
||||
writeln!(buffer, "{prefix}")?;
|
||||
writeln!(buffer, "{prefix}*Exceptions:*")?;
|
||||
for child in childs {
|
||||
writeln!(buffer, "{prefix}")?;
|
||||
render_recursive(child, buffer, depth + 1)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Node::FileGroup { names, license } => {
|
||||
render_license(&prefix, names.iter(), license, buffer)?;
|
||||
}
|
||||
Node::File { name, license } => {
|
||||
render_license(&prefix, std::iter::once(name), license, buffer)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn render_license<'a>(
|
||||
prefix: &str,
|
||||
names: impl Iterator<Item = &'a String>,
|
||||
license: &License,
|
||||
buffer: &mut Vec<u8>,
|
||||
) -> Result<(), Error> {
|
||||
for name in names {
|
||||
writeln!(buffer, "{prefix}**`{name}`** ")?;
|
||||
}
|
||||
writeln!(buffer, "{prefix}License: `{}` ", license.spdx)?;
|
||||
for (i, copyright) in license.copyright.iter().enumerate() {
|
||||
let suffix = if i == license.copyright.len() - 1 { "" } else { " " };
|
||||
writeln!(buffer, "{prefix}Copyright: {copyright}{suffix}")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct Metadata {
|
||||
files: Node,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(rename_all = "kebab-case", tag = "type")]
|
||||
pub(crate) enum Node {
|
||||
Root { childs: Vec<Node> },
|
||||
Directory { name: String, childs: Vec<Node>, license: License },
|
||||
File { name: String, license: License },
|
||||
FileGroup { names: Vec<String>, license: License },
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct License {
|
||||
spdx: String,
|
||||
copyright: Vec<String>,
|
||||
}
|
||||
|
||||
fn env_path(var: &str) -> Result<PathBuf, Error> {
|
||||
if let Some(var) = std::env::var_os(var) {
|
||||
Ok(var.into())
|
||||
} else {
|
||||
anyhow::bail!("missing environment variable {var}")
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue