Auto merge of #129365 - matthiaskrgr:rollup-ebwx6ya, r=matthiaskrgr

Rollup of 9 pull requests

Successful merges:

 - #127279 (use old ctx if has same expand environment during decode span)
 - #127945 (Implement `debug_more_non_exhaustive`)
 - #128941 ( Improve diagnostic-related lints: `untranslatable_diagnostic` & `diagnostic_outside_of_impl`)
 - #129070 (Point at explicit `'static` obligations on a trait)
 - #129187 (bootstrap: fix clean's remove_dir_all implementation)
 - #129231 (improve submodule updates)
 - #129264 (Update `library/Cargo.toml` in weekly job)
 - #129284 (rustdoc: animate the `:target` highlight)
 - #129302 (compiletest: use `std::fs::remove_dir_all` now that it is available)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-08-22 05:17:27 +00:00
commit 739b1fdb15
25 changed files with 986 additions and 367 deletions

View File

@ -64,6 +64,10 @@ jobs:
- name: cargo update
# Remove first line that always just says "Updating crates.io index"
run: cargo update 2>&1 | sed '/crates.io index/d' | tee -a cargo_update.log
- name: cargo update library
run: |
echo -e "\nlibrary dependencies:" >> cargo_update.log
cargo update --manifest-path library/Cargo.toml 2>&1 | sed '/crates.io index/d' | tee -a cargo_update.log
- name: cargo update rustbook
run: |
echo -e "\nrustbook dependencies:" >> cargo_update.log
@ -74,6 +78,7 @@ jobs:
name: Cargo-lock
path: |
Cargo.lock
library/Cargo.lock
src/tools/rustbook/Cargo.lock
retention-days: 1
- name: upload cargo-update log artifact for use in PR
@ -119,7 +124,7 @@ jobs:
git config user.name github-actions
git config user.email github-actions@github.com
git switch --force-create cargo_update
git add ./Cargo.lock ./src/tools/rustbook/Cargo.lock
git add ./Cargo.lock ./library/Cargo.lock ./src/tools/rustbook/Cargo.lock
git commit --no-verify --file=commit.txt
- name: push

View File

@ -747,7 +747,7 @@ fn region_known_to_outlive<'tcx>(
region_b: ty::Region<'tcx>,
) -> bool {
test_region_obligations(tcx, id, param_env, wf_tys, |infcx| {
infcx.sub_regions(infer::RelateRegionParamBound(DUMMY_SP), region_b, region_a);
infcx.sub_regions(infer::RelateRegionParamBound(DUMMY_SP, None), region_b, region_a);
})
}

View File

