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:
bors 2022-12-06 12:29:38 +00:00
commit b6852428a8
59 changed files with 1406 additions and 124 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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![],

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,4 @@
fn main() {
let page_size = page_size::get();
//~^ ERROR failed to resolve: use of undeclared crate or module `page_size`
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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