@ -167,7 +167,7 @@ impl<'tcx> InferCtxtLike for InferCtxt<'tcx> {
}
fn sub_regions(&self, sub: ty::Region<'tcx>, sup: ty::Region<'tcx>) {
self.sub_regions(SubregionOrigin::RelateRegionParamBound(DUMMY_SP), sub, sup)
self.sub_regions(SubregionOrigin::RelateRegionParamBound(DUMMY_SP, None), sub, sup)
}
fn register_ty_outlives(&self, ty: Ty<'tcx>, r: ty::Region<'tcx>) {

View File

@ -390,7 +390,7 @@ pub enum SubregionOrigin<'tcx> {
/// The given region parameter was instantiated with a region
/// that must outlive some other region.
RelateRegionParamBound(Span),
RelateRegionParamBound(Span, Option<Ty<'tcx>>),
/// Creating a pointer `b` to contents of another reference.
Reborrow(Span),
@ -859,7 +859,7 @@ impl<'tcx> InferCtxt<'tcx> {
) {
self.enter_forall(predicate, |ty::OutlivesPredicate(r_a, r_b)| {
let origin = SubregionOrigin::from_obligation_cause(cause, || {
RelateRegionParamBound(cause.span)
RelateRegionParamBound(cause.span, None)
});
self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b`
})
@ -1685,7 +1685,7 @@ impl<'tcx> SubregionOrigin<'tcx> {
Subtype(ref a) => a.span(),
RelateObjectBound(a) => a,
RelateParamBound(a, ..) => a,
RelateRegionParamBound(a) => a,
RelateRegionParamBound(a, _) => a,
Reborrow(a) => a,
ReferenceOutlivesReferent(_, a) => a,
CompareImplItemObligation { span, .. } => span,
@ -1726,6 +1726,10 @@ impl<'tcx> SubregionOrigin<'tcx> {
SubregionOrigin::AscribeUserTypeProvePredicate(span)
}
traits::ObligationCauseCode::ObjectTypeBound(ty, _reg) => {
SubregionOrigin::RelateRegionParamBound(cause.span, Some(ty))
}
_ => default(),
}
}

View File

@ -8,7 +8,7 @@ use rustc_hir::{
BinOp, BinOpKind, Expr, ExprKind, GenericArg, HirId, Impl, Item, ItemKind, Node, Pat, PatKind,
Path, PathSegment, QPath, Ty, TyKind,
};
use rustc_middle::ty::{self, Ty as MiddleTy};
use rustc_middle::ty::{self, GenericArgsRef, Ty as MiddleTy};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::hygiene::{ExpnKind, MacroKind};
use rustc_span::symbol::{kw, sym, Symbol};
@ -415,14 +415,17 @@ declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE
impl LateLintPass<'_> for Diagnostics {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
let collect_args_tys_and_spans = |args: &[Expr<'_>], reserve_one_extra: bool| {
let mut result = Vec::with_capacity(args.len() + usize::from(reserve_one_extra));
result.extend(args.iter().map(|arg| (cx.typeck_results().expr_ty(arg), arg.span)));
result
};
// Only check function calls and method calls.
let (span, def_id, fn_gen_args, call_tys) = match expr.kind {
let (span, def_id, fn_gen_args, arg_tys_and_spans) = match expr.kind {
ExprKind::Call(callee, args) => {
match cx.typeck_results().node_type(callee.hir_id).kind() {
&ty::FnDef(def_id, fn_gen_args) => {
let call_tys: Vec<_> =
args.iter().map(|arg| cx.typeck_results().expr_ty(arg)).collect();
(callee.span, def_id, fn_gen_args, call_tys)
(callee.span, def_id, fn_gen_args, collect_args_tys_and_spans(args, false))
}
_ => return, // occurs for fns passed as args
}
@ -432,38 +435,40 @@ impl LateLintPass<'_> for Diagnostics {
else {
return;
};
let mut call_tys: Vec<_> =
args.iter().map(|arg| cx.typeck_results().expr_ty(arg)).collect();
call_tys.insert(0, cx.tcx.types.self_param); // dummy inserted for `self`
(span, def_id, fn_gen_args, call_tys)
let mut args = collect_args_tys_and_spans(args, true);
args.insert(0, (cx.tcx.types.self_param, _recv.span)); // dummy inserted for `self`
(span, def_id, fn_gen_args, args)
}
_ => return,
};
// Is the callee marked with `#[rustc_lint_diagnostics]`?
let has_attr = ty::Instance::try_resolve(cx.tcx, cx.param_env, def_id, fn_gen_args)
.ok()
.flatten()
.is_some_and(|inst| cx.tcx.has_attr(inst.def_id(), sym::rustc_lint_diagnostics));
Self::diagnostic_outside_of_impl(cx, span, expr.hir_id, def_id, fn_gen_args);
Self::untranslatable_diagnostic(cx, def_id, &arg_tys_and_spans);
}
}
// Closure: is the type `{D,Subd}iagMessage`?
let is_diag_message = |ty: MiddleTy<'_>| {
if let Some(adt_def) = ty.ty_adt_def()
&& let Some(name) = cx.tcx.get_diagnostic_name(adt_def.did())
&& matches!(name, sym::DiagMessage | sym::SubdiagMessage)
{
true
} else {
false
}
};
impl Diagnostics {
// Is the type `{D,Subd}iagMessage`?
fn is_diag_message<'cx>(cx: &LateContext<'cx>, ty: MiddleTy<'cx>) -> bool {
if let Some(adt_def) = ty.ty_adt_def()
&& let Some(name) = cx.tcx.get_diagnostic_name(adt_def.did())
&& matches!(name, sym::DiagMessage | sym::SubdiagMessage)
{
true
} else {
false
}
}
// Does the callee have one or more `impl Into<{D,Subd}iagMessage>` parameters?
let mut impl_into_diagnostic_message_params = vec![];
fn untranslatable_diagnostic<'cx>(
cx: &LateContext<'cx>,
def_id: DefId,
arg_tys_and_spans: &[(MiddleTy<'cx>, Span)],
) {
let fn_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder();
let predicates = cx.tcx.predicates_of(def_id).instantiate_identity(cx.tcx).predicates;
for (i, &param_ty) in fn_sig.inputs().iter().enumerate() {
if let ty::Param(p) = param_ty.kind() {
if let ty::Param(sig_param) = param_ty.kind() {
// It is a type parameter. Check if it is `impl Into<{D,Subd}iagMessage>`.
for pred in predicates.iter() {
if let Some(trait_pred) = pred.as_trait_clause()
@ -471,27 +476,53 @@ impl LateLintPass<'_> for Diagnostics {
&& trait_ref.self_ty() == param_ty // correct predicate for the param?
&& cx.tcx.is_diagnostic_item(sym::Into, trait_ref.def_id)
&& let ty1 = trait_ref.args.type_at(1)
&& is_diag_message(ty1)
&& Self::is_diag_message(cx, ty1)
{
impl_into_diagnostic_message_params.push((i, p.name));
// Calls to methods with an `impl Into<{D,Subd}iagMessage>` parameter must be passed an arg
// with type `{D,Subd}iagMessage` or `impl Into<{D,Subd}iagMessage>`. Otherwise, emit an
// `UNTRANSLATABLE_DIAGNOSTIC` lint.
let (arg_ty, arg_span) = arg_tys_and_spans[i];
// Is the arg type `{Sub,D}iagMessage`or `impl Into<{Sub,D}iagMessage>`?
let is_translatable = Self::is_diag_message(cx, arg_ty)
|| matches!(arg_ty.kind(), ty::Param(arg_param) if arg_param.name == sig_param.name);
if !is_translatable {
cx.emit_span_lint(
UNTRANSLATABLE_DIAGNOSTIC,
arg_span,
UntranslatableDiag,
);
}
}
}
}
}
}
// Is the callee interesting?
if !has_attr && impl_into_diagnostic_message_params.is_empty() {
fn diagnostic_outside_of_impl<'cx>(
cx: &LateContext<'cx>,
span: Span,
current_id: HirId,
def_id: DefId,
fn_gen_args: GenericArgsRef<'cx>,
) {
// Is the callee marked with `#[rustc_lint_diagnostics]`?
let Some(inst) =
ty::Instance::try_resolve(cx.tcx, cx.param_env, def_id, fn_gen_args).ok().flatten()
else {
return;
}
};
let has_attr = cx.tcx.has_attr(inst.def_id(), sym::rustc_lint_diagnostics);
if !has_attr {
return;
};
// Is the parent method marked with `#[rustc_lint_diagnostics]`?
let mut parent_has_attr = false;
for (hir_id, _parent) in cx.tcx.hir().parent_iter(expr.hir_id) {
for (hir_id, _parent) in cx.tcx.hir().parent_iter(current_id) {
if let Some(owner_did) = hir_id.as_owner()
&& cx.tcx.has_attr(owner_did, sym::rustc_lint_diagnostics)
{
parent_has_attr = true;
break;
// The parent method is marked with `#[rustc_lint_diagnostics]`
return;
}
}
@ -500,37 +531,22 @@ impl LateLintPass<'_> for Diagnostics {
// - inside a parent function that is itself marked with `#[rustc_lint_diagnostics]`.
//
// Otherwise, emit a `DIAGNOSTIC_OUTSIDE_OF_IMPL` lint.
if has_attr && !parent_has_attr {
let mut is_inside_appropriate_impl = false;
for (_hir_id, parent) in cx.tcx.hir().parent_iter(expr.hir_id) {
debug!(?parent);
if let Node::Item(Item { kind: ItemKind::Impl(impl_), .. }) = parent
&& let Impl { of_trait: Some(of_trait), .. } = impl_
&& let Some(def_id) = of_trait.trait_def_id()
&& let Some(name) = cx.tcx.get_diagnostic_name(def_id)
&& matches!(name, sym::Diagnostic | sym::Subdiagnostic | sym::LintDiagnostic)
{
is_inside_appropriate_impl = true;
break;
}
}
debug!(?is_inside_appropriate_impl);
if !is_inside_appropriate_impl {
cx.emit_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, DiagOutOfImpl);
let mut is_inside_appropriate_impl = false;
for (_hir_id, parent) in cx.tcx.hir().parent_iter(current_id) {
debug!(?parent);
if let Node::Item(Item { kind: ItemKind::Impl(impl_), .. }) = parent
&& let Impl { of_trait: Some(of_trait), .. } = impl_
&& let Some(def_id) = of_trait.trait_def_id()
&& let Some(name) = cx.tcx.get_diagnostic_name(def_id)
&& matches!(name, sym::Diagnostic | sym::Subdiagnostic | sym::LintDiagnostic)
{
is_inside_appropriate_impl = true;
break;
}
}
// Calls to methods with an `impl Into<{D,Subd}iagMessage>` parameter must be passed an arg
// with type `{D,Subd}iagMessage` or `impl Into<{D,Subd}iagMessage>`. Otherwise, emit an
// `UNTRANSLATABLE_DIAGNOSTIC` lint.
for (param_i, param_i_p_name) in impl_into_diagnostic_message_params {
// Is the arg type `{Sub,D}iagMessage`or `impl Into<{Sub,D}iagMessage>`?
let arg_ty = call_tys[param_i];
let is_translatable = is_diag_message(arg_ty)
|| matches!(arg_ty.kind(), ty::Param(p) if p.name == param_i_p_name);
if !is_translatable {
cx.emit_span_lint(UNTRANSLATABLE_DIAGNOSTIC, span, UntranslatableDiag);
}
debug!(?is_inside_appropriate_impl);
if !is_inside_appropriate_impl {
cx.emit_span_lint(DIAGNOSTIC_OUTSIDE_OF_IMPL, span, DiagOutOfImpl);
}
}
}

View File

@ -1415,6 +1415,14 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext
// Overwrite the dummy data with our decoded SyntaxContextData
HygieneData::with(|hygiene_data| {
if let Some(old) = hygiene_data.syntax_context_data.get(raw_id as usize)
&& old.outer_expn == ctxt_data.outer_expn
&& old.outer_transparency == ctxt_data.outer_transparency
&& old.parent == ctxt_data.parent
{
ctxt_data = old.clone();
}
let dummy = std::mem::replace(
&mut hygiene_data.syntax_context_data[ctxt.as_u32() as usize],
ctxt_data,

View File

@ -11,7 +11,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
pub(super) fn try_report_placeholder_relation(&self) -> Option<Diag<'tcx>> {
match &self.error {
Some(RegionResolutionError::ConcreteFailure(
SubregionOrigin::RelateRegionParamBound(span),
SubregionOrigin::RelateRegionParamBound(span, _),
Region(Interned(
RePlaceholder(ty::Placeholder {
bound: ty::BoundRegion { kind: sub_name, .. },

View File

@ -52,7 +52,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
.add_to_diag(err);
}
}
infer::RelateRegionParamBound(span) => {
infer::RelateRegionParamBound(span, _) => {
RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound }
.add_to_diag(err);
}
@ -199,7 +199,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
note,
})
}
infer::RelateRegionParamBound(span) => {
infer::RelateRegionParamBound(span, _) => {
let param_instantiated = note_and_explain::RegionExplanation::new(
self.tcx,
generic_param_scope,

View File

@ -257,7 +257,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
.add_to_diag(err);
}
}
infer::RelateRegionParamBound(span) => {
infer::RelateRegionParamBound(span, _) => {
RegionOriginNote::Plain {
span,
msg: fluent::trait_selection_relate_region_param_bound,
@ -410,7 +410,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
note,
})
}
infer::RelateRegionParamBound(span) => {
infer::RelateRegionParamBound(span, ty) => {
let param_instantiated = note_and_explain::RegionExplanation::new(
self.tcx,
generic_param_scope,
@ -419,11 +419,31 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
note_and_explain::PrefixKind::LfParamInstantiatedWith,
note_and_explain::SuffixKind::Empty,
);
let mut alt_span = None;
if let Some(ty) = ty
&& sub.is_static()
&& let ty::Dynamic(preds, _, ty::DynKind::Dyn) = ty.kind()
&& let Some(def_id) = preds.principal_def_id()
{
for (clause, span) in
self.tcx.predicates_of(def_id).instantiate_identity(self.tcx)
{
if let ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(a, b)) =
clause.kind().skip_binder()
&& let ty::Param(param) = a.kind()
&& param.name == kw::SelfUpper
&& b.is_static()
{
// Point at explicit `'static` bound on the trait (`trait T: 'static`).
alt_span = Some(span);
}
}
}
let param_must_outlive = note_and_explain::RegionExplanation::new(
self.tcx,
generic_param_scope,
sub,
None,
alt_span,
note_and_explain::PrefixKind::LfParamMustOutlive,
note_and_explain::SuffixKind::Empty,
);

View File

@ -78,7 +78,7 @@ impl fmt::Write for PadAdapter<'_, '_> {
///
/// assert_eq!(
/// format!("{:?}", Foo { bar: 10, baz: "Hello World".to_string() }),
/// "Foo { bar: 10, baz: \"Hello World\" }",
/// r#"Foo { bar: 10, baz: "Hello World" }"#,
/// );
/// ```
#[must_use = "must eventually call `finish()` on Debug builders"]
@ -125,7 +125,7 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> {
///
/// assert_eq!(
/// format!("{:?}", Bar { bar: 10, another: "Hello World".to_string() }),
/// "Bar { bar: 10, another: \"Hello World\", nonexistent_field: 1 }",
/// r#"Bar { bar: 10, another: "Hello World", nonexistent_field: 1 }"#,
/// );
/// ```
#[stable(feature = "debug_builders", since = "1.2.0")]
@ -237,7 +237,7 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> {
///
/// assert_eq!(
/// format!("{:?}", Bar { bar: 10, baz: "Hello World".to_string() }),
/// "Bar { bar: 10, baz: \"Hello World\" }",
/// r#"Bar { bar: 10, baz: "Hello World" }"#,
/// );
/// ```
#[stable(feature = "debug_builders", since = "1.2.0")]
@ -280,7 +280,7 @@ impl<'a, 'b: 'a> DebugStruct<'a, 'b> {
///
/// assert_eq!(
/// format!("{:?}", Foo(10, "Hello World".to_string())),
/// "Foo(10, \"Hello World\")",
/// r#"Foo(10, "Hello World")"#,
/// );
/// ```
#[must_use = "must eventually call `finish()` on Debug builders"]
@ -322,7 +322,7 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> {
///
/// assert_eq!(
/// format!("{:?}", Foo(10, "Hello World".to_string())),
/// "Foo(10, \"Hello World\")",
/// r#"Foo(10, "Hello World")"#,
/// );
/// ```
#[stable(feature = "debug_builders", since = "1.2.0")]
@ -360,6 +360,51 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> {
self
}
/// Marks the tuple struct as non-exhaustive, indicating to the reader that there are some
/// other fields that are not shown in the debug representation.
///
/// # Examples
///
/// ```
/// #![feature(debug_more_non_exhaustive)]
///
/// use std::fmt;
///
/// struct Foo(i32, String);
///
/// impl fmt::Debug for Foo {
/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
/// fmt.debug_tuple("Foo")
/// .field(&self.0)
/// .finish_non_exhaustive() // Show that some other field(s) exist.
/// }
/// }
///
/// assert_eq!(
/// format!("{:?}", Foo(10, "secret!".to_owned())),
/// "Foo(10, ..)",
/// );
/// ```
#[unstable(feature = "debug_more_non_exhaustive", issue = "127942")]
pub fn finish_non_exhaustive(&mut self) -> fmt::Result {
self.result = self.result.and_then(|_| {
if self.fields > 0 {
if self.is_pretty() {
let mut slot = None;
let mut state = Default::default();
let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut state);
writer.write_str("..\n")?;
self.fmt.write_str(")")
} else {
self.fmt.write_str(", ..)")
}
} else {
self.fmt.write_str("(..)")
}
});
self.result
}
/// Finishes output and returns any error encountered.
///
/// # Examples
@ -381,7 +426,7 @@ impl<'a, 'b: 'a> DebugTuple<'a, 'b> {
///
/// assert_eq!(
/// format!("{:?}", Foo(10, "Hello World".to_string())),
/// "Foo(10, \"Hello World\")",
/// r#"Foo(10, "Hello World")"#,
/// );
/// ```
#[stable(feature = "debug_builders", since = "1.2.0")]
@ -555,6 +600,56 @@ impl<'a, 'b: 'a> DebugSet<'a, 'b> {
self
}
/// Marks the set as non-exhaustive, indicating to the reader that there are some other
/// elements that are not shown in the debug representation.
///
/// # Examples
///
/// ```
/// #![feature(debug_more_non_exhaustive)]
///
/// use std::fmt;
///
/// struct Foo(Vec<i32>);
///
/// impl fmt::Debug for Foo {
/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
/// // Print at most two elements, abbreviate the rest
/// let mut f = fmt.debug_set();
/// let mut f = f.entries(self.0.iter().take(2));
/// if self.0.len() > 2 {
/// f.finish_non_exhaustive()
/// } else {
/// f.finish()
/// }
/// }
/// }
///
/// assert_eq!(
/// format!("{:?}", Foo(vec![1, 2, 3, 4])),
/// "{1, 2, ..}",
/// );
/// ```
#[unstable(feature = "debug_more_non_exhaustive", issue = "127942")]
pub fn finish_non_exhaustive(&mut self) -> fmt::Result {
self.inner.result = self.inner.result.and_then(|_| {
if self.inner.has_fields {
if self.inner.is_pretty() {
let mut slot = None;
let mut state = Default::default();
let mut writer = PadAdapter::wrap(self.inner.fmt, &mut slot, &mut state);
writer.write_str("..\n")?;
self.inner.fmt.write_str("}")
} else {
self.inner.fmt.write_str(", ..}")
}
} else {
self.inner.fmt.write_str("..}")
}
});
self.inner.result
}
/// Finishes output and returns any error encountered.
///
/// # Examples
@ -699,6 +794,55 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> {
self
}
/// Marks the list as non-exhaustive, indicating to the reader that there are some other
/// elements that are not shown in the debug representation.
///
/// # Examples
///
/// ```
/// #![feature(debug_more_non_exhaustive)]
///
/// use std::fmt;
///
/// struct Foo(Vec<i32>);
///
/// impl fmt::Debug for Foo {
/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
/// // Print at most two elements, abbreviate the rest
/// let mut f = fmt.debug_list();
/// let mut f = f.entries(self.0.iter().take(2));
/// if self.0.len() > 2 {
/// f.finish_non_exhaustive()
/// } else {
/// f.finish()
/// }
/// }
/// }
///
/// assert_eq!(
/// format!("{:?}", Foo(vec![1, 2, 3, 4])),
/// "[1, 2, ..]",
/// );
/// ```
#[unstable(feature = "debug_more_non_exhaustive", issue = "127942")]
pub fn finish_non_exhaustive(&mut self) -> fmt::Result {
self.inner.result.and_then(|_| {
if self.inner.has_fields {
if self.inner.is_pretty() {
let mut slot = None;
let mut state = Default::default();
let mut writer = PadAdapter::wrap(self.inner.fmt, &mut slot, &mut state);
writer.write_str("..\n")?;
self.inner.fmt.write_str("]")
} else {
self.inner.fmt.write_str(", ..]")
}
} else {
self.inner.fmt.write_str("..]")
}
})
}
/// Finishes output and returns any error encountered.
///
/// # Examples
@ -750,7 +894,7 @@ impl<'a, 'b: 'a> DebugList<'a, 'b> {
///
/// assert_eq!(
/// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])),
/// "{\"A\": 10, \"B\": 11}",
/// r#"{"A": 10, "B": 11}"#,
/// );
/// ```
#[must_use = "must eventually call `finish()` on Debug builders"]
@ -790,7 +934,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
///
/// assert_eq!(
/// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])),
/// "{\"whole\": [(\"A\", 10), (\"B\", 11)]}",
/// r#"{"whole": [("A", 10), ("B", 11)]}"#,
/// );
/// ```
#[stable(feature = "debug_builders", since = "1.2.0")]
@ -826,7 +970,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
///
/// assert_eq!(
/// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])),
/// "{\"whole\": [(\"A\", 10), (\"B\", 11)]}",
/// r#"{"whole": [("A", 10), ("B", 11)]}"#,
/// );
/// ```
#[stable(feature = "debug_map_key_value", since = "1.42.0")]
@ -902,7 +1046,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
///
/// assert_eq!(
/// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])),
/// "{\"whole\": [(\"A\", 10), (\"B\", 11)]}",
/// r#"{"whole": [("A", 10), ("B", 11)]}"#,
/// );
/// ```
#[stable(feature = "debug_map_key_value", since = "1.42.0")]
@ -960,7 +1104,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
///
/// assert_eq!(
/// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])),
/// "{\"A\": 10, \"B\": 11}",
/// r#"{"A": 10, "B": 11}"#,
/// );
/// ```
#[stable(feature = "debug_builders", since = "1.2.0")]
@ -976,6 +1120,62 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
self
}
/// Marks the map as non-exhaustive, indicating to the reader that there are some other
/// entries that are not shown in the debug representation.
///
/// # Examples
///
/// ```
/// #![feature(debug_more_non_exhaustive)]
///
/// use std::fmt;
///
/// struct Foo(Vec<(String, i32)>);
///
/// impl fmt::Debug for Foo {
/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
/// // Print at most two elements, abbreviate the rest
/// let mut f = fmt.debug_map();
/// let mut f = f.entries(self.0.iter().take(2).map(|&(ref k, ref v)| (k, v)));
/// if self.0.len() > 2 {
/// f.finish_non_exhaustive()
/// } else {
/// f.finish()
/// }
/// }
/// }
///
/// assert_eq!(
/// format!("{:?}", Foo(vec![
/// ("A".to_string(), 10),
/// ("B".to_string(), 11),
/// ("C".to_string(), 12),
/// ])),
/// r#"{"A": 10, "B": 11, ..}"#,
/// );
/// ```
#[unstable(feature = "debug_more_non_exhaustive", issue = "127942")]
pub fn finish_non_exhaustive(&mut self) -> fmt::Result {
self.result = self.result.and_then(|_| {
assert!(!self.has_key, "attempted to finish a map with a partial entry");
if self.has_fields {
if self.is_pretty() {
let mut slot = None;
let mut state = Default::default();
let mut writer = PadAdapter::wrap(self.fmt, &mut slot, &mut state);
writer.write_str("..\n")?;
self.fmt.write_str("}")
} else {
self.fmt.write_str(", ..}")
}
} else {
self.fmt.write_str("..}")
}
});
self.result
}
/// Finishes output and returns any error encountered.
///
/// # Panics
@ -1000,7 +1200,7 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> {
///
/// assert_eq!(
/// format!("{:?}", Foo(vec![("A".to_string(), 10), ("B".to_string(), 11)])),
/// "{\"A\": 10, \"B\": 11}",
/// r#"{"A": 10, "B": 11}"#,
/// );
/// ```
#[stable(feature = "debug_builders", since = "1.2.0")]

View File

@ -79,23 +79,23 @@ mod debug_struct {
}
assert_eq!(
"Bar { foo: Foo { bar: true, baz: 10/20 }, hello: \"world\" }",
r#"Bar { foo: Foo { bar: true, baz: 10/20 }, hello: "world" }"#,
format!("{Bar:?}")
);
assert_eq!(
"Bar {
r#"Bar {
foo: Foo {
bar: true,
baz: 10/20,
},
hello: \"world\",
}",
hello: "world",
}"#,
format!("{Bar:#?}")
);
}
#[test]
fn test_only_non_exhaustive() {
fn test_empty_non_exhaustive() {
struct Foo;
impl fmt::Debug for Foo {
@ -157,19 +157,19 @@ mod debug_struct {
}
assert_eq!(
"Bar { foo: Foo { bar: true, baz: 10/20, .. }, hello: \"world\", .. }",
r#"Bar { foo: Foo { bar: true, baz: 10/20, .. }, hello: "world", .. }"#,
format!("{Bar:?}")
);
assert_eq!(
"Bar {
r#"Bar {
foo: Foo {
bar: true,
baz: 10/20,
..
},
hello: \"world\",
hello: "world",
..
}",
}"#,
format!("{Bar:#?}")
);
}
@ -249,15 +249,89 @@ mod debug_tuple {
}
}
assert_eq!("Bar(Foo(true, 10/20), \"world\")", format!("{Bar:?}"));
assert_eq!(r#"Bar(Foo(true, 10/20), "world")"#, format!("{Bar:?}"));
assert_eq!(
"Bar(
r#"Bar(
Foo(
true,
10/20,
),
\"world\",
"world",
)"#,
format!("{Bar:#?}")
);
}
#[test]
fn test_empty_non_exhaustive() {
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_tuple("Foo").finish_non_exhaustive()
}
}
assert_eq!("Foo(..)", format!("{Foo:?}"));
assert_eq!("Foo(..)", format!("{Foo:#?}"));
}
#[test]
fn test_multiple_and_non_exhaustive() {
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_tuple("Foo")
.field(&true)
.field(&format_args!("{}/{}", 10, 20))
.finish_non_exhaustive()
}
}
assert_eq!("Foo(true, 10/20, ..)", format!("{Foo:?}"));
assert_eq!(
"Foo(
true,
10/20,
..
)",
format!("{Foo:#?}")
);
}
#[test]
fn test_nested_non_exhaustive() {
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_tuple("Foo")
.field(&true)
.field(&format_args!("{}/{}", 10, 20))
.finish_non_exhaustive()
}
}
struct Bar;
impl fmt::Debug for Bar {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_tuple("Bar").field(&Foo).field(&"world").finish_non_exhaustive()
}
}
assert_eq!(r#"Bar(Foo(true, 10/20, ..), "world", ..)"#, format!("{Bar:?}"));
assert_eq!(
r#"Bar(
Foo(
true,
10/20,
..
),
"world",
..
)"#,
format!("{Bar:#?}")
);
}
@ -301,11 +375,11 @@ mod debug_map {
assert_eq!(format!("{Entry:?}"), format!("{KeyValue:?}"));
assert_eq!(format!("{Entry:#?}"), format!("{KeyValue:#?}"));
assert_eq!("{\"bar\": true}", format!("{Entry:?}"));
assert_eq!(r#"{"bar": true}"#, format!("{Entry:?}"));
assert_eq!(
"{
\"bar\": true,
}",
r#"{
"bar": true,
}"#,
format!("{Entry:#?}")
);
}
@ -339,12 +413,12 @@ mod debug_map {
assert_eq!(format!("{Entry:?}"), format!("{KeyValue:?}"));
assert_eq!(format!("{Entry:#?}"), format!("{KeyValue:#?}"));
assert_eq!("{\"bar\": true, 10: 10/20}", format!("{Entry:?}"));
assert_eq!(r#"{"bar": true, 10: 10/20}"#, format!("{Entry:?}"));
assert_eq!(
"{
\"bar\": true,
r#"{
"bar": true,
10: 10/20,
}",
}"#,
format!("{Entry:#?}")
);
}
@ -371,21 +445,20 @@ mod debug_map {
}
assert_eq!(
"{\"foo\": {\"bar\": true, 10: 10/20}, \
{\"bar\": true, 10: 10/20}: \"world\"}",
r#"{"foo": {"bar": true, 10: 10/20}, {"bar": true, 10: 10/20}: "world"}"#,
format!("{Bar:?}")
);
assert_eq!(
"{
\"foo\": {
\"bar\": true,
r#"{
"foo": {
"bar": true,
10: 10/20,
},
{
\"bar\": true,
"bar": true,
10: 10/20,
}: \"world\",
}",
}: "world",
}"#,
format!("{Bar:#?}")
);
}
@ -471,6 +544,103 @@ mod debug_map {
let _ = format!("{Foo:?}");
}
#[test]
fn test_empty_non_exhaustive() {
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_map().finish_non_exhaustive()
}
}
assert_eq!("{..}", format!("{Foo:?}"));
assert_eq!("{..}", format!("{Foo:#?}"));
}
#[test]
fn test_multiple_and_non_exhaustive() {
struct Entry;
impl fmt::Debug for Entry {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_map()
.entry(&"bar", &true)
.entry(&10, &format_args!("{}/{}", 10, 20))
.finish_non_exhaustive()
}
}
struct KeyValue;
impl fmt::Debug for KeyValue {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_map()
.key(&"bar")
.value(&true)
.key(&10)
.value(&format_args!("{}/{}", 10, 20))
.finish_non_exhaustive()
}
}
assert_eq!(format!("{Entry:?}"), format!("{KeyValue:?}"));
assert_eq!(format!("{Entry:#?}"), format!("{KeyValue:#?}"));
assert_eq!(r#"{"bar": true, 10: 10/20, ..}"#, format!("{Entry:?}"));
assert_eq!(
r#"{
"bar": true,
10: 10/20,
..
}"#,
format!("{Entry:#?}")
);
}
#[test]
fn test_nested_non_exhaustive() {
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_map()
.entry(&"bar", &true)
.entry(&10, &format_args!("{}/{}", 10, 20))
.finish_non_exhaustive()
}
}
struct Bar;
impl fmt::Debug for Bar {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_map().entry(&"foo", &Foo).entry(&Foo, &"world").finish_non_exhaustive()
}
}
assert_eq!(
r#"{"foo": {"bar": true, 10: 10/20, ..}, {"bar": true, 10: 10/20, ..}: "world", ..}"#,
format!("{Bar:?}")
);
assert_eq!(
r#"{
"foo": {
"bar": true,
10: 10/20,
..
},
{
"bar": true,
10: 10/20,
..
}: "world",
..
}"#,
format!("{Bar:#?}")
);
}
}
mod debug_set {
@ -547,15 +717,89 @@ mod debug_set {
}
}
assert_eq!("{{true, 10/20}, \"world\"}", format!("{Bar:?}"));
assert_eq!(r#"{{true, 10/20}, "world"}"#, format!("{Bar:?}"));
assert_eq!(
"{
r#"{
{
true,
10/20,
},
\"world\",
"world",
}"#,
format!("{Bar:#?}")
);
}
#[test]
fn test_empty_non_exhaustive() {
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_set().finish_non_exhaustive()
}
}
assert_eq!("{..}", format!("{Foo:?}"));
assert_eq!("{..}", format!("{Foo:#?}"));
}
#[test]
fn test_multiple_and_non_exhaustive() {
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_set()
.entry(&true)
.entry(&format_args!("{}/{}", 10, 20))
.finish_non_exhaustive()
}
}
assert_eq!("{true, 10/20, ..}", format!("{Foo:?}"));
assert_eq!(
"{
true,
10/20,
..
}",
format!("{Foo:#?}")
);
}
#[test]
fn test_nested_non_exhaustive() {
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_set()
.entry(&true)
.entry(&format_args!("{}/{}", 10, 20))
.finish_non_exhaustive()
}
}
struct Bar;
impl fmt::Debug for Bar {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_set().entry(&Foo).entry(&"world").finish_non_exhaustive()
}
}
assert_eq!(r#"{{true, 10/20, ..}, "world", ..}"#, format!("{Bar:?}"));
assert_eq!(
r#"{
{
true,
10/20,
..
},
"world",
..
}"#,
format!("{Bar:#?}")
);
}
@ -635,15 +879,89 @@ mod debug_list {
}
}
assert_eq!("[[true, 10/20], \"world\"]", format!("{Bar:?}"));
assert_eq!(r#"[[true, 10/20], "world"]"#, format!("{Bar:?}"));
assert_eq!(
"[
r#"[
[
true,
10/20,
],
\"world\",
"world",
]"#,
format!("{Bar:#?}")
);
}
#[test]
fn test_empty_non_exhaustive() {
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_list().finish_non_exhaustive()
}
}
assert_eq!("[..]", format!("{Foo:?}"));
assert_eq!("[..]", format!("{Foo:#?}"));
}
#[test]
fn test_multiple_non_exhaustive() {
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_list()
.entry(&true)
.entry(&format_args!("{}/{}", 10, 20))
.finish_non_exhaustive()
}
}
assert_eq!("[true, 10/20, ..]", format!("{Foo:?}"));
assert_eq!(
"[
true,
10/20,
..
]",
format!("{Foo:#?}")
);
}
#[test]
fn test_nested_non_exhaustive() {
struct Foo;
impl fmt::Debug for Foo {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_list()
.entry(&true)
.entry(&format_args!("{}/{}", 10, 20))
.finish_non_exhaustive()
}
}
struct Bar;
impl fmt::Debug for Bar {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_list().entry(&Foo).entry(&"world").finish_non_exhaustive()
}
}
assert_eq!(r#"[[true, 10/20, ..], "world", ..]"#, format!("{Bar:?}"));
assert_eq!(
r#"[
[
true,
10/20,
..
],
"world",
..
]"#,
format!("{Bar:#?}")
);
}

View File

@ -43,6 +43,7 @@
#![feature(core_io_borrowed_buf)]
#![feature(core_private_bignum)]
#![feature(core_private_diy_float)]
#![feature(debug_more_non_exhaustive)]
#![feature(dec2flt)]
#![feature(duration_constants)]
#![feature(duration_constructors)]

View File

@ -6,7 +6,6 @@
//! directory unless the `--all` flag is present.
use std::fs;
use std::io::{self, ErrorKind};
use std::path::Path;
use crate::core::builder::{crate_description, Builder, RunConfig, ShouldRun, Step};
@ -101,11 +100,11 @@ fn clean(build: &Build, all: bool, stage: Option<u32>) {
return;
}
rm_rf("tmp".as_ref());
remove_dir_recursive("tmp");
// Clean the entire build directory
if all {
rm_rf(&build.out);
remove_dir_recursive(&build.out);
return;
}
@ -136,17 +135,17 @@ fn clean_specific_stage(build: &Build, stage: u32) {
}
let path = t!(entry.path().canonicalize());
rm_rf(&path);
remove_dir_recursive(&path);
}
}
}
fn clean_default(build: &Build) {
rm_rf(&build.out.join("tmp"));
rm_rf(&build.out.join("dist"));
rm_rf(&build.out.join("bootstrap").join(".last-warned-change-id"));
rm_rf(&build.out.join("bootstrap-shims-dump"));
rm_rf(&build.out.join("rustfmt.stamp"));
remove_dir_recursive(build.out.join("tmp"));
remove_dir_recursive(build.out.join("dist"));
remove_dir_recursive(build.out.join("bootstrap").join(".last-warned-change-id"));
remove_dir_recursive(build.out.join("bootstrap-shims-dump"));
remove_dir_recursive(build.out.join("rustfmt.stamp"));
let mut hosts: Vec<_> = build.hosts.iter().map(|t| build.out.join(t)).collect();
// After cross-compilation, artifacts of the host architecture (which may differ from build.host)
@ -166,78 +165,16 @@ fn clean_default(build: &Build) {
continue;
}
let path = t!(entry.path().canonicalize());
rm_rf(&path);
remove_dir_recursive(&path);
}
}
}
fn rm_rf(path: &Path) {
match path.symlink_metadata() {
Err(e) => {
if e.kind() == ErrorKind::NotFound {
return;
}
panic!("failed to get metadata for file {}: {}", path.display(), e);
}
Ok(metadata) => {
if metadata.file_type().is_file() || metadata.file_type().is_symlink() {
do_op(path, "remove file", |p| match fs::remove_file(p) {
#[cfg(windows)]
Err(e)
if e.kind() == std::io::ErrorKind::PermissionDenied
&& p.file_name().and_then(std::ffi::OsStr::to_str)
== Some("bootstrap.exe") =>
{
eprintln!("WARNING: failed to delete '{}'.", p.display());
Ok(())
}
r => r,
});
return;
}
for file in t!(fs::read_dir(path)) {
rm_rf(&t!(file).path());
}
do_op(path, "remove dir", |p| match fs::remove_dir(p) {
// Check for dir not empty on Windows
// FIXME: Once `ErrorKind::DirectoryNotEmpty` is stabilized,
// match on `e.kind()` instead.
#[cfg(windows)]
Err(e) if e.raw_os_error() == Some(145) => Ok(()),
r => r,
});
}
};
}
fn do_op<F>(path: &Path, desc: &str, mut f: F)
where
F: FnMut(&Path) -> io::Result<()>,
{
match f(path) {
Ok(()) => {}
// On windows we can't remove a readonly file, and git will often clone files as readonly.
// As a result, we have some special logic to remove readonly files on windows.
// This is also the reason that we can't use things like fs::remove_dir_all().
#[cfg(windows)]
Err(ref e) if e.kind() == ErrorKind::PermissionDenied => {
let m = t!(path.symlink_metadata());
let mut p = m.permissions();
p.set_readonly(false);
t!(fs::set_permissions(path, p));
f(path).unwrap_or_else(|e| {
// Delete symlinked directories on Windows
if m.file_type().is_symlink() && path.is_dir() && fs::remove_dir(path).is_ok() {
return;
}
panic!("failed to {} {}: {}", desc, path.display(), e);
});
}
Err(e) => {
panic!("failed to {} {}: {}", desc, path.display(), e);
}
/// Wrapper for [`std::fs::remove_dir_all`] that panics on failure and prints the `path` we failed
/// on.
fn remove_dir_recursive<P: AsRef<Path>>(path: P) {
let path = path.as_ref();
if let Err(e) = fs::remove_dir_all(path) {
panic!("failed to `remove_dir_all` at `{}`: {e}", path.display());
}
}

View File

@ -110,7 +110,7 @@ pub fn prebuilt_llvm_config(builder: &Builder<'_>, target: TargetSelection) -> L
// Initialize the llvm submodule if not initialized already.
// If submodules are disabled, this does nothing.
builder.update_submodule("src/llvm-project");
builder.config.update_submodule("src/llvm-project");
let root = "src/llvm-project/llvm";
let out_dir = builder.llvm_out(target);

View File

@ -14,7 +14,7 @@ use std::sync::OnceLock;
use std::{cmp, env, fs};
use build_helper::exit;
use build_helper::git::GitConfig;
use build_helper::git::{output_result, GitConfig};
use serde::{Deserialize, Deserializer};
use serde_derive::Deserialize;
@ -2509,6 +2509,123 @@ impl Config {
}
}
/// Given a path to the directory of a submodule, update it.
///
/// `relative_path` should be relative to the root of the git repository, not an absolute path.
///
/// This *does not* update the submodule if `config.toml` explicitly says
/// not to, or if we're not in a git repository (like a plain source
/// tarball). Typically [`crate::Build::require_submodule`] should be
/// used instead to provide a nice error to the user if the submodule is
/// missing.
pub(crate) fn update_submodule(&self, relative_path: &str) {
if !self.submodules() {
return;
}
let absolute_path = self.src.join(relative_path);
// NOTE: The check for the empty directory is here because when running x.py the first time,
// the submodule won't be checked out. Check it out now so we can build it.
if !GitInfo::new(false, &absolute_path).is_managed_git_subrepository()
&& !helpers::dir_is_empty(&absolute_path)
{
return;
}
// Submodule updating actually happens during in the dry run mode. We need to make sure that
// all the git commands below are actually executed, because some follow-up code
// in bootstrap might depend on the submodules being checked out. Furthermore, not all
// the command executions below work with an empty output (produced during dry run).
// Therefore, all commands below are marked with `run_always()`, so that they also run in
// dry run mode.
let submodule_git = || {
let mut cmd = helpers::git(Some(&absolute_path));
cmd.run_always();
cmd
};
// Determine commit checked out in submodule.
let checked_out_hash = output(submodule_git().args(["rev-parse", "HEAD"]).as_command_mut());
let checked_out_hash = checked_out_hash.trim_end();
// Determine commit that the submodule *should* have.
let recorded = output(
helpers::git(Some(&self.src))
.run_always()
.args(["ls-tree", "HEAD"])
.arg(relative_path)
.as_command_mut(),
);
let actual_hash = recorded
.split_whitespace()
.nth(2)
.unwrap_or_else(|| panic!("unexpected output `{}`", recorded));
if actual_hash == checked_out_hash {
// already checked out
return;
}
println!("Updating submodule {relative_path}");
self.check_run(
helpers::git(Some(&self.src))
.run_always()
.args(["submodule", "-q", "sync"])
.arg(relative_path),
);
// Try passing `--progress` to start, then run git again without if that fails.
let update = |progress: bool| {
// Git is buggy and will try to fetch submodules from the tracking branch for *this* repository,
// even though that has no relation to the upstream for the submodule.
let current_branch = output_result(
helpers::git(Some(&self.src))
.allow_failure()
.run_always()
.args(["symbolic-ref", "--short", "HEAD"])
.as_command_mut(),
)
.map(|b| b.trim().to_owned());
let mut git = helpers::git(Some(&self.src)).allow_failure();
git.run_always();
if let Ok(branch) = current_branch {
// If there is a tag named after the current branch, git will try to disambiguate by prepending `heads/` to the branch name.
// This syntax isn't accepted by `branch.{branch}`. Strip it.
let branch = branch.strip_prefix("heads/").unwrap_or(&branch);
git.arg("-c").arg(format!("branch.{branch}.remote=origin"));
}
git.args(["submodule", "update", "--init", "--recursive", "--depth=1"]);
if progress {
git.arg("--progress");
}
git.arg(relative_path);
git
};
if !self.check_run(&mut update(true)) {
self.check_run(&mut update(false));
}
// Save any local changes, but avoid running `git stash pop` if there are none (since it will exit with an error).
// diff-index reports the modifications through the exit status
let has_local_modifications = !self.check_run(submodule_git().allow_failure().args([
"diff-index",
"--quiet",
"HEAD",
]));
if has_local_modifications {
self.check_run(submodule_git().args(["stash", "push"]));
}
self.check_run(submodule_git().args(["reset", "-q", "--hard"]));
self.check_run(submodule_git().args(["clean", "-qdfx"]));
if has_local_modifications {
self.check_run(submodule_git().args(["stash", "pop"]));
}
}
#[cfg(feature = "bootstrap-self-test")]
pub fn check_stage0_version(&self, _program_path: &Path, _component_name: &'static str) {}
@ -2613,19 +2730,23 @@ impl Config {
asserts: bool,
) -> bool {
let if_unchanged = || {
// Git is needed to track modifications here, but tarball source is not available.
// If not modified here or built through tarball source, we maintain consistency
// with '"if available"'.
if !self.rust_info.is_from_tarball()
&& self
.last_modified_commit(&["src/llvm-project"], "download-ci-llvm", true)
.is_none()
{
// there are some untracked changes in the given paths.
false
} else {
llvm::is_ci_llvm_available(self, asserts)
if self.rust_info.is_from_tarball() {
// Git is needed for running "if-unchanged" logic.
println!(
"WARNING: 'if-unchanged' has no effect on tarball sources; ignoring `download-ci-llvm`."
);
return false;
}
self.update_submodule("src/llvm-project");
// Check for untracked changes in `src/llvm-project`.
let has_changes = self
.last_modified_commit(&["src/llvm-project"], "download-ci-llvm", true)
.is_none();
// Return false if there are untracked changes, otherwise check if CI LLVM is available.
if has_changes { false } else { llvm::is_ci_llvm_available(self, asserts) }
};
match download_ci_llvm {

View File

@ -56,7 +56,7 @@ impl Config {
/// Returns false if do not execute at all, otherwise returns its
/// `status.success()`.
pub(crate) fn check_run(&self, cmd: &mut BootstrapCommand) -> bool {
if self.dry_run() {
if self.dry_run() && !cmd.run_always {
return true;
}
self.verbose(|| println!("running: {cmd:?}"));

View File

@ -473,117 +473,6 @@ impl Build {
build
}
/// Given a path to the directory of a submodule, update it.
///
/// `relative_path` should be relative to the root of the git repository, not an absolute path.
///
/// This *does not* update the submodule if `config.toml` explicitly says
/// not to, or if we're not in a git repository (like a plain source
/// tarball). Typically [`Build::require_submodule`] should be
/// used instead to provide a nice error to the user if the submodule is
/// missing.
fn update_submodule(&self, relative_path: &str) {
if !self.config.submodules() {
return;
}
let absolute_path = self.config.src.join(relative_path);
// NOTE: The check for the empty directory is here because when running x.py the first time,
// the submodule won't be checked out. Check it out now so we can build it.
if !GitInfo::new(false, &absolute_path).is_managed_git_subrepository()
&& !dir_is_empty(&absolute_path)
{
return;
}
// Submodule updating actually happens during in the dry run mode. We need to make sure that
// all the git commands below are actually executed, because some follow-up code
// in bootstrap might depend on the submodules being checked out. Furthermore, not all
// the command executions below work with an empty output (produced during dry run).
// Therefore, all commands below are marked with `run_always()`, so that they also run in
// dry run mode.
let submodule_git = || {
let mut cmd = helpers::git(Some(&absolute_path));
cmd.run_always();
cmd
};
// Determine commit checked out in submodule.
let checked_out_hash =
submodule_git().args(["rev-parse", "HEAD"]).run_capture_stdout(self).stdout();
let checked_out_hash = checked_out_hash.trim_end();
// Determine commit that the submodule *should* have.
let recorded = helpers::git(Some(&self.src))
.run_always()
.args(["ls-tree", "HEAD"])
.arg(relative_path)
.run_capture_stdout(self)
.stdout();
let actual_hash = recorded
.split_whitespace()
.nth(2)
.unwrap_or_else(|| panic!("unexpected output `{}`", recorded));
if actual_hash == checked_out_hash {
// already checked out
return;
}
println!("Updating submodule {relative_path}");
helpers::git(Some(&self.src))
.run_always()
.args(["submodule", "-q", "sync"])
.arg(relative_path)
.run(self);
// Try passing `--progress` to start, then run git again without if that fails.
let update = |progress: bool| {
// Git is buggy and will try to fetch submodules from the tracking branch for *this* repository,
// even though that has no relation to the upstream for the submodule.
let current_branch = helpers::git(Some(&self.src))
.allow_failure()
.run_always()
.args(["symbolic-ref", "--short", "HEAD"])
.run_capture_stdout(self)
.stdout_if_ok()
.map(|s| s.trim().to_owned());
let mut git = helpers::git(Some(&self.src)).allow_failure();
git.run_always();
if let Some(branch) = current_branch {
// If there is a tag named after the current branch, git will try to disambiguate by prepending `heads/` to the branch name.
// This syntax isn't accepted by `branch.{branch}`. Strip it.
let branch = branch.strip_prefix("heads/").unwrap_or(&branch);
git.arg("-c").arg(format!("branch.{branch}.remote=origin"));
}
git.args(["submodule", "update", "--init", "--recursive", "--depth=1"]);
if progress {
git.arg("--progress");
}
git.arg(relative_path);
git
};
if !update(true).run(self) {
update(false).run(self);
}
// Save any local changes, but avoid running `git stash pop` if there are none (since it will exit with an error).
// diff-index reports the modifications through the exit status
let has_local_modifications =
!submodule_git().allow_failure().args(["diff-index", "--quiet", "HEAD"]).run(self);
if has_local_modifications {
submodule_git().args(["stash", "push"]).run(self);
}
submodule_git().args(["reset", "-q", "--hard"]).run(self);
submodule_git().args(["clean", "-qdfx"]).run(self);
if has_local_modifications {
submodule_git().args(["stash", "pop"]).run(self);
}
}
/// Updates a submodule, and exits with a failure if submodule management
/// is disabled and the submodule does not exist.
///
@ -598,7 +487,7 @@ impl Build {
if cfg!(test) && !self.config.submodules() {
return;
}
self.update_submodule(submodule);
self.config.update_submodule(submodule);
let absolute_path = self.config.src.join(submodule);
if dir_is_empty(&absolute_path) {
let maybe_enable = if !self.config.submodules()
@ -646,7 +535,7 @@ impl Build {
let path = Path::new(submodule);
// Don't update the submodule unless it's already been cloned.
if GitInfo::new(false, path).is_managed_git_subrepository() {
self.update_submodule(submodule);
self.config.update_submodule(submodule);
}
}
}
@ -659,7 +548,7 @@ impl Build {
}
if GitInfo::new(false, Path::new(submodule)).is_managed_git_subrepository() {
self.update_submodule(submodule);
self.config.update_submodule(submodule);
}
}

View File

@ -1547,10 +1547,23 @@ instead, we check that it's not a "finger" cursor.
margin-left: 24px;
}
@keyframes targetfadein {
from {
background-color: var(--main-background-color);
}
10% {
background-color: var(--target-border-color);
}
to {
background-color: var(--target-background-color);
}
}
:target {
padding-right: 3px;
background-color: var(--target-background-color);
border-right: 3px solid var(--target-border-color);
animation: 0.65s cubic-bezier(0, 0, 0.1, 1.0) 0.1s targetfadein;
}
.code-header a.tooltip {

View File

@ -3265,7 +3265,7 @@ impl<'test> TestCx<'test> {
let tmpdir = cwd.join(self.output_base_name());
if tmpdir.exists() {
self.aggressive_rm_rf(&tmpdir).unwrap();
fs::remove_dir_all(&tmpdir).unwrap();
}
create_dir_all(&tmpdir).unwrap();
@ -3404,29 +3404,6 @@ impl<'test> TestCx<'test> {
}
}
fn aggressive_rm_rf(&self, path: &Path) -> io::Result<()> {
for e in path.read_dir()? {
let entry = e?;
let path = entry.path();
if entry.file_type()?.is_dir() {
self.aggressive_rm_rf(&path)?;
} else {
// Remove readonly files as well on windows (by default we can't)
fs::remove_file(&path).or_else(|e| {
if cfg!(windows) && e.kind() == io::ErrorKind::PermissionDenied {
let mut meta = entry.metadata()?.permissions();
meta.set_readonly(false);
fs::set_permissions(&path, meta)?;
fs::remove_file(&path)
} else {
Err(e)
}
})?;
}
}
fs::remove_dir(path)
}
fn run_rmake_v2_test(&self) {
// For `run-make` V2, we need to perform 2 steps to build and run a `run-make` V2 recipe
// (`rmake.rs`) to run the actual tests. The support library is already built as a tool rust
@ -3475,7 +3452,7 @@ impl<'test> TestCx<'test> {
// This setup intentionally diverges from legacy Makefile run-make tests.
let base_dir = self.output_base_name();
if base_dir.exists() {
self.aggressive_rm_rf(&base_dir).unwrap();
fs::remove_dir_all(&base_dir).unwrap();
}
let rmake_out_dir = base_dir.join("rmake_out");
create_dir_all(&rmake_out_dir).unwrap();

View File

@ -0,0 +1,34 @@
//@ revisions: rpass1 rpass2
// issue#112680
#![feature(decl_macro)]
pub trait T {
type Key;
fn index_from_key(key: Self::Key) -> usize;
}
pub macro m($key_ty:ident, $val_ty:ident) {
struct $key_ty {
inner: usize,
}
impl T for $val_ty {
type Key = $key_ty;
fn index_from_key(key: Self::Key) -> usize {
key.inner
}
}
}
m!(TestId, Test);
#[cfg(rpass1)]
struct Test(u32);
#[cfg(rpass2)]
struct Test;
fn main() {}

View File

@ -11,7 +11,7 @@ define-function: (
[theme, background, border],
block {
call-function: ("switch-theme", {"theme": |theme|})
assert-css: ("#method\.a_method:target", {
wait-for-css: ("#method\.a_method:target", {
"background-color": |background|,
"border-right": "3px solid " + |border|,
})

View File

@ -117,4 +117,11 @@ pub fn skipped_because_of_annotation<'a>(dcx: DiagCtxtHandle<'a>) {
fn f(_x: impl Into<DiagMessage>, _y: impl Into<SubdiagMessage>) {}
fn g() {
f(crate::fluent_generated::no_crate_example, crate::fluent_generated::no_crate_example);
f("untranslatable diagnostic", crate::fluent_generated::no_crate_example);
//~^ ERROR diagnostics should be created using translatable messages
f(crate::fluent_generated::no_crate_example, "untranslatable diagnostic");
//~^ ERROR diagnostics should be created using translatable messages
f("untranslatable diagnostic", "untranslatable diagnostic");
//~^ ERROR diagnostics should be created using translatable messages
//~^^ ERROR diagnostics should be created using translatable messages
}

View File

@ -1,8 +1,8 @@
error: diagnostics should be created using translatable messages
--> $DIR/diagnostics.rs:43:9
--> $DIR/diagnostics.rs:43:31
|
LL | Diag::new(dcx, level, "untranslatable diagnostic")
| ^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/diagnostics.rs:7:9
@ -11,16 +11,16 @@ LL | #![deny(rustc::untranslatable_diagnostic)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: diagnostics should be created using translatable messages
--> $DIR/diagnostics.rs:64:14
--> $DIR/diagnostics.rs:64:19
|
LL | diag.note("untranslatable diagnostic");
| ^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: diagnostics should be created using translatable messages
--> $DIR/diagnostics.rs:85:14
--> $DIR/diagnostics.rs:85:19
|
LL | diag.note("untranslatable diagnostic");
| ^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: diagnostics should only be created in `Diagnostic`/`Subdiagnostic`/`LintDiagnostic` impls
--> $DIR/diagnostics.rs:99:21
@ -41,10 +41,34 @@ LL | let _diag = dcx.struct_err("untranslatable diagnostic");
| ^^^^^^^^^^
error: diagnostics should be created using translatable messages
--> $DIR/diagnostics.rs:102:21
--> $DIR/diagnostics.rs:102:32
|
LL | let _diag = dcx.struct_err("untranslatable diagnostic");
| ^^^^^^^^^^
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 6 previous errors
error: diagnostics should be created using translatable messages
--> $DIR/diagnostics.rs:120:7
|
LL | f("untranslatable diagnostic", crate::fluent_generated::no_crate_example);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: diagnostics should be created using translatable messages
--> $DIR/diagnostics.rs:122:50
|
LL | f(crate::fluent_generated::no_crate_example, "untranslatable diagnostic");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: diagnostics should be created using translatable messages
--> $DIR/diagnostics.rs:124:7
|
LL | f("untranslatable diagnostic", "untranslatable diagnostic");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: diagnostics should be created using translatable messages
--> $DIR/diagnostics.rs:124:36
|
LL | f("untranslatable diagnostic", "untranslatable diagnostic");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 10 previous errors

View File

@ -0,0 +1,13 @@
struct Hello<'a> {
value: Box<dyn std::any::Any + 'a>,
//~^ ERROR lifetime bound not satisfied
}
impl<'a> Hello<'a> {
fn new<T: 'a>(value: T) -> Self {
Self { value: Box::new(value) }
//~^ ERROR the parameter type `T` may not live long enough
}
}
fn main() {}

View File

@ -0,0 +1,32 @@
error[E0478]: lifetime bound not satisfied
--> $DIR/explicit-static-bound-on-trait.rs:2:12
|
LL | value: Box<dyn std::any::Any + 'a>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: lifetime parameter instantiated with the lifetime `'a` as defined here
--> $DIR/explicit-static-bound-on-trait.rs:1:14
|
LL | struct Hello<'a> {
| ^^
note: but lifetime parameter must outlive the static lifetime
--> $SRC_DIR/core/src/any.rs:LL:COL
error[E0310]: the parameter type `T` may not live long enough
--> $DIR/explicit-static-bound-on-trait.rs:8:23
|
LL | Self { value: Box::new(value) }
| ^^^^^^^^^^^^^^^
| |
| the parameter type `T` must be valid for the static lifetime...
| ...so that the type `T` will meet its required lifetime bounds
|
help: consider adding an explicit lifetime bound
|
LL | fn new<T: 'a + 'static>(value: T) -> Self {
| +++++++++
error: aborting due to 2 previous errors
Some errors have detailed explanations: E0310, E0478.
For more information about an error, try `rustc --explain E0310`.