Merge pull request #2451 from jieyouxu/rustc-pull

This commit is contained in:
许杰友 Jieyou Xu (Joe) 2025-06-03 20:19:44 +08:00 committed by GitHub
commit 033b829e0a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
217 changed files with 3227 additions and 1605 deletions

View file

@ -653,7 +653,7 @@ Torsten Weber <TorstenWeber12@gmail.com> <torstenweber12@gmail.com>
Trevor Gross <tmgross@umich.edu> <t.gross35@gmail.com>
Trevor Gross <tmgross@umich.edu> <tgross@intrepidcs.com>
Trevor Spiteri <tspiteri@ieee.org> <trevor.spiteri@um.edu.mt>
Tshepang Mbambo <tshepang@gmail.com>
Tshepang Mbambo <hopsi@tuta.io> <tshepang@gmail.com>
Ty Overby <ty@pre-alpha.com>
Tyler Mandry <tmandry@gmail.com> <tmandry@google.com>
Tyler Ruckinger <t.ruckinger@gmail.com>

View file

@ -177,56 +177,26 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "askama"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d4744ed2eef2645831b441d8f5459689ade2ab27c854488fbab1fbe94fce1a7"
dependencies = [
"askama_derive 0.13.1",
"itoa",
"percent-encoding",
"serde",
"serde_json",
]
[[package]]
name = "askama"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f75363874b771be265f4ffe307ca705ef6f3baa19011c149da8674a87f1b75c4"
dependencies = [
"askama_derive 0.14.0",
"askama_derive",
"itoa",
"percent-encoding",
"serde",
"serde_json",
]
[[package]]
name = "askama_derive"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d661e0f57be36a5c14c48f78d09011e67e0cb618f269cca9f2fd8d15b68c46ac"
dependencies = [
"askama_parser 0.13.0",
"basic-toml",
"memchr",
"proc-macro2",
"quote",
"rustc-hash 2.1.1",
"serde",
"serde_derive",
"syn 2.0.101",
]
[[package]]
name = "askama_derive"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "129397200fe83088e8a68407a8e2b1f826cf0086b21ccdb866a722c8bcd3a94f"
dependencies = [
"askama_parser 0.14.0",
"askama_parser",
"basic-toml",
"memchr",
"proc-macro2",
@ -237,18 +207,6 @@ dependencies = [
"syn 2.0.101",
]
[[package]]
name = "askama_parser"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf315ce6524c857bb129ff794935cf6d42c82a6cff60526fe2a63593de4d0d4f"
dependencies = [
"memchr",
"serde",
"serde_derive",
"winnow 0.7.10",
]
[[package]]
name = "askama_parser"
version = "0.14.0"
@ -582,7 +540,7 @@ name = "clippy"
version = "0.1.89"
dependencies = [
"anstream",
"askama 0.13.1",
"askama",
"cargo_metadata 0.18.1",
"clippy_config",
"clippy_lints",
@ -1432,7 +1390,7 @@ name = "generate-copyright"
version = "0.1.0"
dependencies = [
"anyhow",
"askama 0.14.0",
"askama",
"cargo_metadata 0.18.1",
"serde",
"serde_json",
@ -4676,7 +4634,7 @@ name = "rustdoc"
version = "0.0.0"
dependencies = [
"arrayvec",
"askama 0.14.0",
"askama",
"base64",
"expect-test",
"indexmap",

View file

@ -3,10 +3,11 @@ use rustc_ast::ptr::P;
use rustc_ast::visit::AssocCtxt;
use rustc_ast::*;
use rustc_errors::ErrorGuaranteed;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def::{DefKind, PerNS, Res};
use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
use rustc_hir::{self as hir, HirId, LifetimeSource, PredicateOrigin};
use rustc_index::{IndexSlice, IndexVec};
use rustc_middle::span_bug;
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
use rustc_span::edit_distance::find_best_match_for_name;
use rustc_span::{DUMMY_SP, DesugaringKind, Ident, Span, Symbol, kw, sym};
@ -527,7 +528,22 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
UseTreeKind::Glob => {
let res = self.expect_full_res(id);
let res = smallvec![self.lower_res(res)];
let res = self.lower_res(res);
// Put the result in the appropriate namespace.
let res = match res {
Res::Def(DefKind::Mod | DefKind::Trait, _) => {
PerNS { type_ns: Some(res), value_ns: None, macro_ns: None }
}
Res::Def(DefKind::Enum, _) => {
PerNS { type_ns: None, value_ns: Some(res), macro_ns: None }
}
Res::Err => {
// Propagate the error to all namespaces, just to be sure.
let err = Some(Res::Err);
PerNS { type_ns: err, value_ns: err, macro_ns: err }
}
_ => span_bug!(path.span, "bad glob res {:?}", res),
};
let path = Path { segments, span: path.span, tokens: None };
let path = self.lower_use_path(res, &path, ParamMode::Explicit);
hir::ItemKind::Use(path, hir::UseKind::Glob)
@ -601,7 +617,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
} else {
// For non-empty lists we can just drop all the data, the prefix is already
// present in HIR as a part of nested imports.
self.arena.alloc(hir::UsePath { res: smallvec![], segments: &[], span })
self.arena.alloc(hir::UsePath { res: PerNS::default(), segments: &[], span })
};
hir::ItemKind::Use(path, hir::UseKind::ListStem)
}

View file

@ -64,7 +64,7 @@ use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
use rustc_session::parse::{add_feature_diagnostics, feature_err};
use rustc_span::symbol::{Ident, Symbol, kw, sym};
use rustc_span::{DUMMY_SP, DesugaringKind, Span};
use smallvec::{SmallVec, smallvec};
use smallvec::SmallVec;
use thin_vec::ThinVec;
use tracing::{debug, instrument, trace};
@ -705,14 +705,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.resolver.get_partial_res(id).map_or(Res::Err, |pr| pr.expect_full_res())
}
fn lower_import_res(&mut self, id: NodeId, span: Span) -> SmallVec<[Res; 3]> {
let res = self.resolver.get_import_res(id).present_items();
let res: SmallVec<_> = res.map(|res| self.lower_res(res)).collect();
if res.is_empty() {
fn lower_import_res(&mut self, id: NodeId, span: Span) -> PerNS<Option<Res>> {
let per_ns = self.resolver.get_import_res(id);
let per_ns = per_ns.map(|res| res.map(|res| self.lower_res(res)));
if per_ns.is_empty() {
// Propagate the error to all namespaces, just to be sure.
self.dcx().span_delayed_bug(span, "no resolution for an import");
return smallvec![Res::Err];
let err = Some(Res::Err);
return PerNS { type_ns: err, value_ns: err, macro_ns: err };
}
res
per_ns
}
fn make_lang_item_qpath(

View file

@ -1,13 +1,13 @@
use std::sync::Arc;
use rustc_ast::{self as ast, *};
use rustc_hir::def::{DefKind, PartialRes, Res};
use rustc_hir::def::{DefKind, PartialRes, PerNS, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::{self as hir, GenericArg};
use rustc_middle::{span_bug, ty};
use rustc_session::parse::add_feature_diagnostics;
use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Ident, Span, Symbol, sym};
use smallvec::{SmallVec, smallvec};
use smallvec::smallvec;
use tracing::{debug, instrument};
use super::errors::{
@ -226,11 +226,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
pub(crate) fn lower_use_path(
&mut self,
res: SmallVec<[Res; 3]>,
res: PerNS<Option<Res>>,
p: &Path,
param_mode: ParamMode,
) -> &'hir hir::UsePath<'hir> {
assert!((1..=3).contains(&res.len()));
assert!(!res.is_empty());
self.arena.alloc(hir::UsePath {
res,
segments: self.arena.alloc_from_iter(p.segments.iter().map(|segment| {

View file

@ -840,14 +840,22 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
} else {
bug!("not an upvar")
};
err.span_label(
*span,
format!(
"calling `{}` requires mutable binding due to {}",
self.describe_place(the_place_err).unwrap(),
reason
),
);
// sometimes we deliberately don't store the name of a place when coming from a macro in
// another crate. We generally want to limit those diagnostics a little, to hide
// implementation details (such as those from pin!() or format!()). In that case show a
// slightly different error message, or none at all if something else happened. In other
// cases the message is likely not useful.
if let Some(place_name) = self.describe_place(the_place_err) {
err.span_label(
*span,
format!("calling `{place_name}` requires mutable binding due to {reason}"),
);
} else if span.from_expansion() {
err.span_label(
*span,
format!("a call in this macro requires a mutable binding due to {reason}",),
);
}
}
}

View file

@ -23,7 +23,7 @@ use rustc_codegen_ssa::traits::{
use rustc_middle::bug;
#[cfg(feature = "master")]
use rustc_middle::ty::layout::FnAbiOf;
use rustc_middle::ty::layout::{HasTypingEnv, LayoutOf};
use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, Instance, Ty};
use rustc_span::{Span, Symbol, sym};
use rustc_target::callconv::{ArgAbi, PassMode};
@ -205,21 +205,10 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
span: Span,
) -> Result<(), Instance<'tcx>> {
let tcx = self.tcx;
let callee_ty = instance.ty(tcx, self.typing_env());
let (def_id, fn_args) = match *callee_ty.kind() {
ty::FnDef(def_id, fn_args) => (def_id, fn_args),
_ => bug!("expected fn item type, found {}", callee_ty),
};
let sig = callee_ty.fn_sig(tcx);
let sig = tcx.normalize_erasing_late_bound_regions(self.typing_env(), sig);
let arg_tys = sig.inputs();
let ret_ty = sig.output();
let name = tcx.item_name(def_id);
let name = tcx.item_name(instance.def_id());
let name_str = name.as_str();
let llret_ty = self.layout_of(ret_ty).gcc_type(self);
let fn_args = instance.args;
let simple = get_simple_intrinsic(self, name);
let simple_func = get_simple_function(self, name);
@ -320,8 +309,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
| sym::rotate_right
| sym::saturating_add
| sym::saturating_sub => {
let ty = arg_tys[0];
match int_type_width_signed(ty, self) {
match int_type_width_signed(args[0].layout.ty, self) {
Some((width, signed)) => match name {
sym::ctlz | sym::cttz => {
let func = self.current_func.borrow().expect("func");
@ -400,7 +388,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType {
span,
name,
ty,
ty: args[0].layout.ty,
});
return Ok(());
}
@ -492,7 +480,14 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
}
_ if name_str.starts_with("simd_") => {
match generic_simd_intrinsic(self, name, callee_ty, args, ret_ty, llret_ty, span) {
match generic_simd_intrinsic(
self,
name,
args,
result.layout.ty,
result.layout.gcc_type(self),
span,
) {
Ok(value) => value,
Err(()) => return Ok(()),
}
@ -503,13 +498,10 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc
};
if result.layout.ty.is_bool() {
OperandRef::from_immediate_or_packed_pair(self, value, result.layout)
.val
.store(self, result);
let val = self.from_immediate(value);
self.store_to_place(val, result.val);
} else if !result.layout.ty.is_unit() {
let ptr_llty = self.type_ptr_to(result.layout.gcc_type(self));
let ptr = self.pointercast(result.val.llval, ptr_llty);
self.store(value, ptr, result.val.align);
self.store_to_place(value, result.val);
}
Ok(())
}

View file

@ -28,7 +28,6 @@ use crate::context::CodegenCx;
pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
bx: &mut Builder<'a, 'gcc, 'tcx>,
name: Symbol,
callee_ty: Ty<'tcx>,
args: &[OperandRef<'tcx, RValue<'gcc>>],
ret_ty: Ty<'tcx>,
llret_ty: Type<'gcc>,
@ -54,24 +53,17 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
};
}
let tcx = bx.tcx();
let sig = tcx.normalize_erasing_late_bound_regions(
ty::TypingEnv::fully_monomorphized(),
callee_ty.fn_sig(tcx),
);
let arg_tys = sig.inputs();
if name == sym::simd_select_bitmask {
require_simd!(
arg_tys[1],
InvalidMonomorphization::SimdArgument { span, name, ty: arg_tys[1] }
args[1].layout.ty,
InvalidMonomorphization::SimdArgument { span, name, ty: args[1].layout.ty }
);
let (len, _) = arg_tys[1].simd_size_and_type(bx.tcx());
let (len, _) = args[1].layout.ty.simd_size_and_type(bx.tcx());
let expected_int_bits = (len.max(8) - 1).next_power_of_two();
let expected_bytes = len / 8 + ((len % 8 > 0) as u64);
let mask_ty = arg_tys[0];
let mask_ty = args[0].layout.ty;
let mut mask = match *mask_ty.kind() {
ty::Int(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(),
ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(),
@ -121,8 +113,11 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
}
// every intrinsic below takes a SIMD vector as its first argument
require_simd!(arg_tys[0], InvalidMonomorphization::SimdInput { span, name, ty: arg_tys[0] });
let in_ty = arg_tys[0];
require_simd!(
args[0].layout.ty,
InvalidMonomorphization::SimdInput { span, name, ty: args[0].layout.ty }
);
let in_ty = args[0].layout.ty;
let comparison = match name {
sym::simd_eq => Some(BinOp::Eq),
@ -134,7 +129,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
_ => None,
};
let (in_len, in_elem) = arg_tys[0].simd_size_and_type(bx.tcx());
let (in_len, in_elem) = args[0].layout.ty.simd_size_and_type(bx.tcx());
if let Some(cmp_op) = comparison {
require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty });
@ -401,13 +396,13 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
#[cfg(feature = "master")]
if name == sym::simd_insert || name == sym::simd_insert_dyn {
require!(
in_elem == arg_tys[2],
in_elem == args[2].layout.ty,
InvalidMonomorphization::InsertedType {
span,
name,
in_elem,
in_ty,
out_ty: arg_tys[2]
out_ty: args[2].layout.ty
}
);
@ -439,10 +434,10 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
let m_elem_ty = in_elem;
let m_len = in_len;
require_simd!(
arg_tys[1],
InvalidMonomorphization::SimdArgument { span, name, ty: arg_tys[1] }
args[1].layout.ty,
InvalidMonomorphization::SimdArgument { span, name, ty: args[1].layout.ty }
);
let (v_len, _) = arg_tys[1].simd_size_and_type(bx.tcx());
let (v_len, _) = args[1].layout.ty.simd_size_and_type(bx.tcx());
require!(
m_len == v_len,
InvalidMonomorphization::MismatchedLengths { span, name, m_len, v_len }
@ -911,18 +906,18 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
// All types must be simd vector types
require_simd!(in_ty, InvalidMonomorphization::SimdFirst { span, name, ty: in_ty });
require_simd!(
arg_tys[1],
InvalidMonomorphization::SimdSecond { span, name, ty: arg_tys[1] }
args[1].layout.ty,
InvalidMonomorphization::SimdSecond { span, name, ty: args[1].layout.ty }
);
require_simd!(
arg_tys[2],
InvalidMonomorphization::SimdThird { span, name, ty: arg_tys[2] }
args[2].layout.ty,
InvalidMonomorphization::SimdThird { span, name, ty: args[2].layout.ty }
);
require_simd!(ret_ty, InvalidMonomorphization::SimdReturn { span, name, ty: ret_ty });
// Of the same length:
let (out_len, _) = arg_tys[1].simd_size_and_type(bx.tcx());
let (out_len2, _) = arg_tys[2].simd_size_and_type(bx.tcx());
let (out_len, _) = args[1].layout.ty.simd_size_and_type(bx.tcx());
let (out_len2, _) = args[2].layout.ty.simd_size_and_type(bx.tcx());
require!(
in_len == out_len,
InvalidMonomorphization::SecondArgumentLength {
@ -930,7 +925,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
name,
in_len,
in_ty,
arg_ty: arg_tys[1],
arg_ty: args[1].layout.ty,
out_len
}
);
@ -941,7 +936,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
name,
in_len,
in_ty,
arg_ty: arg_tys[2],
arg_ty: args[2].layout.ty,
out_len: out_len2
}
);
@ -970,8 +965,8 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
// The second argument must be a simd vector with an element type that's a pointer
// to the element type of the first argument
let (_, element_ty0) = arg_tys[0].simd_size_and_type(bx.tcx());
let (_, element_ty1) = arg_tys[1].simd_size_and_type(bx.tcx());
let (_, element_ty0) = args[0].layout.ty.simd_size_and_type(bx.tcx());
let (_, element_ty1) = args[1].layout.ty.simd_size_and_type(bx.tcx());
let (pointer_count, underlying_ty) = match *element_ty1.kind() {
ty::RawPtr(p_ty, _) if p_ty == in_elem => {
(ptr_count(element_ty1), non_ptr(element_ty1))
@ -983,7 +978,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
span,
name,
expected_element: element_ty1,
second_arg: arg_tys[1],
second_arg: args[1].layout.ty,
in_elem,
in_ty,
mutability: ExpectedPointerMutability::Not,
@ -998,7 +993,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
// The element type of the third argument must be an integer type of any width:
// TODO: also support unsigned integers.
let (_, element_ty2) = arg_tys[2].simd_size_and_type(bx.tcx());
let (_, element_ty2) = args[2].layout.ty.simd_size_and_type(bx.tcx());
match *element_ty2.kind() {
ty::Int(_) => (),
_ => {
@ -1030,17 +1025,17 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
// All types must be simd vector types
require_simd!(in_ty, InvalidMonomorphization::SimdFirst { span, name, ty: in_ty });
require_simd!(
arg_tys[1],
InvalidMonomorphization::SimdSecond { span, name, ty: arg_tys[1] }
args[1].layout.ty,
InvalidMonomorphization::SimdSecond { span, name, ty: args[1].layout.ty }
);
require_simd!(
arg_tys[2],
InvalidMonomorphization::SimdThird { span, name, ty: arg_tys[2] }
args[2].layout.ty,
InvalidMonomorphization::SimdThird { span, name, ty: args[2].layout.ty }
);
// Of the same length:
let (element_len1, _) = arg_tys[1].simd_size_and_type(bx.tcx());
let (element_len2, _) = arg_tys[2].simd_size_and_type(bx.tcx());
let (element_len1, _) = args[1].layout.ty.simd_size_and_type(bx.tcx());
let (element_len2, _) = args[2].layout.ty.simd_size_and_type(bx.tcx());
require!(
in_len == element_len1,
InvalidMonomorphization::SecondArgumentLength {
@ -1048,7 +1043,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
name,
in_len,
in_ty,
arg_ty: arg_tys[1],
arg_ty: args[1].layout.ty,
out_len: element_len1
}
);
@ -1059,7 +1054,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
name,
in_len,
in_ty,
arg_ty: arg_tys[2],
arg_ty: args[2].layout.ty,
out_len: element_len2
}
);
@ -1082,9 +1077,9 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
// The second argument must be a simd vector with an element type that's a pointer
// to the element type of the first argument
let (_, element_ty0) = arg_tys[0].simd_size_and_type(bx.tcx());
let (_, element_ty1) = arg_tys[1].simd_size_and_type(bx.tcx());
let (_, element_ty2) = arg_tys[2].simd_size_and_type(bx.tcx());
let (_, element_ty0) = args[0].layout.ty.simd_size_and_type(bx.tcx());
let (_, element_ty1) = args[1].layout.ty.simd_size_and_type(bx.tcx());
let (_, element_ty2) = args[2].layout.ty.simd_size_and_type(bx.tcx());
let (pointer_count, underlying_ty) = match *element_ty1.kind() {
ty::RawPtr(p_ty, mutbl) if p_ty == in_elem && mutbl == hir::Mutability::Mut => {
(ptr_count(element_ty1), non_ptr(element_ty1))
@ -1096,7 +1091,7 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
span,
name,
expected_element: element_ty1,
second_arg: arg_tys[1],
second_arg: args[1].layout.ty,
in_elem,
in_ty,
mutability: ExpectedPointerMutability::Mut,
@ -1194,8 +1189,8 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>(
return_error!(InvalidMonomorphization::ExpectedVectorElementType {
span,
name,
expected_element: arg_tys[0].simd_size_and_type(bx.tcx()).1,
vector_type: arg_tys[0],
expected_element: args[0].layout.ty.simd_size_and_type(bx.tcx()).1,
vector_type: args[0].layout.ty,
});
}
};

View file

@ -169,19 +169,9 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
span: Span,
) -> Result<(), ty::Instance<'tcx>> {
let tcx = self.tcx;
let callee_ty = instance.ty(tcx, self.typing_env());
let ty::FnDef(def_id, fn_args) = *callee_ty.kind() else {
bug!("expected fn item type, found {}", callee_ty);
};
let sig = callee_ty.fn_sig(tcx);
let sig = tcx.normalize_erasing_late_bound_regions(self.typing_env(), sig);
let arg_tys = sig.inputs();
let ret_ty = sig.output();
let name = tcx.item_name(def_id);
let llret_ty = self.layout_of(ret_ty).llvm_type(self);
let name = tcx.item_name(instance.def_id());
let fn_args = instance.args;
let simple = get_simple_intrinsic(self, name);
let llval = match name {
@ -265,22 +255,22 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
BackendRepr::Scalar(scalar) => {
match scalar.primitive() {
Primitive::Int(..) => {
if self.cx().size_of(ret_ty).bytes() < 4 {
if self.cx().size_of(result.layout.ty).bytes() < 4 {
// `va_arg` should not be called on an integer type
// less than 4 bytes in length. If it is, promote
// the integer to an `i32` and truncate the result
// back to the smaller type.
let promoted_result = emit_va_arg(self, args[0], tcx.types.i32);
self.trunc(promoted_result, llret_ty)
self.trunc(promoted_result, result.layout.llvm_type(self))
} else {
emit_va_arg(self, args[0], ret_ty)
emit_va_arg(self, args[0], result.layout.ty)
}
}
Primitive::Float(Float::F16) => {
bug!("the va_arg intrinsic does not work with `f16`")
}
Primitive::Float(Float::F64) | Primitive::Pointer(_) => {
emit_va_arg(self, args[0], ret_ty)
emit_va_arg(self, args[0], result.layout.ty)
}
// `va_arg` should never be used with the return type f32.
Primitive::Float(Float::F32) => {
@ -384,7 +374,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
| sym::rotate_right
| sym::saturating_add
| sym::saturating_sub => {
let ty = arg_tys[0];
let ty = args[0].layout.ty;
if !ty.is_integral() {
tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType {
span,
@ -403,26 +393,26 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
&[args[0].immediate(), y],
);
self.intcast(ret, llret_ty, false)
self.intcast(ret, result.layout.llvm_type(self), false)
}
sym::ctlz_nonzero => {
let y = self.const_bool(true);
let llvm_name = &format!("llvm.ctlz.i{width}");
let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]);
self.intcast(ret, llret_ty, false)
self.intcast(ret, result.layout.llvm_type(self), false)
}
sym::cttz_nonzero => {
let y = self.const_bool(true);
let llvm_name = &format!("llvm.cttz.i{width}");
let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]);
self.intcast(ret, llret_ty, false)
self.intcast(ret, result.layout.llvm_type(self), false)
}
sym::ctpop => {
let ret = self.call_intrinsic(
&format!("llvm.ctpop.i{width}"),
&[args[0].immediate()],
);
self.intcast(ret, llret_ty, false)
self.intcast(ret, result.layout.llvm_type(self), false)
}
sym::bswap => {
if width == 8 {
@ -554,16 +544,16 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
// Unpack non-power-of-2 #[repr(packed, simd)] arguments.
// This gives them the expected layout of a regular #[repr(simd)] vector.
let mut loaded_args = Vec::new();
for (ty, arg) in arg_tys.iter().zip(args) {
for arg in args {
loaded_args.push(
// #[repr(packed, simd)] vectors are passed like arrays (as references,
// with reduced alignment and no padding) rather than as immediates.
// We can use a vector load to fix the layout and turn the argument
// into an immediate.
if ty.is_simd()
if arg.layout.ty.is_simd()
&& let OperandValue::Ref(place) = arg.val
{
let (size, elem_ty) = ty.simd_size_and_type(self.tcx());
let (size, elem_ty) = arg.layout.ty.simd_size_and_type(self.tcx());
let elem_ll_ty = match elem_ty.kind() {
ty::Float(f) => self.type_float_from_ty(*f),
ty::Int(i) => self.type_int_from_ty(*i),
@ -580,10 +570,10 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
);
}
let llret_ty = if ret_ty.is_simd()
&& let BackendRepr::Memory { .. } = self.layout_of(ret_ty).layout.backend_repr
let llret_ty = if result.layout.ty.is_simd()
&& let BackendRepr::Memory { .. } = result.layout.backend_repr
{
let (size, elem_ty) = ret_ty.simd_size_and_type(self.tcx());
let (size, elem_ty) = result.layout.ty.simd_size_and_type(self.tcx());
let elem_ll_ty = match elem_ty.kind() {
ty::Float(f) => self.type_float_from_ty(*f),
ty::Int(i) => self.type_int_from_ty(*i),
@ -593,16 +583,15 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
};
self.type_vector(elem_ll_ty, size)
} else {
llret_ty
result.layout.llvm_type(self)
};
match generic_simd_intrinsic(
self,
name,
callee_ty,
fn_args,
&loaded_args,
ret_ty,
result.layout.ty,
llret_ty,
span,
) {
@ -621,9 +610,8 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
};
if result.layout.ty.is_bool() {
OperandRef::from_immediate_or_packed_pair(self, llval, result.layout)
.val
.store(self, result);
let val = self.from_immediate(llval);
self.store_to_place(val, result.val);
} else if !result.layout.ty.is_unit() {
self.store_to_place(llval, result.val);
}
@ -1151,7 +1139,6 @@ fn get_rust_try_fn<'a, 'll, 'tcx>(
fn generic_simd_intrinsic<'ll, 'tcx>(
bx: &mut Builder<'_, 'll, 'tcx>,
name: Symbol,
callee_ty: Ty<'tcx>,
fn_args: GenericArgsRef<'tcx>,
args: &[OperandRef<'tcx, &'ll Value>],
ret_ty: Ty<'tcx>,
@ -1222,26 +1209,22 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
bx.trunc(i_xn_msb, bx.type_vector(bx.type_i1(), in_len))
}
let tcx = bx.tcx();
let sig = tcx.normalize_erasing_late_bound_regions(bx.typing_env(), callee_ty.fn_sig(tcx));
let arg_tys = sig.inputs();
// Sanity-check: all vector arguments must be immediates.
if cfg!(debug_assertions) {
for (ty, arg) in arg_tys.iter().zip(args) {
if ty.is_simd() {
for arg in args {
if arg.layout.ty.is_simd() {
assert_matches!(arg.val, OperandValue::Immediate(_));
}
}
}
if name == sym::simd_select_bitmask {
let (len, _) = require_simd!(arg_tys[1], SimdArgument);
let (len, _) = require_simd!(args[1].layout.ty, SimdArgument);
let expected_int_bits = len.max(8).next_power_of_two();
let expected_bytes = len.div_ceil(8);
let mask_ty = arg_tys[0];
let mask_ty = args[0].layout.ty;
let mask = match mask_ty.kind() {
ty::Int(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(),
ty::Uint(i) if i.bit_width() == Some(expected_int_bits) => args[0].immediate(),
@ -1275,8 +1258,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
}
// every intrinsic below takes a SIMD vector as its first argument
let (in_len, in_elem) = require_simd!(arg_tys[0], SimdInput);
let in_ty = arg_tys[0];
let (in_len, in_elem) = require_simd!(args[0].layout.ty, SimdInput);
let in_ty = args[0].layout.ty;
let comparison = match name {
sym::simd_eq => Some(BinOp::Eq),
@ -1407,13 +1390,13 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
if name == sym::simd_insert || name == sym::simd_insert_dyn {
require!(
in_elem == arg_tys[2],
in_elem == args[2].layout.ty,
InvalidMonomorphization::InsertedType {
span,
name,
in_elem,
in_ty,
out_ty: arg_tys[2]
out_ty: args[2].layout.ty
}
);
@ -1464,7 +1447,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
if name == sym::simd_select {
let m_elem_ty = in_elem;
let m_len = in_len;
let (v_len, _) = require_simd!(arg_tys[1], SimdArgument);
let (v_len, _) = require_simd!(args[1].layout.ty, SimdArgument);
require!(
m_len == v_len,
InvalidMonomorphization::MismatchedLengths { span, name, m_len, v_len }
@ -1665,9 +1648,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
// The second argument must be a simd vector with an element type that's a pointer
// to the element type of the first argument
let (_, element_ty0) = require_simd!(in_ty, SimdFirst);
let (out_len, element_ty1) = require_simd!(arg_tys[1], SimdSecond);
let (out_len, element_ty1) = require_simd!(args[1].layout.ty, SimdSecond);
// The element type of the third argument must be a signed integer type of any width:
let (out_len2, element_ty2) = require_simd!(arg_tys[2], SimdThird);
let (out_len2, element_ty2) = require_simd!(args[2].layout.ty, SimdThird);
require_simd!(ret_ty, SimdReturn);
// Of the same length:
@ -1678,7 +1661,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
name,
in_len,
in_ty,
arg_ty: arg_tys[1],
arg_ty: args[1].layout.ty,
out_len
}
);
@ -1689,7 +1672,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
name,
in_len,
in_ty,
arg_ty: arg_tys[2],
arg_ty: args[2].layout.ty,
out_len: out_len2
}
);
@ -1709,7 +1692,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
span,
name,
expected_element: element_ty1,
second_arg: arg_tys[1],
second_arg: args[1].layout.ty,
in_elem,
in_ty,
mutability: ExpectedPointerMutability::Not,
@ -1770,10 +1753,10 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
let (mask_len, mask_elem) = (in_len, in_elem);
// The second argument must be a pointer matching the element type
let pointer_ty = arg_tys[1];
let pointer_ty = args[1].layout.ty;
// The last argument is a passthrough vector providing values for disabled lanes
let values_ty = arg_tys[2];
let values_ty = args[2].layout.ty;
let (values_len, values_elem) = require_simd!(values_ty, SimdThird);
require_simd!(ret_ty, SimdReturn);
@ -1861,10 +1844,10 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
let (mask_len, mask_elem) = (in_len, in_elem);
// The second argument must be a pointer matching the element type
let pointer_ty = arg_tys[1];
let pointer_ty = args[1].layout.ty;
// The last argument specifies the values to store to memory
let values_ty = arg_tys[2];
let values_ty = args[2].layout.ty;
let (values_len, values_elem) = require_simd!(values_ty, SimdThird);
// Of the same length:
@ -1944,8 +1927,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
// The second argument must be a simd vector with an element type that's a pointer
// to the element type of the first argument
let (_, element_ty0) = require_simd!(in_ty, SimdFirst);
let (element_len1, element_ty1) = require_simd!(arg_tys[1], SimdSecond);
let (element_len2, element_ty2) = require_simd!(arg_tys[2], SimdThird);
let (element_len1, element_ty1) = require_simd!(args[1].layout.ty, SimdSecond);
let (element_len2, element_ty2) = require_simd!(args[2].layout.ty, SimdThird);
// Of the same length:
require!(
@ -1955,7 +1938,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
name,
in_len,
in_ty,
arg_ty: arg_tys[1],
arg_ty: args[1].layout.ty,
out_len: element_len1
}
);
@ -1966,7 +1949,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
name,
in_len,
in_ty,
arg_ty: arg_tys[2],
arg_ty: args[2].layout.ty,
out_len: element_len2
}
);
@ -1981,7 +1964,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
span,
name,
expected_element: element_ty1,
second_arg: arg_tys[1],
second_arg: args[1].layout.ty,
in_elem,
in_ty,
mutability: ExpectedPointerMutability::Mut,
@ -2503,7 +2486,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
let ptrs = args[0].immediate();
// The second argument must be a ptr-sized integer.
// (We don't care about the signedness, this is wrapping anyway.)
let (_offsets_len, offsets_elem) = arg_tys[1].simd_size_and_type(bx.tcx());
let (_offsets_len, offsets_elem) = args[1].layout.ty.simd_size_and_type(bx.tcx());
if !matches!(offsets_elem.kind(), ty::Int(ty::IntTy::Isize) | ty::Uint(ty::UintTy::Usize)) {
span_bug!(
span,
@ -2527,8 +2510,8 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
return_error!(InvalidMonomorphization::ExpectedVectorElementType {
span,
name,
expected_element: arg_tys[0].simd_size_and_type(bx.tcx()).1,
vector_type: arg_tys[0]
expected_element: args[0].layout.ty.simd_size_and_type(bx.tcx()).1,
vector_type: args[0].layout.ty
});
}
};

View file

@ -3,7 +3,7 @@ mod raw_dylib;
use std::collections::BTreeSet;
use std::ffi::OsString;
use std::fs::{File, OpenOptions, read};
use std::io::{BufWriter, Write};
use std::io::{BufReader, BufWriter, Write};
use std::ops::{ControlFlow, Deref};
use std::path::{Path, PathBuf};
use std::process::{Output, Stdio};
@ -184,6 +184,12 @@ pub fn link_binary(
);
}
if sess.target.binary_format == BinaryFormat::Elf {
if let Err(err) = warn_if_linked_with_gold(sess, &out_filename) {
info!(?err, "Error while checking if gold was the linker");
}
}
if output.is_stdout() {
if output.is_tty() {
sess.dcx().emit_err(errors::BinaryOutputToTty {
@ -3375,3 +3381,54 @@ fn add_lld_args(
}
}
}
// gold has been deprecated with binutils 2.44
// and is known to behave incorrectly around Rust programs.
// There have been reports of being unable to bootstrap with gold:
// https://github.com/rust-lang/rust/issues/139425
// Additionally, gold miscompiles SHF_GNU_RETAIN sections, which are
// emitted with `#[used(linker)]`.
fn warn_if_linked_with_gold(sess: &Session, path: &Path) -> Result<(), Box<dyn std::error::Error>> {
use object::read::elf::{FileHeader, SectionHeader};
use object::read::{ReadCache, ReadRef, Result};
use object::{Endianness, elf};
fn elf_has_gold_version_note<'a>(
elf: &impl FileHeader,
data: impl ReadRef<'a>,
) -> Result<bool> {
let endian = elf.endian()?;
let section =
elf.sections(endian, data)?.section_by_name(endian, b".note.gnu.gold-version");
if let Some((_, section)) = section {
if let Some(mut notes) = section.notes(endian, data)? {
return Ok(notes.any(|note| {
note.is_ok_and(|note| note.n_type(endian) == elf::NT_GNU_GOLD_VERSION)
}));
}
}
Ok(false)
}
let data = ReadCache::new(BufReader::new(File::open(path)?));
let was_linked_with_gold = if sess.target.pointer_width == 64 {
let elf = elf::FileHeader64::<Endianness>::parse(&data)?;
elf_has_gold_version_note(elf, &data)?
} else if sess.target.pointer_width == 32 {
let elf = elf::FileHeader32::<Endianness>::parse(&data)?;
elf_has_gold_version_note(elf, &data)?
} else {
return Ok(());
};
if was_linked_with_gold {
let mut warn =
sess.dcx().struct_warn("the gold linker is deprecated and has known bugs with Rust");
warn.help("consider using LLD or ld from GNU binutils instead");
warn.emit();
}
Ok(())
}

View file

@ -1,7 +1,7 @@
use rustc_abi::WrappingRange;
use rustc_middle::bug;
use rustc_middle::mir::SourceInfo;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::{bug, span_bug};
use rustc_session::config::OptLevel;
use rustc_span::sym;
@ -60,18 +60,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
source_info: SourceInfo,
) -> Result<(), ty::Instance<'tcx>> {
let span = source_info.span;
let callee_ty = instance.ty(bx.tcx(), bx.typing_env());
let ty::FnDef(def_id, fn_args) = *callee_ty.kind() else {
span_bug!(span, "expected fn item type, found {}", callee_ty);
};
let sig = callee_ty.fn_sig(bx.tcx());
let sig = bx.tcx().normalize_erasing_late_bound_regions(bx.typing_env(), sig);
let arg_tys = sig.inputs();
let ret_ty = sig.output();
let name = bx.tcx().item_name(def_id);
let name = bx.tcx().item_name(instance.def_id());
let name_str = name.as_str();
let fn_args = instance.args;
// If we're swapping something that's *not* an `OperandValue::Ref`,
// then we can do it directly and avoid the alloca.
@ -97,13 +89,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
}
let llret_ty = bx.backend_type(bx.layout_of(ret_ty));
let ret_llval = |bx: &mut Bx, llval| {
if result.layout.ty.is_bool() {
OperandRef::from_immediate_or_packed_pair(bx, llval, result.layout)
.val
.store(bx, result);
let val = bx.from_immediate(llval);
bx.store_to_place(val, result.val);
} else if !result.layout.ty.is_unit() {
bx.store_to_place(llval, result.val);
}
@ -143,7 +132,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
sym::vtable_align => ty::COMMON_VTABLE_ENTRIES_ALIGN,
_ => bug!(),
};
let value = meth::VirtualIndex::from_index(idx).get_usize(bx, vtable, callee_ty);
let value = meth::VirtualIndex::from_index(idx).get_usize(
bx,
vtable,
instance.ty(bx.tcx(), bx.typing_env()),
);
match name {
// Size is always <= isize::MAX.
sym::vtable_size => {
@ -164,7 +157,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
| sym::type_name
| sym::variant_count => {
let value = bx.tcx().const_eval_instance(bx.typing_env(), instance, span).unwrap();
OperandRef::from_const(bx, value, ret_ty).immediate_or_packed_pair(bx)
OperandRef::from_const(bx, value, result.layout.ty).immediate_or_packed_pair(bx)
}
sym::arith_offset => {
let ty = fn_args.type_at(0);
@ -248,7 +241,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bx.or_disjoint(a, b)
}
sym::exact_div => {
let ty = arg_tys[0];
let ty = args[0].layout.ty;
match int_type_width_signed(ty, bx.tcx()) {
Some((_width, signed)) => {
if signed {
@ -268,7 +261,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
}
sym::fadd_fast | sym::fsub_fast | sym::fmul_fast | sym::fdiv_fast | sym::frem_fast => {
match float_type_width(arg_tys[0]) {
match float_type_width(args[0].layout.ty) {
Some(_width) => match name {
sym::fadd_fast => bx.fadd_fast(args[0].immediate(), args[1].immediate()),
sym::fsub_fast => bx.fsub_fast(args[0].immediate(), args[1].immediate()),
@ -281,7 +274,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicFloatType {
span,
name,
ty: arg_tys[0],
ty: args[0].layout.ty,
});
return Ok(());
}
@ -291,7 +284,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
| sym::fsub_algebraic
| sym::fmul_algebraic
| sym::fdiv_algebraic
| sym::frem_algebraic => match float_type_width(arg_tys[0]) {
| sym::frem_algebraic => match float_type_width(args[0].layout.ty) {
Some(_width) => match name {
sym::fadd_algebraic => {
bx.fadd_algebraic(args[0].immediate(), args[1].immediate())
@ -314,31 +307,32 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicFloatType {
span,
name,
ty: arg_tys[0],
ty: args[0].layout.ty,
});
return Ok(());
}
},
sym::float_to_int_unchecked => {
if float_type_width(arg_tys[0]).is_none() {
if float_type_width(args[0].layout.ty).is_none() {
bx.tcx().dcx().emit_err(InvalidMonomorphization::FloatToIntUnchecked {
span,
ty: arg_tys[0],
ty: args[0].layout.ty,
});
return Ok(());
}
let Some((_width, signed)) = int_type_width_signed(ret_ty, bx.tcx()) else {
let Some((_width, signed)) = int_type_width_signed(result.layout.ty, bx.tcx())
else {
bx.tcx().dcx().emit_err(InvalidMonomorphization::FloatToIntUnchecked {
span,
ty: ret_ty,
ty: result.layout.ty,
});
return Ok(());
};
if signed {
bx.fptosi(args[0].immediate(), llret_ty)
bx.fptosi(args[0].immediate(), bx.backend_type(result.layout))
} else {
bx.fptoui(args[0].immediate(), llret_ty)
bx.fptoui(args[0].immediate(), bx.backend_type(result.layout))
}
}

View file

@ -4,7 +4,18 @@
//! green/native threading. This is just a bare-bones enough solution for
//! librustdoc, it is not production quality at all.
cfg_select! {
// cfg(bootstrap)
macro_rules! cfg_select_dispatch {
($($tokens:tt)*) => {
#[cfg(bootstrap)]
cfg_match! { $($tokens)* }
#[cfg(not(bootstrap))]
cfg_select! { $($tokens)* }
};
}
cfg_select_dispatch! {
target_os = "linux" => {
mod linux;
use linux as imp;

View file

@ -10,6 +10,8 @@
#![allow(internal_features)]
#![allow(rustc::default_hash_types)]
#![allow(rustc::potential_query_instability)]
#![cfg_attr(bootstrap, feature(cfg_match))]
#![cfg_attr(not(bootstrap), feature(cfg_select))]
#![deny(unsafe_op_in_unsafe_fn)]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
@ -19,7 +21,6 @@
#![feature(ascii_char_variants)]
#![feature(assert_matches)]
#![feature(auto_traits)]
#![feature(cfg_select)]
#![feature(core_intrinsics)]
#![feature(dropck_eyepatch)]
#![feature(extend_one)]

View file

@ -859,8 +859,19 @@ fn get_thread_id() -> u32 {
std::thread::current().id().as_u64().get() as u32
}
// cfg(bootstrap)
macro_rules! cfg_select_dispatch {
($($tokens:tt)*) => {
#[cfg(bootstrap)]
cfg_match! { $($tokens)* }
#[cfg(not(bootstrap))]
cfg_select! { $($tokens)* }
};
}
// Memory reporting
cfg_select! {
cfg_select_dispatch! {
windows => {
pub fn get_resident_set_size() -> Option<usize> {
use windows::{

View file

@ -8,7 +8,6 @@ macro_rules! arena_types {
[] asm_template: rustc_ast::InlineAsmTemplatePiece,
[] attribute: rustc_hir::Attribute,
[] owner_info: rustc_hir::OwnerInfo<'tcx>,
[] use_path: rustc_hir::UsePath<'tcx>,
[] lit: rustc_hir::Lit,
[] macro_def: rustc_ast::MacroDef,
]);

View file

@ -584,7 +584,7 @@ impl<CTX: crate::HashStableContext> ToStableHashKey<CTX> for Namespace {
}
/// Just a helper separate structure for each namespace.
#[derive(Copy, Clone, Default, Debug)]
#[derive(Copy, Clone, Default, Debug, HashStable_Generic)]
pub struct PerNS<T> {
pub value_ns: T,
pub type_ns: T,
@ -596,10 +596,16 @@ impl<T> PerNS<T> {
PerNS { value_ns: f(self.value_ns), type_ns: f(self.type_ns), macro_ns: f(self.macro_ns) }
}
/// Note: Do you really want to use this? Often you know which namespace a
/// name will belong in, and you can consider just that namespace directly,
/// rather than iterating through all of them.
pub fn into_iter(self) -> IntoIter<T, 3> {
[self.value_ns, self.type_ns, self.macro_ns].into_iter()
}
/// Note: Do you really want to use this? Often you know which namespace a
/// name will belong in, and you can consider just that namespace directly,
/// rather than iterating through all of them.
pub fn iter(&self) -> IntoIter<&T, 3> {
[&self.value_ns, &self.type_ns, &self.macro_ns].into_iter()
}
@ -634,6 +640,10 @@ impl<T> PerNS<Option<T>> {
}
/// Returns an iterator over the items which are `Some`.
///
/// Note: Do you really want to use this? Often you know which namespace a
/// name will belong in, and you can consider just that namespace directly,
/// rather than iterating through all of them.
pub fn present_items(self) -> impl Iterator<Item = T> {
[self.type_ns, self.value_ns, self.macro_ns].into_iter().flatten()
}

View file

@ -30,7 +30,7 @@ use thin_vec::ThinVec;
use tracing::debug;
use crate::LangItem;
use crate::def::{CtorKind, DefKind, Res};
use crate::def::{CtorKind, DefKind, PerNS, Res};
use crate::def_id::{DefId, LocalDefIdMap};
pub(crate) use crate::hir_id::{HirId, ItemLocalId, ItemLocalMap, OwnerId};
use crate::intravisit::{FnKind, VisitorExt};
@ -347,7 +347,7 @@ pub struct Path<'hir, R = Res> {
}
/// Up to three resolutions for type, value and macro namespaces.
pub type UsePath<'hir> = Path<'hir, SmallVec<[Res; 3]>>;
pub type UsePath<'hir> = Path<'hir, PerNS<Option<Res>>>;
impl Path<'_> {
pub fn is_global(&self) -> bool {
@ -2370,6 +2370,10 @@ impl Expr<'_> {
// Lang item paths cannot currently be local variables or statics.
ExprKind::Path(QPath::LangItem(..)) => false,
// Suppress errors for bad expressions.
ExprKind::Err(_guar)
| ExprKind::Let(&LetExpr { recovered: ast::Recovered::Yes(_guar), .. }) => true,
// Partially qualified paths in expressions can only legally
// refer to associated items which are always rvalues.
ExprKind::Path(QPath::TypeRelative(..))
@ -2401,8 +2405,7 @@ impl Expr<'_> {
| ExprKind::Binary(..)
| ExprKind::Yield(..)
| ExprKind::Cast(..)
| ExprKind::DropTemps(..)
| ExprKind::Err(_) => false,
| ExprKind::DropTemps(..) => false,
}
}

View file

@ -1148,7 +1148,7 @@ pub fn walk_use<'v, V: Visitor<'v>>(
hir_id: HirId,
) -> V::Result {
let UsePath { segments, ref res, span } = *path;
for &res in res {
for res in res.present_items() {
try_visit!(visitor.visit_path(&Path { segments, res, span }, hir_id));
}
V::Result::output()

View file

@ -1012,10 +1012,6 @@ fn run_required_analyses(tcx: TyCtxt<'_>) {
{
tcx.ensure_ok().mir_drops_elaborated_and_const_checked(def_id);
}
});
});
sess.time("coroutine_obligations", || {
tcx.par_hir_body_owners(|def_id| {
if tcx.is_coroutine(def_id.to_def_id()) {
tcx.ensure_ok().mir_coroutine_witnesses(def_id);
let _ = tcx.ensure_ok().check_coroutine_obligations(

View file

@ -715,6 +715,7 @@ fn test_unstable_options_tracking_hash() {
untracked!(no_analysis, true);
untracked!(no_leak_check, true);
untracked!(no_parallel_backend, true);
untracked!(no_steal_thir, true);
untracked!(parse_crate_root_only, true);
// `pre_link_arg` is omitted because it just forwards to `pre_link_args`.
untracked!(pre_link_args, vec![String::from("abc"), String::from("def")]);

View file

@ -328,16 +328,19 @@ impl<'tcx> LateLintPass<'tcx> for TypeIr {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
let rustc_hir::ItemKind::Use(path, kind) = item.kind else { return };
let is_mod_inherent = |def_id| cx.tcx.is_diagnostic_item(sym::type_ir_inherent, def_id);
let is_mod_inherent = |res: Res| {
res.opt_def_id()
.is_some_and(|def_id| cx.tcx.is_diagnostic_item(sym::type_ir_inherent, def_id))
};
// Path segments except for the final.
if let Some(seg) =
path.segments.iter().find(|seg| seg.res.opt_def_id().is_some_and(is_mod_inherent))
{
if let Some(seg) = path.segments.iter().find(|seg| is_mod_inherent(seg.res)) {
cx.emit_span_lint(USAGE_OF_TYPE_IR_INHERENT, seg.ident.span, TypeIrInherentUsage);
}
// Final path resolutions, like `use rustc_type_ir::inherent`
else if path.res.iter().any(|res| res.opt_def_id().is_some_and(is_mod_inherent)) {
else if let Some(type_ns) = path.res.type_ns
&& is_mod_inherent(type_ns)
{
cx.emit_span_lint(
USAGE_OF_TYPE_IR_INHERENT,
path.segments.last().unwrap().ident.span,
@ -346,13 +349,12 @@ impl<'tcx> LateLintPass<'tcx> for TypeIr {
}
let (lo, hi, snippet) = match path.segments {
[.., penultimate, segment]
if penultimate.res.opt_def_id().is_some_and(is_mod_inherent) =>
{
[.., penultimate, segment] if is_mod_inherent(penultimate.res) => {
(segment.ident.span, item.kind.ident().unwrap().span, "*")
}
[.., segment]
if path.res.iter().flat_map(Res::opt_def_id).any(is_mod_inherent)
if let Some(type_ns) = path.res.type_ns
&& is_mod_inherent(type_ns)
&& let rustc_hir::UseKind::Single(ident) = kind =>
{
let (lo, snippet) =

View file

@ -1,4 +1,3 @@
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{self as hir};
use rustc_session::{declare_lint, declare_lint_pass};
use rustc_span::kw;
@ -47,17 +46,15 @@ declare_lint_pass!(UnqualifiedLocalImports => [UNQUALIFIED_LOCAL_IMPORTS]);
impl<'tcx> LateLintPass<'tcx> for UnqualifiedLocalImports {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
let hir::ItemKind::Use(path, _kind) = item.kind else { return };
// `path` has three resolutions for the type, module, value namespaces.
// Check if any of them qualifies: local crate, and not a macro.
// (Macros can't be imported any other way so we don't complain about them.)
let is_local_import = |res: &Res| {
matches!(
res,
hir::def::Res::Def(def_kind, def_id)
if def_id.is_local() && !matches!(def_kind, DefKind::Macro(_)),
)
};
if !path.res.iter().any(is_local_import) {
// Check the type and value namespace resolutions for a local crate.
let is_local_import = matches!(
path.res.type_ns,
Some(hir::def::Res::Def(_, def_id)) if def_id.is_local()
) || matches!(
path.res.value_ns,
Some(hir::def::Res::Def(_, def_id)) if def_id.is_local()
);
if !is_local_import {
return;
}
// So this does refer to something local. Let's check whether it starts with `self`,

View file

@ -535,7 +535,8 @@ rustc_queries! {
separate_provide_extern
}
/// Fetch the THIR for a given body.
/// Fetch the THIR for a given body. The THIR body gets stolen by unsafety checking unless
/// `-Zno-steal-thir` is on.
query thir_body(key: LocalDefId) -> Result<(&'tcx Steal<thir::Thir<'tcx>>, thir::ExprId), ErrorGuaranteed> {
// Perf tests revealed that hashing THIR is inefficient (see #85729).
no_hash

View file

@ -201,9 +201,14 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
/// Handle closures/coroutines/inline-consts, which is unsafecked with their parent body.
fn visit_inner_body(&mut self, def: LocalDefId) {
if let Ok((inner_thir, expr)) = self.tcx.thir_body(def) {
// Runs all other queries that depend on THIR.
// Run all other queries that depend on THIR.
self.tcx.ensure_done().mir_built(def);
let inner_thir = &inner_thir.steal();
let inner_thir = if self.tcx.sess.opts.unstable_opts.no_steal_thir {
&inner_thir.borrow()
} else {
// We don't have other use for the THIR. Steal it to reduce memory usage.
&inner_thir.steal()
};
let hir_context = self.tcx.local_def_id_to_hir_id(def);
let safety_context = mem::replace(&mut self.safety_context, SafetyContext::Safe);
let mut inner_visitor = UnsafetyVisitor {
@ -1157,7 +1162,12 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
let Ok((thir, expr)) = tcx.thir_body(def) else { return };
// Runs all other queries that depend on THIR.
tcx.ensure_done().mir_built(def);
let thir = &thir.steal();
let thir = if tcx.sess.opts.unstable_opts.no_steal_thir {
&thir.borrow()
} else {
// We don't have other use for the THIR. Steal it to reduce memory usage.
&thir.steal()
};
let hir_id = tcx.local_def_id_to_hir_id(def);
let safety_context = tcx.hir_fn_sig_by_hir_id(hir_id).map_or(SafetyContext::Safe, |fn_sig| {

View file

@ -1047,26 +1047,21 @@ fn find_fallback_pattern_typo<'tcx>(
let hir::ItemKind::Use(path, _) = item.kind else {
continue;
};
for res in &path.res {
if let Res::Def(DefKind::Const, id) = res
&& infcx.can_eq(param_env, ty, cx.tcx.type_of(id).instantiate_identity())
{
if cx.tcx.visibility(id).is_accessible_from(parent, cx.tcx) {
// The original const is accessible, suggest using it directly.
let item_name = cx.tcx.item_name(*id);
accessible.push(item_name);
accessible_path.push(with_no_trimmed_paths!(cx.tcx.def_path_str(id)));
} else if cx
.tcx
.visibility(item.owner_id)
.is_accessible_from(parent, cx.tcx)
{
// The const is accessible only through the re-export, point at
// the `use`.
let ident = item.kind.ident().unwrap();
imported.push(ident.name);
imported_spans.push(ident.span);
}
if let Some(value_ns) = path.res.value_ns
&& let Res::Def(DefKind::Const, id) = value_ns
&& infcx.can_eq(param_env, ty, cx.tcx.type_of(id).instantiate_identity())
{
if cx.tcx.visibility(id).is_accessible_from(parent, cx.tcx) {
// The original const is accessible, suggest using it directly.
let item_name = cx.tcx.item_name(id);
accessible.push(item_name);
accessible_path.push(with_no_trimmed_paths!(cx.tcx.def_path_str(id)));
} else if cx.tcx.visibility(item.owner_id).is_accessible_from(parent, cx.tcx) {
// The const is accessible only through the re-export, point at
// the `use`.
let ident = item.kind.ident().unwrap();
imported.push(ident.name);
imported_spans.push(ident.span);
}
}
}

View file

@ -1,6 +1,7 @@
use std::{fmt, iter, mem};
use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx};
use rustc_hir::def::DefKind;
use rustc_hir::lang_items::LangItem;
use rustc_index::Idx;
use rustc_middle::mir::*;
@ -254,8 +255,19 @@ where
// impl_item_refs may be empty if drop fn is not implemented in 'impl AsyncDrop for ...'
// (#140974).
// Such code will report error, so just generate sync drop here and return
let Some(drop_fn_def_id) =
tcx.associated_item_def_ids(drop_trait).into_iter().nth(0).copied()
let Some(drop_fn_def_id) = tcx
.associated_item_def_ids(drop_trait)
.first()
.and_then(|def_id| {
if tcx.def_kind(def_id) == DefKind::AssocFn
&& tcx.check_args_compatible(*def_id, trait_args)
{
Some(def_id)
} else {
None
}
})
.copied()
else {
tcx.dcx().span_delayed_bug(
self.elaborator.body().span,

View file

@ -2312,7 +2312,9 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
}
fn check_macro_use(&self, hir_id: HirId, attr: &Attribute, target: Target) {
let name = attr.name().unwrap();
let Some(name) = attr.name() else {
return;
};
match target {
Target::ExternCrate | Target::Mod => {}
_ => {

View file

@ -132,7 +132,7 @@ impl<'tcx> Visitor<'tcx> for ExportableItemCollector<'tcx> {
self.add_exportable(def_id);
}
hir::ItemKind::Use(path, _) => {
for res in &path.res {
for res in path.res.present_items() {
// Only local items are exportable.
if let Some(res_id) = res.opt_def_id()
&& let Some(res_id) = res_id.as_local()

View file

@ -118,9 +118,10 @@ impl<'a, 'ra, 'tcx> UnusedImportCheckVisitor<'a, 'ra, 'tcx> {
ast::UseTreeKind::Simple(Some(ident)) => {
if ident.name == kw::Underscore
&& !self.r.import_res_map.get(&id).is_some_and(|per_ns| {
per_ns.iter().filter_map(|res| res.as_ref()).any(|res| {
matches!(res, Res::Def(DefKind::Trait | DefKind::TraitAlias, _))
})
matches!(
per_ns.type_ns,
Some(Res::Def(DefKind::Trait | DefKind::TraitAlias, _))
)
})
{
self.unused_import(self.base_id).add(id);
@ -193,6 +194,16 @@ impl<'a, 'ra, 'tcx> UnusedImportCheckVisitor<'a, 'ra, 'tcx> {
continue;
}
let module = self
.r
.get_nearest_non_block_module(self.r.local_def_id(extern_crate.id).to_def_id());
if module.no_implicit_prelude {
// If the module has `no_implicit_prelude`, then we don't suggest
// replacing the extern crate with a use, as it would not be
// inserted into the prelude. User writes `extern` style deliberately.
continue;
}
let vis_span = extern_crate
.vis_span
.find_ancestor_inside(extern_crate.span)

View file

@ -4898,11 +4898,28 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
self.resolve_expr(e, Some(expr));
}
ExprKind::Let(ref pat, ref scrutinee, _, _) => {
ExprKind::Let(ref pat, ref scrutinee, _, Recovered::No) => {
self.visit_expr(scrutinee);
self.resolve_pattern_top(pat, PatternSource::Let);
}
ExprKind::Let(ref pat, ref scrutinee, _, Recovered::Yes(_)) => {
self.visit_expr(scrutinee);
// This is basically a tweaked, inlined `resolve_pattern_top`.
let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
self.resolve_pattern(pat, PatternSource::Let, &mut bindings);
// We still collect the bindings in this `let` expression which is in
// an invalid position (and therefore shouldn't declare variables into
// its parent scope). To avoid unnecessary errors though, we do just
// reassign the resolutions to `Res::Err`.
for (_, bindings) in &mut bindings {
for (_, binding) in bindings {
*binding = Res::Err;
}
}
self.apply_pattern_bindings(bindings);
}
ExprKind::If(ref cond, ref then, ref opt_else) => {
self.with_rib(ValueNS, RibKind::Normal, |this| {
let old = this.diag_metadata.in_if_condition.replace(cond);

View file

@ -2366,6 +2366,8 @@ options! {
"run LLVM in non-parallel mode (while keeping codegen-units and ThinLTO)"),
no_profiler_runtime: bool = (false, parse_no_value, [TRACKED],
"prevent automatic injection of the profiler_builtins crate"),
no_steal_thir: bool = (false, parse_bool, [UNTRACKED],
"don't steal the THIR when we're done with it; useful for rustc drivers (default: no)"),
no_trait_vptr: bool = (false, parse_no_value, [TRACKED],
"disable generation of trait vptr in vtable for upcasting"),
no_unique_section_names: bool = (false, parse_bool, [TRACKED],

View file

@ -29,7 +29,18 @@ pub(crate) fn analyze_source_file(src: &str) -> (Vec<RelativeBytePos>, Vec<Multi
(lines, multi_byte_chars)
}
cfg_select! {
// cfg(bootstrap)
macro_rules! cfg_select_dispatch {
($($tokens:tt)*) => {
#[cfg(bootstrap)]
cfg_match! { $($tokens)* }
#[cfg(not(bootstrap))]
cfg_select! { $($tokens)* }
};
}
cfg_select_dispatch! {
any(target_arch = "x86", target_arch = "x86_64") => {
fn analyze_source_file_dispatch(
src: &str,

View file

@ -17,10 +17,11 @@
// tidy-alphabetical-start
#![allow(internal_features)]
#![cfg_attr(bootstrap, feature(cfg_match))]
#![cfg_attr(not(bootstrap), feature(cfg_select))]
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
#![doc(rust_logo)]
#![feature(array_windows)]
#![feature(cfg_select)]
#![feature(core_io_borrowed_buf)]
#![feature(hash_set_entry)]
#![feature(if_let_guard)]

View file

@ -937,8 +937,10 @@ symbols! {
external_doc,
f,
f128,
f128_epsilon,
f128_nan,
f16,
f16_epsilon,
f16_nan,
f16c_target_feature,
f32,

View file

@ -200,7 +200,7 @@ pub trait Unsize<T: ?Sized> {
///
/// Constants are only allowed as patterns if (a) their type implements
/// `PartialEq`, and (b) interpreting the value of the constant as a pattern
/// is equialent to calling `PartialEq`. This ensures that constants used as
/// is equivalent to calling `PartialEq`. This ensures that constants used as
/// patterns cannot expose implementation details in an unexpected way or
/// cause semver hazards.
///

View file

@ -84,7 +84,7 @@ use crate::ptr;
/// use std::mem::ManuallyDrop;
///
/// pub struct BadOption<T> {
/// // Invariant: Has been dropped iff `is_some` is false.
/// // Invariant: Has been dropped if `is_some` is false.
/// value: ManuallyDrop<T>,
/// is_some: bool,
/// }

View file

@ -171,6 +171,7 @@ impl f128 {
/// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon
/// [`MANTISSA_DIGITS`]: f128::MANTISSA_DIGITS
#[unstable(feature = "f128", issue = "116909")]
#[rustc_diagnostic_item = "f128_epsilon"]
pub const EPSILON: f128 = 1.92592994438723585305597794258492732e-34_f128;
/// Smallest finite `f128` value.

View file

@ -168,6 +168,7 @@ impl f16 {
/// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon
/// [`MANTISSA_DIGITS`]: f16::MANTISSA_DIGITS
#[unstable(feature = "f16", issue = "116909")]
#[rustc_diagnostic_item = "f16_epsilon"]
pub const EPSILON: f16 = 9.7656e-4_f16;
/// Smallest finite `f16` value.

View file

@ -159,8 +159,11 @@ impl<T> OnceLock<T> {
/// Gets the mutable reference to the underlying value.
///
/// Returns `None` if the cell is uninitialized, or being initialized.
/// This method never blocks.
/// Returns `None` if the cell is uninitialized.
///
/// This method never blocks. Since it borrows the `OnceLock` mutably,
/// it is statically guaranteed that no active borrows to the `OnceLock`
/// exist, including from other threads.
#[inline]
#[stable(feature = "once_cell", since = "1.70.0")]
pub fn get_mut(&mut self) -> Option<&mut T> {
@ -315,7 +318,9 @@ impl<T> OnceLock<T> {
/// Gets the mutable reference of the contents of the cell, initializing
/// it to `f()` if the cell was uninitialized.
///
/// This method never blocks.
/// This method never blocks. Since it borrows the `OnceLock` mutably,
/// it is statically guaranteed that no active borrows to the `OnceLock`
/// exist, including from other threads.
///
/// # Panics
///
@ -405,7 +410,9 @@ impl<T> OnceLock<T> {
/// it to `f()` if the cell was uninitialized. If the cell was uninitialized
/// and `f()` failed, an error is returned.
///
/// This method never blocks.
/// This method never blocks. Since it borrows the `OnceLock` mutably,
/// it is statically guaranteed that no active borrows to the `OnceLock`
/// exist, including from other threads.
///
/// # Panics
///
@ -469,7 +476,8 @@ impl<T> OnceLock<T> {
///
/// Has no effect and returns `None` if the `OnceLock` was uninitialized.
///
/// Safety is guaranteed by requiring a mutable reference.
/// Since this method borrows the `OnceLock` mutably, it is statically guaranteed that
/// no active borrows to the `OnceLock` exist, including from other threads.
///
/// # Examples
///

View file

@ -1,17 +1,18 @@
# These defaults are meant for contributors to the standard library and documentation.
[build]
# When building the standard library, you almost never want to build the compiler itself.
build-stage = 0
test-stage = 0
bench-stage = 0
build-stage = 1
test-stage = 1
bench-stage = 1
[rust]
# This greatly increases the speed of rebuilds, especially when there are only minor changes. However, it makes the initial build slightly slower.
incremental = true
# Make the compiler and standard library faster to build, at the expense of a ~20% runtime slowdown.
lto = "off"
# Download rustc by default for library profile if compiler-affecting
# directories are not modified. For CI this is disabled.
# When building the standard library, you almost never want to build the compiler itself.
#
# If compiler-affecting directories are not modified, use precompiled rustc to speed up
# library development by skipping compiler builds.
download-rustc = "if-unchanged"
[llvm]

View file

@ -54,13 +54,15 @@ check-aux:
src/etc/test-float-parse \
$(BOOTSTRAP_ARGS)
# Run standard library tests in Miri.
$(Q)$(BOOTSTRAP) miri --stage 2 \
$(Q)MIRIFLAGS="-Zmiri-strict-provenance" \
$(BOOTSTRAP) miri --stage 2 \
library/coretests \
library/alloctests \
library/alloc \
$(BOOTSTRAP_ARGS) \
--no-doc
# Some doctests use file system operations to demonstrate dealing with `Result`.
# Some doctests use file system operations to demonstrate dealing with `Result`,
# so we have to run them with isolation disabled.
$(Q)MIRIFLAGS="-Zmiri-disable-isolation" \
$(BOOTSTRAP) miri --stage 2 \
library/coretests \
@ -70,22 +72,19 @@ check-aux:
--doc
# In `std` we cannot test everything, so we skip some modules.
$(Q)MIRIFLAGS="-Zmiri-disable-isolation" \
$(BOOTSTRAP) miri --stage 2 library/std \
$(BOOTSTRAP) miri --stage 2 \
library/std \
$(BOOTSTRAP_ARGS) \
--no-doc -- \
--skip fs:: --skip net:: --skip process:: --skip sys::fd:: --skip sys::pal::
$(Q)MIRIFLAGS="-Zmiri-disable-isolation" \
$(BOOTSTRAP) miri --stage 2 library/std \
$(BOOTSTRAP_ARGS) \
--doc -- \
-- \
--skip fs:: --skip net:: --skip process:: --skip sys::fd:: --skip sys::pal::
# Also test some very target-specific modules on other targets
# (making sure to cover an i686 target as well).
$(Q)MIRIFLAGS="-Zmiri-disable-isolation" BOOTSTRAP_SKIP_TARGET_SANITY=1 \
$(BOOTSTRAP) miri --stage 2 library/std \
$(BOOTSTRAP_ARGS) \
$(BOOTSTRAP) miri --stage 2 \
library/std \
--target aarch64-apple-darwin,i686-pc-windows-msvc \
--no-doc -- \
$(BOOTSTRAP_ARGS) \
-- \
time:: sync:: thread:: env::
dist:
$(Q)$(BOOTSTRAP) dist $(BOOTSTRAP_ARGS)

View file

@ -120,14 +120,12 @@ fn main() {
};
cmd.args(&args).env(dylib_path_var(), env::join_paths(&dylib_path).unwrap());
if let Some(crate_name) = crate_name {
if let Some(target) = env::var_os("RUSTC_TIME") {
if target == "all"
|| target.into_string().unwrap().split(',').any(|c| c.trim() == crate_name)
{
cmd.arg("-Ztime-passes");
}
}
if let Some(crate_name) = crate_name
&& let Some(target) = env::var_os("RUSTC_TIME")
&& (target == "all"
|| target.into_string().unwrap().split(',').any(|c| c.trim() == crate_name))
{
cmd.arg("-Ztime-passes");
}
// Print backtrace in case of ICE
@ -242,10 +240,10 @@ fn main() {
}
}
if env::var_os("RUSTC_BOLT_LINK_FLAGS").is_some() {
if let Some("rustc_driver") = crate_name {
cmd.arg("-Clink-args=-Wl,-q");
}
if env::var_os("RUSTC_BOLT_LINK_FLAGS").is_some()
&& let Some("rustc_driver") = crate_name
{
cmd.arg("-Clink-args=-Wl,-q");
}
let is_test = args.iter().any(|a| a == "--test");
@ -282,25 +280,24 @@ fn main() {
(child, status)
};
if env::var_os("RUSTC_PRINT_STEP_TIMINGS").is_some()
|| env::var_os("RUSTC_PRINT_STEP_RUSAGE").is_some()
if (env::var_os("RUSTC_PRINT_STEP_TIMINGS").is_some()
|| env::var_os("RUSTC_PRINT_STEP_RUSAGE").is_some())
&& let Some(crate_name) = crate_name
{
if let Some(crate_name) = crate_name {
let dur = start.elapsed();
// If the user requested resource usage data, then
// include that in addition to the timing output.
let rusage_data =
env::var_os("RUSTC_PRINT_STEP_RUSAGE").and_then(|_| format_rusage_data(child));
eprintln!(
"[RUSTC-TIMING] {} test:{} {}.{:03}{}{}",
crate_name,
is_test,
dur.as_secs(),
dur.subsec_millis(),
if rusage_data.is_some() { " " } else { "" },
rusage_data.unwrap_or_default(),
);
}
let dur = start.elapsed();
// If the user requested resource usage data, then
// include that in addition to the timing output.
let rusage_data =
env::var_os("RUSTC_PRINT_STEP_RUSAGE").and_then(|_| format_rusage_data(child));
eprintln!(
"[RUSTC-TIMING] {} test:{} {}.{:03}{}{}",
crate_name,
is_test,
dur.as_secs(),
dur.subsec_millis(),
if rusage_data.is_some() { " " } else { "" },
rusage_data.unwrap_or_default(),
);
}
if status.success() {

View file

@ -1,5 +1,6 @@
//! Implementation of compiling the compiler and standard library, in "check"-based modes.
use crate::core::build_steps::compile;
use crate::core::build_steps::compile::{
add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, std_crates_for_run_make,
};
@ -45,10 +46,12 @@ impl Step for Std {
const DEFAULT: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
let stage = run.builder.top_stage;
run.crate_or_deps("sysroot")
.crate_or_deps("coretests")
.crate_or_deps("alloctests")
.path("library")
.default_condition(stage != 0)
}
fn make_run(run: RunConfig<'_>) {
@ -62,6 +65,12 @@ impl Step for Std {
let target = self.target;
let compiler = builder.compiler(builder.top_stage, builder.config.build);
if builder.top_stage == 0 {
// Reuse the stage0 libstd
builder.ensure(compile::Std::new(compiler, target));
return;
}
let mut cargo = builder::Cargo::new(
builder,
compiler,

View file

@ -207,16 +207,18 @@ impl Step for Rustc {
let compiler = builder.compiler(builder.top_stage, builder.config.build);
let target = self.target;
if compiler.stage != 0 {
// If we're not in stage 0, then we won't have a std from the beta
// compiler around. That means we need to make sure there's one in
// the sysroot for the compiler to find. Otherwise, we're going to
// fail when building crates that need to generate code (e.g., build
// scripts and their dependencies).
builder.ensure(compile::Std::new(compiler, compiler.host));
builder.ensure(compile::Std::new(compiler, target));
} else {
builder.ensure(check::Std::new(target).build_kind(Some(Kind::Check)));
if !builder.download_rustc() {
if compiler.stage != 0 {
// If we're not in stage 0, then we won't have a std from the beta
// compiler around. That means we need to make sure there's one in
// the sysroot for the compiler to find. Otherwise, we're going to
// fail when building crates that need to generate code (e.g., build
// scripts and their dependencies).
builder.ensure(compile::Std::new(compiler, compiler.host));
builder.ensure(compile::Std::new(compiler, target));
} else {
builder.ensure(check::Std::new(target).build_kind(Some(Kind::Check)));
}
}
let mut cargo = builder::Cargo::new(
@ -286,7 +288,9 @@ macro_rules! lint_any {
let compiler = builder.compiler(builder.top_stage, builder.config.build);
let target = self.target;
builder.ensure(check::Rustc::new(target, builder).build_kind(Some(Kind::Check)));
if !builder.download_rustc() {
builder.ensure(check::Rustc::new(target, builder).build_kind(Some(Kind::Check)));
};
let cargo = prepare_tool_cargo(
builder,

View file

@ -147,14 +147,27 @@ impl Step for Std {
)]
fn run(self, builder: &Builder<'_>) {
let target = self.target;
let compiler = self.compiler;
// We already have std ready to be used for stage 0.
if self.compiler.stage == 0 {
let compiler = self.compiler;
builder.ensure(StdLink::from_std(self, compiler));
return;
}
let compiler = if builder.download_rustc() && self.force_recompile {
// When there are changes in the library tree with CI-rustc, we want to build
// the stageN library and that requires using stageN-1 compiler.
builder.compiler(self.compiler.stage.saturating_sub(1), builder.config.build)
} else {
self.compiler
};
// When using `download-rustc`, we already have artifacts for the host available. Don't
// recompile them.
if builder.download_rustc() && builder.config.is_host_target(target)
// NOTE: the beta compiler may generate different artifacts than the downloaded compiler, so
// its artifacts can't be reused.
&& compiler.stage != 0
if builder.download_rustc()
&& builder.config.is_host_target(target)
&& !self.force_recompile
{
let sysroot = builder.ensure(Sysroot { compiler, force_recompile: false });
@ -189,7 +202,13 @@ impl Step for Std {
let compiler_to_use = builder.compiler_for(compiler.stage, compiler.host, target);
trace!(?compiler_to_use);
if compiler_to_use != compiler {
if compiler_to_use != compiler
// Never uplift std unless we have compiled stage 1; if stage 1 is compiled,
// uplift it from there.
//
// FIXME: improve `fn compiler_for` to avoid adding stage condition here.
&& compiler.stage > 1
{
trace!(?compiler_to_use, ?compiler, "compiler != compiler_to_use, uplifting library");
builder.ensure(Std::new(compiler_to_use, target));
@ -222,27 +241,6 @@ impl Step for Std {
target_deps.extend(self.copy_extra_objects(builder, &compiler, target));
// The LLD wrappers and `rust-lld` are self-contained linking components that can be
// necessary to link the stdlib on some targets. We'll also need to copy these binaries to
// the `stage0-sysroot` to ensure the linker is found when bootstrapping on such a target.
if compiler.stage == 0 && builder.config.is_host_target(compiler.host) {
trace!(
"(build == host) copying linking components to `stage0-sysroot` for bootstrapping"
);
// We want to copy the host `bin` folder within the `rustlib` folder in the sysroot.
let src_sysroot_bin = builder
.rustc_snapshot_sysroot()
.join("lib")
.join("rustlib")
.join(compiler.host)
.join("bin");
if src_sysroot_bin.exists() {
let target_sysroot_bin = builder.sysroot_target_bindir(compiler, target);
t!(fs::create_dir_all(&target_sysroot_bin));
builder.cp_link_r(&src_sysroot_bin, &target_sysroot_bin);
}
}
// We build a sysroot for mir-opt tests using the same trick that Miri does: A check build
// with -Zalways-encode-mir. This frees us from the need to have a target linker, and the
// fact that this is a check build integrates nicely with run_cargo.
@ -628,18 +626,18 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, stage: u32, car
// Help the libc crate compile by assisting it in finding various
// sysroot native libraries.
if target.contains("musl") {
if let Some(p) = builder.musl_libdir(target) {
let root = format!("native={}", p.to_str().unwrap());
cargo.rustflag("-L").rustflag(&root);
}
if target.contains("musl")
&& let Some(p) = builder.musl_libdir(target)
{
let root = format!("native={}", p.to_str().unwrap());
cargo.rustflag("-L").rustflag(&root);
}
if target.contains("-wasi") {
if let Some(dir) = builder.wasi_libdir(target) {
let root = format!("native={}", dir.to_str().unwrap());
cargo.rustflag("-L").rustflag(&root);
}
if target.contains("-wasi")
&& let Some(dir) = builder.wasi_libdir(target)
{
let root = format!("native={}", dir.to_str().unwrap());
cargo.rustflag("-L").rustflag(&root);
}
}
@ -737,7 +735,7 @@ impl Step for StdLink {
let target = self.target;
// NOTE: intentionally does *not* check `target == builder.build` to avoid having to add the same check in `test::Crate`.
let (libdir, hostdir) = if self.force_recompile && builder.download_rustc() {
let (libdir, hostdir) = if !self.force_recompile && builder.download_rustc() {
// NOTE: copies part of `sysroot_libdir` to avoid having to add a new `force_recompile` argument there too
let lib = builder.sysroot_libdir_relative(self.compiler);
let sysroot = builder.ensure(crate::core::build_steps::compile::Sysroot {
@ -753,23 +751,16 @@ impl Step for StdLink {
(libdir, hostdir)
};
add_to_sysroot(
builder,
&libdir,
&hostdir,
&build_stamp::libstd_stamp(builder, compiler, target),
);
let is_downloaded_beta_stage0 = builder
.build
.config
.initial_rustc
.starts_with(builder.out.join(compiler.host).join("stage0/bin"));
// Special case for stage0, to make `rustup toolchain link` and `x dist --stage 0`
// work for stage0-sysroot. We only do this if the stage0 compiler comes from beta,
// and is not set to a custom path.
if compiler.stage == 0
&& builder
.build
.config
.initial_rustc
.starts_with(builder.out.join(compiler.host).join("stage0/bin"))
{
if compiler.stage == 0 && is_downloaded_beta_stage0 {
// Copy bin files from stage0/bin to stage0-sysroot/bin
let sysroot = builder.out.join(compiler.host).join("stage0-sysroot");
@ -779,21 +770,9 @@ impl Step for StdLink {
t!(fs::create_dir_all(&sysroot_bin_dir));
builder.cp_link_r(&stage0_bin_dir, &sysroot_bin_dir);
// Copy all files from stage0/lib to stage0-sysroot/lib
let stage0_lib_dir = builder.out.join(host).join("stage0/lib");
if let Ok(files) = fs::read_dir(stage0_lib_dir) {
for file in files {
let file = t!(file);
let path = file.path();
if path.is_file() {
builder.copy_link(
&path,
&sysroot.join("lib").join(path.file_name().unwrap()),
FileType::Regular,
);
}
}
}
t!(fs::create_dir_all(sysroot.join("lib")));
builder.cp_link_r(&stage0_lib_dir, &sysroot.join("lib"));
// Copy codegen-backends from stage0
let sysroot_codegen_backends = builder.sysroot_codegen_backends(compiler);
@ -807,6 +786,30 @@ impl Step for StdLink {
if stage0_codegen_backends.exists() {
builder.cp_link_r(&stage0_codegen_backends, &sysroot_codegen_backends);
}
} else if compiler.stage == 0 {
let sysroot = builder.out.join(compiler.host.triple).join("stage0-sysroot");
if builder.local_rebuild {
// On local rebuilds this path might be a symlink to the project root,
// which can be read-only (e.g., on CI). So remove it before copying
// the stage0 lib.
let _ = fs::remove_dir_all(sysroot.join("lib/rustlib/src/rust"));
}
builder.cp_link_r(&builder.initial_sysroot.join("lib"), &sysroot.join("lib"));
} else {
if builder.download_rustc() {
// Ensure there are no CI-rustc std artifacts.
let _ = fs::remove_dir_all(&libdir);
let _ = fs::remove_dir_all(&hostdir);
}
add_to_sysroot(
builder,
&libdir,
&hostdir,
&build_stamp::libstd_stamp(builder, compiler, target),
);
}
}
}
@ -1029,7 +1032,7 @@ impl Step for Rustc {
let compiler = self.compiler;
let target = self.target;
// NOTE: the ABI of the beta compiler is different from the ABI of the downloaded compiler,
// NOTE: the ABI of the stage0 compiler is different from the ABI of the downloaded compiler,
// so its artifacts can't be reused.
if builder.download_rustc() && compiler.stage != 0 {
trace!(stage = compiler.stage, "`download_rustc` requested");
@ -1388,12 +1391,13 @@ fn rustc_llvm_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelect
// found. This is to avoid the linker errors about undefined references to
// `__llvm_profile_instrument_memop` when linking `rustc_driver`.
let mut llvm_linker_flags = String::new();
if builder.config.llvm_profile_generate && target.is_msvc() {
if let Some(ref clang_cl_path) = builder.config.llvm_clang_cl {
// Add clang's runtime library directory to the search path
let clang_rt_dir = get_clang_cl_resource_dir(builder, clang_cl_path);
llvm_linker_flags.push_str(&format!("-L{}", clang_rt_dir.display()));
}
if builder.config.llvm_profile_generate
&& target.is_msvc()
&& let Some(ref clang_cl_path) = builder.config.llvm_clang_cl
{
// Add clang's runtime library directory to the search path
let clang_rt_dir = get_clang_cl_resource_dir(builder, clang_cl_path);
llvm_linker_flags.push_str(&format!("-L{}", clang_rt_dir.display()));
}
// The config can also specify its own llvm linker flags.
@ -1785,9 +1789,9 @@ impl Step for Sysroot {
t!(fs::create_dir_all(&sysroot));
// In some cases(see https://github.com/rust-lang/rust/issues/109314), when the stage0
// compiler relies on more recent version of LLVM than the beta compiler, it may not
// compiler relies on more recent version of LLVM than the stage0 compiler, it may not
// be able to locate the correct LLVM in the sysroot. This situation typically occurs
// when we upgrade LLVM version while the beta compiler continues to use an older version.
// when we upgrade LLVM version while the stage0 compiler continues to use an older version.
//
// Make sure to add the correct version of LLVM into the stage0 sysroot.
if compiler.stage == 0 {

View file

@ -2276,11 +2276,12 @@ impl Step for LlvmTools {
let target = self.target;
// Run only if a custom llvm-config is not used
if let Some(config) = builder.config.target_config.get(&target) {
if !builder.config.llvm_from_ci && config.llvm_config.is_some() {
builder.info(&format!("Skipping LlvmTools ({target}): external LLVM"));
return None;
}
if let Some(config) = builder.config.target_config.get(&target)
&& !builder.config.llvm_from_ci
&& config.llvm_config.is_some()
{
builder.info(&format!("Skipping LlvmTools ({target}): external LLVM"));
return None;
}
if !builder.config.dry_run() {
@ -2398,11 +2399,11 @@ impl Step for RustDev {
let target = self.target;
/* run only if llvm-config isn't used */
if let Some(config) = builder.config.target_config.get(&target) {
if let Some(ref _s) = config.llvm_config {
builder.info(&format!("Skipping RustDev ({target}): external LLVM"));
return None;
}
if let Some(config) = builder.config.target_config.get(&target)
&& let Some(ref _s) = config.llvm_config
{
builder.info(&format!("Skipping RustDev ({target}): external LLVM"));
return None;
}
if !builder.config.dry_run() {

View file

@ -318,10 +318,10 @@ pub fn format(build: &Builder<'_>, check: bool, all: bool, paths: &[PathBuf]) {
// `into_path` produces an absolute path. Try to strip `cwd` to get a shorter
// relative path.
let mut path = entry.clone().into_path();
if let Ok(cwd) = cwd {
if let Ok(path2) = path.strip_prefix(cwd) {
path = path2.to_path_buf();
}
if let Ok(cwd) = cwd
&& let Ok(path2) = path.strip_prefix(cwd)
{
path = path2.to_path_buf();
}
path.display().to_string()
});

View file

@ -107,18 +107,18 @@ pub fn prebuilt_llvm_config(
// If we're using a custom LLVM bail out here, but we can only use a
// custom LLVM for the build triple.
if let Some(config) = builder.config.target_config.get(&target) {
if let Some(ref s) = config.llvm_config {
check_llvm_version(builder, s);
let llvm_config = s.to_path_buf();
let mut llvm_cmake_dir = llvm_config.clone();
llvm_cmake_dir.pop();
llvm_cmake_dir.pop();
llvm_cmake_dir.push("lib");
llvm_cmake_dir.push("cmake");
llvm_cmake_dir.push("llvm");
return LlvmBuildStatus::AlreadyBuilt(LlvmResult { llvm_config, llvm_cmake_dir });
}
if let Some(config) = builder.config.target_config.get(&target)
&& let Some(ref s) = config.llvm_config
{
check_llvm_version(builder, s);
let llvm_config = s.to_path_buf();
let mut llvm_cmake_dir = llvm_config.clone();
llvm_cmake_dir.pop();
llvm_cmake_dir.pop();
llvm_cmake_dir.push("lib");
llvm_cmake_dir.push("cmake");
llvm_cmake_dir.push("llvm");
return LlvmBuildStatus::AlreadyBuilt(LlvmResult { llvm_config, llvm_cmake_dir });
}
if handle_submodule_when_needed {
@ -468,10 +468,10 @@ impl Step for Llvm {
cfg.define("LLVM_ENABLE_RUNTIMES", enabled_llvm_runtimes.join(";"));
}
if let Some(num_linkers) = builder.config.llvm_link_jobs {
if num_linkers > 0 {
cfg.define("LLVM_PARALLEL_LINK_JOBS", num_linkers.to_string());
}
if let Some(num_linkers) = builder.config.llvm_link_jobs
&& num_linkers > 0
{
cfg.define("LLVM_PARALLEL_LINK_JOBS", num_linkers.to_string());
}
// https://llvm.org/docs/HowToCrossCompileLLVM.html
@ -597,10 +597,10 @@ fn check_llvm_version(builder: &Builder<'_>, llvm_config: &Path) {
let version = get_llvm_version(builder, llvm_config);
let mut parts = version.split('.').take(2).filter_map(|s| s.parse::<u32>().ok());
if let (Some(major), Some(_minor)) = (parts.next(), parts.next()) {
if major >= 19 {
return;
}
if let (Some(major), Some(_minor)) = (parts.next(), parts.next())
&& major >= 19
{
return;
}
panic!("\n\nbad LLVM version: {version}, need >=19\n\n")
}
@ -730,11 +730,9 @@ fn configure_cmake(
// If ccache is configured we inform the build a little differently how
// to invoke ccache while also invoking our compilers.
if use_compiler_launcher {
if let Some(ref ccache) = builder.config.ccache {
cfg.define("CMAKE_C_COMPILER_LAUNCHER", ccache)
.define("CMAKE_CXX_COMPILER_LAUNCHER", ccache);
}
if use_compiler_launcher && let Some(ref ccache) = builder.config.ccache {
cfg.define("CMAKE_C_COMPILER_LAUNCHER", ccache)
.define("CMAKE_CXX_COMPILER_LAUNCHER", ccache);
}
cfg.define("CMAKE_C_COMPILER", sanitize_cc(&cc))
.define("CMAKE_CXX_COMPILER", sanitize_cc(&cxx))
@ -792,20 +790,20 @@ fn configure_cmake(
cxxflags.push(format!(" --target={target}"));
}
cfg.define("CMAKE_CXX_FLAGS", cxxflags);
if let Some(ar) = builder.ar(target) {
if ar.is_absolute() {
// LLVM build breaks if `CMAKE_AR` is a relative path, for some reason it
// tries to resolve this path in the LLVM build directory.
cfg.define("CMAKE_AR", sanitize_cc(&ar));
}
if let Some(ar) = builder.ar(target)
&& ar.is_absolute()
{
// LLVM build breaks if `CMAKE_AR` is a relative path, for some reason it
// tries to resolve this path in the LLVM build directory.
cfg.define("CMAKE_AR", sanitize_cc(&ar));
}
if let Some(ranlib) = builder.ranlib(target) {
if ranlib.is_absolute() {
// LLVM build breaks if `CMAKE_RANLIB` is a relative path, for some reason it
// tries to resolve this path in the LLVM build directory.
cfg.define("CMAKE_RANLIB", sanitize_cc(&ranlib));
}
if let Some(ranlib) = builder.ranlib(target)
&& ranlib.is_absolute()
{
// LLVM build breaks if `CMAKE_RANLIB` is a relative path, for some reason it
// tries to resolve this path in the LLVM build directory.
cfg.define("CMAKE_RANLIB", sanitize_cc(&ranlib));
}
if let Some(ref flags) = builder.config.llvm_ldflags {
@ -1038,13 +1036,14 @@ impl Step for Lld {
// when doing PGO on CI, cmake or clang-cl don't automatically link clang's
// profiler runtime in. In that case, we need to manually ask cmake to do it, to avoid
// linking errors, much like LLVM's cmake setup does in that situation.
if builder.config.llvm_profile_generate && target.is_msvc() {
if let Some(clang_cl_path) = builder.config.llvm_clang_cl.as_ref() {
// Find clang's runtime library directory and push that as a search path to the
// cmake linker flags.
let clang_rt_dir = get_clang_cl_resource_dir(builder, clang_cl_path);
ldflags.push_all(format!("/libpath:{}", clang_rt_dir.display()));
}
if builder.config.llvm_profile_generate
&& target.is_msvc()
&& let Some(clang_cl_path) = builder.config.llvm_clang_cl.as_ref()
{
// Find clang's runtime library directory and push that as a search path to the
// cmake linker flags.
let clang_rt_dir = get_clang_cl_resource_dir(builder, clang_cl_path);
ldflags.push_all(format!("/libpath:{}", clang_rt_dir.display()));
}
// LLD is built as an LLVM tool, but is distributed outside of the `llvm-tools` component,

View file

@ -154,10 +154,10 @@ Consider setting `rust.debuginfo-level = 1` in `bootstrap.toml`."#);
let compiler = builder.compiler(builder.top_stage, builder.config.build);
builder.ensure(Std::new(compiler, builder.config.build));
if let Some(opts) = args.cmd.shared_opts() {
if opts.profiles.contains(&Profile::Doc) {
builder.ensure(Rustdoc { compiler });
}
if let Some(opts) = args.cmd.shared_opts()
&& opts.profiles.contains(&Profile::Doc)
{
builder.ensure(Rustdoc { compiler });
}
let sysroot = builder.ensure(Sysroot::new(compiler));

View file

@ -241,10 +241,10 @@ impl Step for Link {
if run.builder.config.dry_run() {
return;
}
if let [cmd] = &run.paths[..] {
if cmd.assert_single_path().path.as_path().as_os_str() == "link" {
run.builder.ensure(Link);
}
if let [cmd] = &run.paths[..]
&& cmd.assert_single_path().path.as_path().as_os_str() == "link"
{
run.builder.ensure(Link);
}
}
fn run(self, builder: &Builder<'_>) -> Self::Output {
@ -457,10 +457,10 @@ impl Step for Hook {
}
fn make_run(run: RunConfig<'_>) {
if let [cmd] = &run.paths[..] {
if cmd.assert_single_path().path.as_path().as_os_str() == "hook" {
run.builder.ensure(Hook);
}
if let [cmd] = &run.paths[..]
&& cmd.assert_single_path().path.as_path().as_os_str() == "hook"
{
run.builder.ensure(Hook);
}
}
@ -672,10 +672,10 @@ impl Step for Editor {
if run.builder.config.dry_run() {
return;
}
if let [cmd] = &run.paths[..] {
if cmd.assert_single_path().path.as_path().as_os_str() == "editor" {
run.builder.ensure(Editor);
}
if let [cmd] = &run.paths[..]
&& cmd.assert_single_path().path.as_path().as_os_str() == "editor"
{
run.builder.ensure(Editor);
}
}

View file

@ -1576,7 +1576,7 @@ impl Step for Compiletest {
if builder.top_stage == 0 && env::var("COMPILETEST_FORCE_STAGE0").is_err() {
eprintln!("\
ERROR: `--stage 0` runs compiletest on the beta compiler, not your local changes, and will almost always cause tests to fail
ERROR: `--stage 0` runs compiletest on the stage0 (precompiled) compiler, not your local changes, and will almost always cause tests to fail
HELP: to test the compiler, use `--stage 1` instead
HELP: to test the standard library, use `--stage 0 library/std` instead
NOTE: if you're sure you want to do this, please open an issue as to why. In the meantime, you can override this with `COMPILETEST_FORCE_STAGE0=1`."
@ -1604,9 +1604,9 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
// NOTE: Only stage 1 is special cased because we need the rustc_private artifacts to match the
// running compiler in stage 2 when plugins run.
let (stage, stage_id) = if suite == "ui-fulldeps" && compiler.stage == 1 {
// At stage 0 (stage - 1) we are using the beta compiler. Using `self.target` can lead
// finding an incorrect compiler path on cross-targets, as the stage 0 beta compiler is
// always equal to `build.build` in the configuration.
// At stage 0 (stage - 1) we are using the stage0 compiler. Using `self.target` can lead
// finding an incorrect compiler path on cross-targets, as the stage 0 is always equal to
// `build.build` in the configuration.
let build = builder.build.build;
compiler = builder.compiler(compiler.stage - 1, build);
let test_stage = compiler.stage + 1;
@ -1692,7 +1692,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
}
if mode == "rustdoc-json" {
// Use the beta compiler for jsondocck
// Use the stage0 compiler for jsondocck
let json_compiler = compiler.with_stage(0);
cmd.arg("--jsondocck-path")
.arg(builder.ensure(tool::JsonDocCk { compiler: json_compiler, target }).tool_path);
@ -2417,10 +2417,10 @@ impl Step for ErrorIndex {
}
fn markdown_test(builder: &Builder<'_>, compiler: Compiler, markdown: &Path) -> bool {
if let Ok(contents) = fs::read_to_string(markdown) {
if !contents.contains("```") {
return true;
}
if let Ok(contents) = fs::read_to_string(markdown)
&& !contents.contains("```")
{
return true;
}
builder.verbose(|| println!("doc tests for: {}", markdown.display()));

View file

@ -329,9 +329,9 @@ pub(crate) fn get_tool_rustc_compiler(
return target_compiler;
}
if builder.download_rustc() && target_compiler.stage > 0 {
// We already have the stage N compiler, we don't need to cut the stage.
return builder.compiler(target_compiler.stage, builder.config.build);
if builder.download_rustc() && target_compiler.stage == 1 {
// We shouldn't drop to stage0 compiler when using CI rustc.
return builder.compiler(1, builder.config.build);
}
// Similar to `compile::Assemble`, build with the previous stage's compiler. Otherwise

View file

@ -988,15 +988,15 @@ impl Builder<'_> {
// requirement, but the `-L` library path is not propagated across
// separate Cargo projects. We can add LLVM's library path to the
// rustc args as a workaround.
if mode == Mode::ToolRustc || mode == Mode::Codegen {
if let Some(llvm_config) = self.llvm_config(target) {
let llvm_libdir =
command(llvm_config).arg("--libdir").run_capture_stdout(self).stdout();
if target.is_msvc() {
rustflags.arg(&format!("-Clink-arg=-LIBPATH:{llvm_libdir}"));
} else {
rustflags.arg(&format!("-Clink-arg=-L{llvm_libdir}"));
}
if (mode == Mode::ToolRustc || mode == Mode::Codegen)
&& let Some(llvm_config) = self.llvm_config(target)
{
let llvm_libdir =
command(llvm_config).arg("--libdir").run_capture_stdout(self).stdout();
if target.is_msvc() {
rustflags.arg(&format!("-Clink-arg=-LIBPATH:{llvm_libdir}"));
} else {
rustflags.arg(&format!("-Clink-arg=-L{llvm_libdir}"));
}
}
@ -1231,12 +1231,11 @@ impl Builder<'_> {
_ => None,
};
if let Some(limit) = limit {
if stage == 0
|| self.config.default_codegen_backend(target).unwrap_or_default() == "llvm"
{
rustflags.arg(&format!("-Cllvm-args=-import-instr-limit={limit}"));
}
if let Some(limit) = limit
&& (stage == 0
|| self.config.default_codegen_backend(target).unwrap_or_default() == "llvm")
{
rustflags.arg(&format!("-Cllvm-args=-import-instr-limit={limit}"));
}
}

View file

@ -237,7 +237,7 @@ fn alias_and_path_for_library() {
);
assert_eq!(
first(cache.all::<doc::Std>()),
&[doc_std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0)]
&[doc_std!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1)]
);
}
@ -254,19 +254,6 @@ fn ci_rustc_if_unchanged_invalidate_on_compiler_changes() {
});
}
#[test]
fn ci_rustc_if_unchanged_invalidate_on_library_changes_in_ci() {
git_test(|ctx| {
prepare_rustc_checkout(ctx);
ctx.create_upstream_merge(&["compiler/bar"]);
// This change should invalidate download-ci-rustc
ctx.create_nonupstream_merge(&["library/foo"]);
let config = parse_config_download_rustc_at(ctx.get_path(), "if-unchanged", true);
assert_eq!(config.download_rustc_commit, None);
});
}
#[test]
fn ci_rustc_if_unchanged_do_not_invalidate_on_library_changes_outside_ci() {
git_test(|ctx| {
@ -433,14 +420,14 @@ mod defaults {
assert_eq!(first(cache.all::<doc::ErrorIndex>()), &[doc::ErrorIndex { target: a },]);
assert_eq!(
first(cache.all::<tool::ErrorIndex>()),
&[tool::ErrorIndex { compiler: Compiler::new(0, a) }]
&[tool::ErrorIndex { compiler: Compiler::new(1, a) }]
);
// docs should be built with the beta compiler, not with the stage0 artifacts.
// docs should be built with the stage0 compiler, not with the stage0 artifacts.
// recall that rustdoc is off-by-one: `stage` is the compiler rustdoc is _linked_ to,
// not the one it was built by.
assert_eq!(
first(cache.all::<tool::Rustdoc>()),
&[tool::Rustdoc { compiler: Compiler::new(0, a) },]
&[tool::Rustdoc { compiler: Compiler::new(1, a) },]
);
}
}

View file

@ -45,6 +45,7 @@ use crate::utils::helpers::{self, exe, output, t};
/// final output/compiler, which can be significantly affected by changes made to the bootstrap sources.
#[rustfmt::skip] // We don't want rustfmt to oneline this list
pub(crate) const RUSTC_IF_UNCHANGED_ALLOWED_PATHS: &[&str] = &[
":!library",
":!src/tools",
":!src/librustdoc",
":!src/rustdoc-json-types",
@ -1699,20 +1700,20 @@ impl Config {
};
// We want to be able to set string values without quotes,
// like in `configure.py`. Try adding quotes around the right hand side
if let Some((key, value)) = option.split_once('=') {
if !value.contains('"') {
match get_table(&format!(r#"{key}="{value}""#)) {
Ok(v) => {
override_toml.merge(
None,
&mut Default::default(),
v,
ReplaceOpt::ErrorOnDuplicate,
);
continue;
}
Err(e) => err = e,
if let Some((key, value)) = option.split_once('=')
&& !value.contains('"')
{
match get_table(&format!(r#"{key}="{value}""#)) {
Ok(v) => {
override_toml.merge(
None,
&mut Default::default(),
v,
ReplaceOpt::ErrorOnDuplicate,
);
continue;
}
Err(e) => err = e,
}
}
eprintln!("failed to parse override `{option}`: `{err}");
@ -2056,16 +2057,15 @@ impl Config {
|| (matches!(debug_toml, Some(true))
&& !matches!(rustc_debug_assertions_toml, Some(false)));
if debug_assertions_requested {
if let Some(ref opt) = download_rustc {
if opt.is_string_or_true() {
eprintln!(
"WARN: currently no CI rustc builds have rustc debug assertions \
if debug_assertions_requested
&& let Some(ref opt) = download_rustc
&& opt.is_string_or_true()
{
eprintln!(
"WARN: currently no CI rustc builds have rustc debug assertions \
enabled. Please either set `rust.debug-assertions` to `false` if you \
want to use download CI rustc or set `rust.download-rustc` to `false`."
);
}
}
);
}
config.download_rustc_commit = config.download_ci_rustc_commit(
@ -2176,19 +2176,17 @@ impl Config {
// We need to override `rust.channel` if it's manually specified when using the CI rustc.
// This is because if the compiler uses a different channel than the one specified in bootstrap.toml,
// tests may fail due to using a different channel than the one used by the compiler during tests.
if let Some(commit) = &config.download_rustc_commit {
if is_user_configured_rust_channel {
println!(
"WARNING: `rust.download-rustc` is enabled. The `rust.channel` option will be overridden by the CI rustc's channel."
);
if let Some(commit) = &config.download_rustc_commit
&& is_user_configured_rust_channel
{
println!(
"WARNING: `rust.download-rustc` is enabled. The `rust.channel` option will be overridden by the CI rustc's channel."
);
let channel = config
.read_file_by_commit(Path::new("src/ci/channel"), commit)
.trim()
.to_owned();
let channel =
config.read_file_by_commit(Path::new("src/ci/channel"), commit).trim().to_owned();
config.channel = channel;
}
config.channel = channel;
}
if let Some(llvm) = toml.llvm {
@ -2533,10 +2531,12 @@ impl Config {
|| bench_stage.is_some();
// See https://github.com/rust-lang/compiler-team/issues/326
config.stage = match config.cmd {
Subcommand::Check { .. } => flags.stage.or(check_stage).unwrap_or(0),
Subcommand::Check { .. } | Subcommand::Clippy { .. } | Subcommand::Fix => {
flags.stage.or(check_stage).unwrap_or(1)
}
// `download-rustc` only has a speed-up for stage2 builds. Default to stage2 unless explicitly overridden.
Subcommand::Doc { .. } => {
flags.stage.or(doc_stage).unwrap_or(if download_rustc { 2 } else { 0 })
flags.stage.or(doc_stage).unwrap_or(if download_rustc { 2 } else { 1 })
}
Subcommand::Build => {
flags.stage.or(build_stage).unwrap_or(if download_rustc { 2 } else { 1 })
@ -2551,8 +2551,6 @@ impl Config {
// These are all bootstrap tools, which don't depend on the compiler.
// The stage we pass shouldn't matter, but use 0 just in case.
Subcommand::Clean { .. }
| Subcommand::Clippy { .. }
| Subcommand::Fix
| Subcommand::Run { .. }
| Subcommand::Setup { .. }
| Subcommand::Format { .. }
@ -2698,10 +2696,10 @@ impl Config {
let bindir = &self.bindir;
if bindir.is_absolute() {
// Try to make it relative to the prefix.
if let Some(prefix) = &self.prefix {
if let Ok(stripped) = bindir.strip_prefix(prefix) {
return stripped;
}
if let Some(prefix) = &self.prefix
&& let Ok(stripped) = bindir.strip_prefix(prefix)
{
return stripped;
}
}
bindir
@ -3150,24 +3148,10 @@ impl Config {
}
};
// RUSTC_IF_UNCHANGED_ALLOWED_PATHS
let mut allowed_paths = RUSTC_IF_UNCHANGED_ALLOWED_PATHS.to_vec();
// In CI, disable ci-rustc if there are changes in the library tree. But for non-CI, allow
// these changes to speed up the build process for library developers. This provides consistent
// functionality for library developers between `download-rustc=true` and `download-rustc="if-unchanged"`
// options.
//
// If you update "library" logic here, update `builder::tests::ci_rustc_if_unchanged_logic` test
// logic accordingly.
if !self.is_running_on_ci {
allowed_paths.push(":!library");
}
let commit = if self.rust_info.is_managed_git_subrepository() {
// Look for a version to compare to based on the current commit.
// Only commits merged by bors will have CI artifacts.
let freshness = self.check_path_modifications(&allowed_paths);
let freshness = self.check_path_modifications(RUSTC_IF_UNCHANGED_ALLOWED_PATHS);
self.verbose(|| {
eprintln!("rustc freshness: {freshness:?}");
});
@ -3493,19 +3477,19 @@ fn check_incompatible_options_for_ci_rustc(
// We always build the in-tree compiler on cross targets, so we only care
// about the host target here.
let host_str = host.to_string();
if let Some(current_cfg) = current_config_toml.target.as_ref().and_then(|c| c.get(&host_str)) {
if current_cfg.profiler.is_some() {
let ci_target_toml = ci_config_toml.target.as_ref().and_then(|c| c.get(&host_str));
let ci_cfg = ci_target_toml.ok_or(format!(
"Target specific config for '{host_str}' is not present for CI-rustc"
))?;
if let Some(current_cfg) = current_config_toml.target.as_ref().and_then(|c| c.get(&host_str))
&& current_cfg.profiler.is_some()
{
let ci_target_toml = ci_config_toml.target.as_ref().and_then(|c| c.get(&host_str));
let ci_cfg = ci_target_toml.ok_or(format!(
"Target specific config for '{host_str}' is not present for CI-rustc"
))?;
let profiler = &ci_cfg.profiler;
err!(current_cfg.profiler, profiler, "build");
let profiler = &ci_cfg.profiler;
err!(current_cfg.profiler, profiler, "build");
let optimized_compiler_builtins = &ci_cfg.optimized_compiler_builtins;
err!(current_cfg.optimized_compiler_builtins, optimized_compiler_builtins, "build");
}
let optimized_compiler_builtins = &ci_cfg.optimized_compiler_builtins;
err!(current_cfg.optimized_compiler_builtins, optimized_compiler_builtins, "build");
}
let (Some(current_rust_config), Some(ci_rust_config)) =

View file

@ -666,7 +666,7 @@ impl Config {
}
};
// For the beta compiler, put special effort into ensuring the checksums are valid.
// For the stage0 compiler, put special effort into ensuring the checksums are valid.
let checksum = if should_verify {
let error = format!(
"src/stage0 doesn't contain a checksum for {url}. \
@ -709,10 +709,10 @@ download-rustc = false
";
}
self.download_file(&format!("{base_url}/{url}"), &tarball, help_on_error);
if let Some(sha256) = checksum {
if !self.verify(&tarball, sha256) {
panic!("failed to verify {}", tarball.display());
}
if let Some(sha256) = checksum
&& !self.verify(&tarball, sha256)
{
panic!("failed to verify {}", tarball.display());
}
self.unpack(&tarball, &bin_root, prefix);

View file

@ -1451,23 +1451,23 @@ Executed at: {executed_at}"#,
// Look for Wasmtime, and for its default options be sure to disable
// its caching system since we're executing quite a lot of tests and
// ideally shouldn't pollute the cache too much.
if let Some(path) = finder.maybe_have("wasmtime") {
if let Ok(mut path) = path.into_os_string().into_string() {
path.push_str(" run -C cache=n --dir .");
// Make sure that tests have access to RUSTC_BOOTSTRAP. This (for example) is
// required for libtest to work on beta/stable channels.
//
// NB: with Wasmtime 20 this can change to `-S inherit-env` to
// inherit the entire environment rather than just this single
// environment variable.
path.push_str(" --env RUSTC_BOOTSTRAP");
if let Some(path) = finder.maybe_have("wasmtime")
&& let Ok(mut path) = path.into_os_string().into_string()
{
path.push_str(" run -C cache=n --dir .");
// Make sure that tests have access to RUSTC_BOOTSTRAP. This (for example) is
// required for libtest to work on beta/stable channels.
//
// NB: with Wasmtime 20 this can change to `-S inherit-env` to
// inherit the entire environment rather than just this single
// environment variable.
path.push_str(" --env RUSTC_BOOTSTRAP");
if target.contains("wasip2") {
path.push_str(" --wasi inherit-network --wasi allow-ip-name-lookup");
}
return Some(path);
if target.contains("wasip2") {
path.push_str(" --wasi inherit-network --wasi allow-ip-name-lookup");
}
return Some(path);
}
None
@ -1637,12 +1637,12 @@ Executed at: {executed_at}"#,
/// sha, version, etc.
fn rust_version(&self) -> String {
let mut version = self.rust_info().version(self, &self.version);
if let Some(ref s) = self.config.description {
if !s.is_empty() {
version.push_str(" (");
version.push_str(s);
version.push(')');
}
if let Some(ref s) = self.config.description
&& !s.is_empty()
{
version.push_str(" (");
version.push_str(s);
version.push(')');
}
version
}
@ -1760,14 +1760,14 @@ Executed at: {executed_at}"#,
pub fn copy_link(&self, src: &Path, dst: &Path, file_type: FileType) {
self.copy_link_internal(src, dst, false);
if file_type.could_have_split_debuginfo() {
if let Some(dbg_file) = split_debuginfo(src) {
self.copy_link_internal(
&dbg_file,
&dst.with_extension(dbg_file.extension().unwrap()),
false,
);
}
if file_type.could_have_split_debuginfo()
&& let Some(dbg_file) = split_debuginfo(src)
{
self.copy_link_internal(
&dbg_file,
&dst.with_extension(dbg_file.extension().unwrap()),
false,
);
}
}
@ -1779,13 +1779,14 @@ Executed at: {executed_at}"#,
if src == dst {
return;
}
if let Err(e) = fs::remove_file(dst) {
if cfg!(windows) && e.kind() != io::ErrorKind::NotFound {
// workaround for https://github.com/rust-lang/rust/issues/127126
// if removing the file fails, attempt to rename it instead.
let now = t!(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH));
let _ = fs::rename(dst, format!("{}-{}", dst.display(), now.as_nanos()));
}
if let Err(e) = fs::remove_file(dst)
&& cfg!(windows)
&& e.kind() != io::ErrorKind::NotFound
{
// workaround for https://github.com/rust-lang/rust/issues/127126
// if removing the file fails, attempt to rename it instead.
let now = t!(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH));
let _ = fs::rename(dst, format!("{}-{}", dst.display(), now.as_nanos()));
}
let metadata = t!(src.symlink_metadata(), format!("src = {}", src.display()));
let mut src = src.to_path_buf();
@ -1894,10 +1895,10 @@ Executed at: {executed_at}"#,
chmod(&dst, file_type.perms());
// If this file can have debuginfo, look for split debuginfo and install it too.
if file_type.could_have_split_debuginfo() {
if let Some(dbg_file) = split_debuginfo(src) {
self.install(&dbg_file, dstdir, FileType::Regular);
}
if file_type.could_have_split_debuginfo()
&& let Some(dbg_file) = split_debuginfo(src)
{
self.install(&dbg_file, dstdir, FileType::Regular);
}
}

View file

@ -46,10 +46,10 @@ pub fn find_recent_config_change_ids(current_id: usize) -> &'static [ChangeInfo]
// an empty list (it may be due to switching from a recent branch to an
// older one); otherwise, return the full list (assuming the user provided
// the incorrect change-id by accident).
if let Some(config) = CONFIG_CHANGE_HISTORY.iter().max_by_key(|config| config.change_id) {
if current_id > config.change_id {
return &[];
}
if let Some(config) = CONFIG_CHANGE_HISTORY.iter().max_by_key(|config| config.change_id)
&& current_id > config.change_id
{
return &[];
}
CONFIG_CHANGE_HISTORY
@ -411,4 +411,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
severity: ChangeSeverity::Info,
summary: "`./x run` now supports running in-tree `rustfmt`, e.g., `./x run rustfmt -- --check /path/to/file.rs`.",
},
ChangeInfo {
change_id: 119899,
severity: ChangeSeverity::Warning,
summary: "Stage0 library no longer matches the in-tree library, which means stage1 compiler now uses the beta library.",
},
];

View file

@ -13,7 +13,7 @@ use crate::utils::load_env_var;
#[derive(serde::Deserialize, Debug, Clone)]
#[serde(deny_unknown_fields)]
pub struct Job {
/// Name of the job, e.g. mingw-check
/// Name of the job, e.g. mingw-check-1
pub name: String,
/// GitHub runner on which the job should be executed
pub os: String,

View file

@ -40,7 +40,7 @@ try-job: dist-i686-msvc"#,
fn pr_jobs() {
let stdout = get_matrix("pull_request", "commit", "refs/heads/pr/1234");
insta::assert_snapshot!(stdout, @r#"
jobs=[{"name":"mingw-check","full_name":"PR - mingw-check","os":"ubuntu-24.04","env":{"PR_CI_JOB":1},"free_disk":true},{"name":"mingw-check-tidy","full_name":"PR - mingw-check-tidy","os":"ubuntu-24.04","env":{"PR_CI_JOB":1},"continue_on_error":true,"free_disk":true,"doc_url":"https://foo.bar"}]
jobs=[{"name":"mingw-check-1","full_name":"PR - mingw-check-1","os":"ubuntu-24.04","env":{"PR_CI_JOB":1},"free_disk":true},{"name":"mingw-check-2","full_name":"PR - mingw-check-2","os":"ubuntu-24.04","env":{"PR_CI_JOB":1},"free_disk":true},{"name":"mingw-check-tidy","full_name":"PR - mingw-check-tidy","os":"ubuntu-24.04","env":{"PR_CI_JOB":1},"continue_on_error":true,"free_disk":true,"doc_url":"https://foo.bar"}]
run_type=pr
"#);
}
@ -51,6 +51,8 @@ fn get_matrix(event_name: &str, commit_msg: &str, branch_ref: &str) -> String {
.env("GITHUB_EVENT_NAME", event_name)
.env("COMMIT_MESSAGE", commit_msg)
.env("GITHUB_REF", branch_ref)
.env("GITHUB_RUN_ID", "123")
.env("GITHUB_RUN_ATTEMPT", "1")
.stdout(Stdio::piped())
.output()
.expect("Failed to execute command");

View file

@ -64,7 +64,9 @@ envs:
# These jobs automatically inherit envs.pr, to avoid repeating
# it in each job definition.
pr:
- name: mingw-check
- name: mingw-check-1
<<: *job-linux-4c
- name: mingw-check-2
<<: *job-linux-4c
- name: mingw-check-tidy
continue_on_error: true

View file

@ -34,31 +34,23 @@ RUN npm install es-check@6.1.1 eslint@8.6.0 typescript@5.7.3 -g
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
COPY host-x86_64/mingw-check/reuse-requirements.txt /tmp/
COPY host-x86_64/mingw-check-1/reuse-requirements.txt /tmp/
RUN pip3 install --no-deps --no-cache-dir --require-hashes -r /tmp/reuse-requirements.txt
COPY host-x86_64/mingw-check/check-default-config-profiles.sh /scripts/
COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/
COPY host-x86_64/mingw-check/validate-error-codes.sh /scripts/
COPY host-x86_64/mingw-check-1/check-default-config-profiles.sh /scripts/
COPY host-x86_64/mingw-check-1/validate-toolstate.sh /scripts/
COPY host-x86_64/mingw-check-1/validate-error-codes.sh /scripts/
# Check library crates on all tier 1 targets.
# We disable optimized compiler built-ins because that requires a C toolchain for the target.
# We also skip the x86_64-unknown-linux-gnu target as it is well-tested by other jobs.
ENV SCRIPT \
python3 ../x.py check --stage 0 --set build.optimized-compiler-builtins=false core alloc std --target=aarch64-unknown-linux-gnu,i686-pc-windows-msvc,i686-unknown-linux-gnu,x86_64-apple-darwin,x86_64-pc-windows-gnu,x86_64-pc-windows-msvc && \
/scripts/check-default-config-profiles.sh && \
python3 ../x.py check compiletest --set build.compiletest-use-stage0-libtest=true && \
python3 ../x.py check --target=x86_64-pc-windows-gnu --host=x86_64-pc-windows-gnu && \
python3 ../x.py clippy ci && \
python3 ../x.py build --stage 0 src/tools/build-manifest && \
python3 ../x.py test --stage 0 src/tools/compiletest && \
python3 ../x.py test --stage 0 core alloc std test proc_macro && \
# Build both public and internal documentation.
RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc --stage 0 library && \
mkdir -p /checkout/obj/staging/doc && \
cp -r build/x86_64-unknown-linux-gnu/doc /checkout/obj/staging && \
RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc --stage 0 compiler && \
RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc --stage 0 library/test && \
python3 ../x.py check compiletest --set build.compiletest-use-stage0-libtest=true && \
python3 ../x.py check --stage 1 --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \
python3 ../x.py check --stage 1 --set build.optimized-compiler-builtins=false core alloc std --target=aarch64-unknown-linux-gnu,i686-pc-windows-msvc,i686-unknown-linux-gnu,x86_64-apple-darwin,x86_64-pc-windows-gnu,x86_64-pc-windows-msvc && \
/scripts/validate-toolstate.sh && \
/scripts/validate-error-codes.sh && \
reuse --include-submodules lint && \

View file

@ -0,0 +1,37 @@
FROM ubuntu:22.04
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ \
make \
ninja-build \
file \
curl \
ca-certificates \
python3 \
python3-pip \
python3-pkg-resources \
git \
cmake \
sudo \
gdb \
xz-utils \
libssl-dev \
pkg-config \
mingw-w64 \
&& rm -rf /var/lib/apt/lists/*
ENV RUST_CONFIGURE_ARGS="--set rust.validate-mir-opts=3"
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
ENV SCRIPT \
python3 ../x.py clippy ci && \
python3 ../x.py test --stage 1 core alloc std test proc_macro && \
# Build both public and internal documentation.
RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc --stage 0 compiler && \
RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc --stage 1 library && \
mkdir -p /checkout/obj/staging/doc && \
cp -r build/x86_64-unknown-linux-gnu/doc /checkout/obj/staging && \
RUSTDOCFLAGS=\"--document-private-items --document-hidden-items\" python3 ../x.py doc --stage 1 library/test

View file

@ -34,12 +34,12 @@ COPY host-x86_64/mingw-check-tidy/eslint.version /tmp/
COPY scripts/sccache.sh /scripts/
RUN sh /scripts/sccache.sh
COPY host-x86_64/mingw-check/reuse-requirements.txt /tmp/
COPY host-x86_64/mingw-check-1/reuse-requirements.txt /tmp/
RUN pip3 install --no-deps --no-cache-dir --require-hashes -r /tmp/reuse-requirements.txt \
&& pip3 install virtualenv
COPY host-x86_64/mingw-check/validate-toolstate.sh /scripts/
COPY host-x86_64/mingw-check/validate-error-codes.sh /scripts/
COPY host-x86_64/mingw-check-1/validate-toolstate.sh /scripts/
COPY host-x86_64/mingw-check-1/validate-error-codes.sh /scripts/
# NOTE: intentionally uses python2 for x.py so we can test it still works.
# validate-toolstate only runs in our CI, so it's ok for it to only support python3.

View file

@ -29,5 +29,5 @@ RUN echo "optimize = false" >> /config/nopt-std-config.toml
ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu \
--disable-optimize-tests \
--set rust.test-compare-mode
ENV SCRIPT python3 ../x.py test --stage 0 --config /config/nopt-std-config.toml library/std \
ENV SCRIPT python3 ../x.py test --stage 1 --config /config/nopt-std-config.toml library/std \
&& python3 ../x.py --stage 2 test

View file

@ -111,7 +111,9 @@ envs:
# These jobs automatically inherit envs.pr, to avoid repeating
# it in each job definition.
pr:
- name: mingw-check
- name: mingw-check-1
<<: *job-linux-4c
- name: mingw-check-2
<<: *job-linux-4c
- name: mingw-check-tidy
continue_on_error: true
@ -125,7 +127,7 @@ pr:
env:
IMAGE: aarch64-gnu-llvm-19
DOCKER_SCRIPT: stage_2_test_set1.sh
<<: *job-aarch64-linux-8c
<<: *job-aarch64-linux
- name: aarch64-gnu-llvm-19-2
env:
IMAGE: aarch64-gnu-llvm-19
@ -281,11 +283,14 @@ auto:
env:
IMAGE: i686-gnu-nopt
DOCKER_SCRIPT: >-
python3 ../x.py test --stage 0 --config /config/nopt-std-config.toml library/std &&
python3 ../x.py test --stage 1 --config /config/nopt-std-config.toml library/std &&
/scripts/stage_2_test_set2.sh
<<: *job-linux-4c
- name: mingw-check
- name: mingw-check-1
<<: *job-linux-4c
- name: mingw-check-2
<<: *job-linux-4c
- name: test-various

View file

@ -1 +1 @@
99e7c15e81385b38a8186b51edc4577d5d7b5bdd
c68032fd4c442d275f4daa571ba19c076106b490

View file

@ -45,13 +45,13 @@ compiler.
```mermaid
graph TD
s0c["stage0 compiler (1.63)"]:::downloaded -->|A| s0l("stage0 std (1.64)"):::with-s0c;
s0c["stage0 compiler (1.86.0-beta.1)"]:::downloaded -->|A| s0l("stage0 std (1.86.0-beta.1)"):::downloaded;
s0c & s0l --- stepb[ ]:::empty;
stepb -->|B| s0ca["stage0 compiler artifacts (1.64)"]:::with-s0c;
s0ca -->|copy| s1c["stage1 compiler (1.64)"]:::with-s0c;
s1c -->|C| s1l("stage1 std (1.64)"):::with-s1c;
stepb -->|B| s0ca["stage0 compiler artifacts (1.87.0-dev)"]:::with-s0c;
s0ca -->|copy| s1c["stage1 compiler (1.87.0-dev)"]:::with-s0c;
s1c -->|C| s1l("stage1 std (1.87.0-dev)"):::with-s1c;
s1c & s1l --- stepd[ ]:::empty;
stepd -->|D| s1ca["stage1 compiler artifacts (1.64)"]:::with-s1c;
stepd -->|D| s1ca["stage1 compiler artifacts (1.87.0-dev)"]:::with-s1c;
s1ca -->|copy| s2c["stage2 compiler"]:::with-s1c;
classDef empty width:0px,height:0px;
@ -62,19 +62,21 @@ graph TD
### Stage 0: the pre-compiled compiler
The stage0 compiler is usually the current _beta_ `rustc` compiler and its
The stage0 compiler is by default the very recent _beta_ `rustc` compiler and its
associated dynamic libraries, which `./x.py` will download for you. (You can
also configure `./x.py` to use something else.)
also configure `./x.py` to change stage0 to something else.)
The stage0 compiler is then used only to compile [`src/bootstrap`],
[`library/std`], and [`compiler/rustc`]. When assembling the libraries and
binaries that will become the stage1 `rustc` compiler, the freshly compiled
`std` and `rustc` are used. There are two concepts at play here: a compiler
(with its set of dependencies) and its 'target' or 'object' libraries (`std` and
`rustc`). Both are staged, but in a staggered manner.
The precompiled stage0 compiler is then used only to compile [`src/bootstrap`] and [`compiler/rustc`]
with precompiled stage0 std.
Note that to build the stage1 compiler we use the precompiled stage0 compiler and std.
Therefore, to use a compiler with a std that is freshly built from the tree, you need to
build the stage2 compiler.
There are two concepts at play here: a compiler (with its set of dependencies) and its
'target' or 'object' libraries (`std` and `rustc`). Both are staged, but in a staggered manner.
[`compiler/rustc`]: https://github.com/rust-lang/rust/tree/master/compiler/rustc
[`library/std`]: https://github.com/rust-lang/rust/tree/master/library/std
[`src/bootstrap`]: https://github.com/rust-lang/rust/tree/master/src/bootstrap
### Stage 1: from current code, by an earlier compiler
@ -84,16 +86,14 @@ The rustc source code is then compiled with the `stage0` compiler to produce the
### Stage 2: the truly current compiler
We then rebuild our `stage1` compiler with itself to produce the `stage2`
We then rebuild the compiler using `stage1` compiler with in-tree std to produce the `stage2`
compiler.
In theory, the `stage1` compiler is functionally identical to the `stage2`
compiler, but in practice there are subtle differences. In particular, the
`stage1` compiler itself was built by `stage0` and hence not by the source in
your working directory. This means that the ABI generated by the `stage0`
compiler may not match the ABI that would have been made by the `stage1`
compiler, which can cause problems for dynamic libraries, tests, and tools using
`rustc_private`.
The `stage1` compiler itself was built by precompiled `stage0` compiler and std
and hence not by the source in your working directory. This means that the ABI
generated by the `stage0` compiler may not match the ABI that would have been made
by the `stage1` compiler, which can cause problems for dynamic libraries, tests
and tools using `rustc_private`.
Note that the `proc_macro` crate avoids this issue with a `C` FFI layer called
`proc_macro::bridge`, allowing it to be used with `stage1`.
@ -101,9 +101,10 @@ Note that the `proc_macro` crate avoids this issue with a `C` FFI layer called
The `stage2` compiler is the one distributed with `rustup` and all other install
methods. However, it takes a very long time to build because one must first
build the new compiler with an older compiler and then use that to build the new
compiler with itself. For development, you usually only want the `stage1`
compiler, which you can build with `./x build library`. See [Building the
compiler](../how-to-build-and-run.html#building-the-compiler).
compiler with itself.
For development, you usually only want to use `--stage 1` flag to build things.
See [Building the compiler](../how-to-build-and-run.html#building-the-compiler).
### Stage 3: the same-result test
@ -114,10 +115,11 @@ something has broken.
### Building the stages
The script [`./x`] tries to be helpful and pick the stage you most likely meant
for each subcommand. These defaults are as follows:
for each subcommand. Here are some `x` commands with their default stages:
- `check`: `--stage 0`
- `doc`: `--stage 0`
- `check`: `--stage 1`
- `clippy`: `--stage 1`
- `doc`: `--stage 1`
- `build`: `--stage 1`
- `test`: `--stage 1`
- `dist`: `--stage 2`
@ -191,8 +193,8 @@ include, but are not limited to:
without building `rustc` from source ('build with `stage0`, then test the
artifacts'). If you're working on the standard library, this is normally the
test command you want.
- `./x build --stage 0` means to build with the beta `rustc`.
- `./x doc --stage 0` means to document using the beta `rustdoc`.
- `./x build --stage 0` means to build with the stage0 `rustc`.
- `./x doc --stage 0` means to document using the stage0 `rustdoc`.
#### Examples of what *not* to do
@ -208,9 +210,6 @@ include, but are not limited to:
### Building vs. running
Note that `build --stage N compiler/rustc` **does not** build the stage N
compiler: instead it builds the stage N+1 compiler _using_ the stage N compiler.
In short, _stage 0 uses the `stage0` compiler to create `stage0` artifacts which
will later be uplifted to be the stage1 compiler_.
@ -268,23 +267,6 @@ However, when cross-compiling, `stage1` `std` will only run on the host. So the
(See in the table how `stage2` only builds non-host `std` targets).
### Why does only libstd use `cfg(bootstrap)`?
For docs on `cfg(bootstrap)` itself, see [Complications of
Bootstrapping](#complications-of-bootstrapping).
The `rustc` generated by the `stage0` compiler is linked to the freshly-built
`std`, which means that for the most part only `std` needs to be `cfg`-gated, so
that `rustc` can use features added to `std` immediately after their addition,
without need for them to get into the downloaded `beta` compiler.
Note this is different from any other Rust program: `stage1` `rustc` is built by
the _beta_ compiler, but using the _master_ version of `libstd`!
The only time `rustc` uses `cfg(bootstrap)` is when it adds internal lints that
use diagnostic items, or when it uses unstable library features that were
recently changed.
### What is a 'sysroot'?
When you build a project with `cargo`, the build artifacts for dependencies are
@ -459,7 +441,6 @@ compiler itself uses to run. These aren't actually used by artifacts the new
compiler generates. This step also copies the `rustc` and `rustdoc` binaries we
generated into `build/$HOST/stage/bin`.
The `stage1/bin/rustc` is a fully functional compiler, but it doesn't yet have
any libraries to link built binaries or libraries to. The next 3 steps will
provide those libraries for it; they are mostly equivalent to constructing the
`stage1/bin` compiler so we don't go through them individually here.
The `stage1/bin/rustc` is a fully functional compiler built with stage0 (precompiled) compiler and std.
To use a compiler built entirely from source with the in-tree compiler and std, you need to build the
stage2 compiler, which is compiled using the stage1 (in-tree) compiler and std.

View file

@ -217,7 +217,6 @@ probably the best "go to" command for building a local compiler:
This may *look* like it only builds the standard library, but that is not the case.
What this command does is the following:
- Build `std` using the stage0 compiler
- Build `rustc` using the stage0 compiler
- This produces the stage1 compiler
- Build `std` using the stage1 compiler
@ -241,8 +240,7 @@ build. The **full** `rustc` build (what you get with `./x build
--stage 2 compiler/rustc`) has quite a few more steps:
- Build `rustc` with the stage1 compiler.
- The resulting compiler here is called the "stage2" compiler.
- Build `std` with stage2 compiler.
- The resulting compiler here is called the "stage2" compiler, which uses stage1 std from the previous command.
- Build `librustdoc` and a bunch of other things with the stage2 compiler.
You almost never need to do this.
@ -250,14 +248,14 @@ You almost never need to do this.
### Build specific components
If you are working on the standard library, you probably don't need to build
the compiler unless you are planning to use a recently added nightly feature.
Instead, you can just build using the bootstrap compiler.
every other default component. Instead, you can build a specific component by
providing its name, like this:
```bash
./x build --stage 0 library
./x build --stage 1 library
```
If you choose the `library` profile when running `x setup`, you can omit `--stage 0` (it's the
If you choose the `library` profile when running `x setup`, you can omit `--stage 1` (it's the
default).
## Creating a rustup toolchain
@ -271,7 +269,6 @@ you will likely need to build at some point; for example, if you want
to run the entire test suite).
```bash
rustup toolchain link stage0 build/host/stage0-sysroot # beta compiler + stage0 std
rustup toolchain link stage1 build/host/stage1
rustup toolchain link stage2 build/host/stage2
```

View file

@ -85,7 +85,7 @@ Look for existing targets to use as examples.
After adding your target to the `rustc_target` crate you may want to add
`core`, `std`, ... with support for your new target. In that case you will
probably need access to some `target_*` cfg. Unfortunately when building with
stage0 (the beta compiler), you'll get an error that the target cfg is
stage0 (a precompiled compiler), you'll get an error that the target cfg is
unexpected because stage0 doesn't know about the new target specification and
we pass `--check-cfg` in order to tell it to check.

View file

@ -307,51 +307,15 @@ lets you use `cargo fmt`.
[the section on vscode]: suggested.md#configuring-rust-analyzer-for-rustc
[the section on rustup]: how-to-build-and-run.md?highlight=rustup#creating-a-rustup-toolchain
## Faster builds with `--keep-stage`.
## Faster Builds with CI-rustc
Sometimes just checking whether the compiler builds is not enough. A common
example is that you need to add a `debug!` statement to inspect the value of
some state or better understand the problem. In that case, you don't really need
a full build. By bypassing bootstrap's cache invalidation, you can often get
these builds to complete very fast (e.g., around 30 seconds). The only catch is
this requires a bit of fudging and may produce compilers that don't work (but
that is easily detected and fixed).
The sequence of commands you want is as follows:
- Initial build: `./x build library`
- As [documented previously], this will build a functional stage1 compiler as
part of running all stage0 commands (which include building a `std`
compatible with the stage1 compiler) as well as the first few steps of the
"stage 1 actions" up to "stage1 (sysroot stage1) builds std".
- Subsequent builds: `./x build library --keep-stage 1`
- Note that we added the `--keep-stage 1` flag here
[documented previously]: ./how-to-build-and-run.md#building-the-compiler
As mentioned, the effect of `--keep-stage 1` is that we just _assume_ that the
old standard library can be re-used. If you are editing the compiler, this is
almost always true: you haven't changed the standard library, after all. But
sometimes, it's not true: for example, if you are editing the "metadata" part of
the compiler, which controls how the compiler encodes types and other states
into the `rlib` files, or if you are editing things that wind up in the metadata
(such as the definition of the MIR).
**The TL;DR is that you might get weird behavior from a compile when using
`--keep-stage 1`** -- for example, strange [ICEs](../appendix/glossary.html#ice)
or other panics. In that case, you should simply remove the `--keep-stage 1`
from the command and rebuild. That ought to fix the problem.
You can also use `--keep-stage 1` when running tests. Something like this:
- Initial test run: `./x test tests/ui`
- Subsequent test run: `./x test tests/ui --keep-stage 1`
### Iterating the standard library with `--keep-stage`
If you are making changes to the standard library, you can use `./x build
--keep-stage 0 library` to iteratively rebuild the standard library without
rebuilding the compiler.
If you are not working on the compiler, you often don't need to build the compiler tree.
For example, you can skip building the compiler and only build the `library` tree or the
tools under `src/tools`. To achieve that, you have to enable this by setting the `download-rustc`
option in your configuration. This tells bootstrap to use the latest nightly compiler for `stage > 0`
steps, meaning it will have two precompiled compilers: stage0 compiler and `download-rustc` compiler
for `stage > 0` steps. This way, it will never need to build the in-tree compiler. As a result, your
build time will be significantly reduced by not building the in-tree compiler.
## Using incremental compilation

View file

@ -66,9 +66,9 @@ kinds of builds (sets of jobs).
### Pull Request builds
After each push to a pull request, a set of `pr` jobs are executed. Currently,
these execute the `x86_64-gnu-llvm-X`, `x86_64-gnu-tools`, `mingw-check` and
`mingw-check-tidy` jobs, all running on Linux. These execute a relatively short
(~30 minutes) and lightweight test suite that should catch common issues. More
these execute the `x86_64-gnu-llvm-X`, `x86_64-gnu-tools`, `mingw-check-1`, `mingw-check-2`
and `mingw-check-tidy` jobs, all running on Linux. These execute a relatively short
(~40 minutes) and lightweight test suite that should catch common issues. More
specifically, they run a set of lints, they try to perform a cross-compile check
build to Windows mingw (without producing any artifacts) and they test the
compiler using a *system* version of LLVM. Unfortunately, it would take too many

View file

@ -11,7 +11,7 @@ based on the ABI defined by Fortanix for the [Enclave Development Platform
[@jethrogb](https://github.com/jethrogb)
[@raoulstrackx](https://github.com/raoulstrackx)
[@mzohreva](https://github.com/mzohreva)
[@aditijannu](https://github.com/aditijannu)
Further contacts:

View file

@ -0,0 +1,7 @@
# `no-steal-thir`
By default, to save on memory, the THIR body (obtained from the `tcx.thir_body` query) is stolen
once no longer used. This is inconvenient for authors of rustc drivers who want to access the THIR.
This option disables the stealing. This has no observable effect on compiler behavior, only on
memory usage.

View file

@ -119,6 +119,7 @@ pub fn register_tests(cfg: &Config) -> Vec<TestInfo> {
// Register normal generators for all floats.
#[cfg(not(bootstrap))]
#[cfg(target_has_reliable_f16)]
register_float::<f16>(&mut tests, cfg);
register_float::<f32>(&mut tests, cfg);

View file

@ -170,6 +170,7 @@ macro_rules! impl_float {
impl_float!(f32, u32; f64, u64);
#[cfg(not(bootstrap))]
#[cfg(target_has_reliable_f16)]
impl_float!(f16, u16);

View file

@ -1606,7 +1606,7 @@ fn first_non_private<'tcx>(
&& let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(local_use_def_id)
&& let hir::ItemKind::Use(path, hir::UseKind::Single(_)) = item.kind
{
for res in &path.res {
for res in path.res.present_items() {
if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res {
continue;
}
@ -3014,7 +3014,7 @@ fn clean_use_statement<'tcx>(
) -> Vec<Item> {
let mut items = Vec::new();
let hir::UsePath { segments, ref res, span } = *path;
for &res in res {
for res in res.present_items() {
let path = hir::Path { segments, res, span };
items.append(&mut clean_use_statement_inner(import, name, &path, kind, cx, inlined_names));
}

View file

@ -440,7 +440,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
hir::ItemKind::GlobalAsm { .. } => {}
hir::ItemKind::Use(_, hir::UseKind::ListStem) => {}
hir::ItemKind::Use(path, kind) => {
for &res in &path.res {
for res in path.res.present_items() {
// Struct and variant constructors and proc macro stubs always show up alongside
// their definitions, we've already processed them so just discard these.
if should_ignore_res(res) {

View file

@ -42,7 +42,7 @@ walkdir = "2.3"
filetime = "0.2.9"
itertools = "0.12"
pulldown-cmark = { version = "0.11", default-features = false, features = ["html"] }
askama = { version = "0.13", default-features = false, features = ["alloc", "config", "derive"] }
askama = { version = "0.14", default-features = false, features = ["alloc", "config", "derive"] }
# UI test dependencies
if_chain = "1.0"

View file

@ -17,7 +17,7 @@ providing the `LateContext` (`cx`), our expression at hand, and
the symbol of the trait in question:
```rust
use clippy_utils::is_trait_method;
use clippy_utils::ty::implements_trait;
use rustc_hir::Expr;
use rustc_lint::{LateContext, LateLintPass};
use rustc_span::symbol::sym;
@ -25,7 +25,7 @@ use rustc_span::symbol::sym;
impl LateLintPass<'_> for CheckIteratorTraitLint {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
let implements_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| {
implements_trait(cx, cx.typeck_results().expr_ty(arg), id, &[])
implements_trait(cx, cx.typeck_results().expr_ty(expr), id, &[])
});
if implements_iterator {
// [...]

View file

@ -1026,7 +1026,7 @@ The order of associated items in traits.
The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by
reference.
**Default Value:** `target_pointer_width * 2`
**Default Value:** `target_pointer_width`
---
**Affected lints:**

View file

@ -828,7 +828,7 @@ define_Conf! {
trait_assoc_item_kinds_order: SourceItemOrderingTraitAssocItemKinds = DEFAULT_TRAIT_ASSOC_ITEM_KINDS_ORDER.into(),
/// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by
/// reference.
#[default_text = "target_pointer_width * 2"]
#[default_text = "target_pointer_width"]
#[lints(trivially_copy_pass_by_ref)]
trivial_copy_size_limit: Option<u64> = None,
/// The maximum complexity a type can have

View file

@ -1,5 +1,6 @@
use crate::utils::{
ClippyInfo, ErrAction, FileUpdater, UpdateMode, UpdateStatus, panic_action, run_with_args_split, run_with_output,
ErrAction, FileUpdater, UpdateMode, UpdateStatus, expect_action, run_with_output, split_args_for_threads,
walk_dir_no_dot_or_target,
};
use itertools::Itertools;
use rustc_lexer::{TokenKind, tokenize};
@ -9,7 +10,6 @@ use std::io::{self, Read};
use std::ops::ControlFlow;
use std::path::PathBuf;
use std::process::{self, Command, Stdio};
use walkdir::WalkDir;
pub enum Error {
Io(io::Error),
@ -260,7 +260,7 @@ fn fmt_syms(update_mode: UpdateMode) {
);
}
fn run_rustfmt(clippy: &ClippyInfo, update_mode: UpdateMode) {
fn run_rustfmt(update_mode: UpdateMode) {
let mut rustfmt_path = String::from_utf8(run_with_output(
"rustup which rustfmt",
Command::new("rustup").args(["which", "rustfmt"]),
@ -268,42 +268,19 @@ fn run_rustfmt(clippy: &ClippyInfo, update_mode: UpdateMode) {
.expect("invalid rustfmt path");
rustfmt_path.truncate(rustfmt_path.trim_end().len());
let mut cargo_path = String::from_utf8(run_with_output(
"rustup which cargo",
Command::new("rustup").args(["which", "cargo"]),
))
.expect("invalid cargo path");
cargo_path.truncate(cargo_path.trim_end().len());
let args: Vec<_> = walk_dir_no_dot_or_target()
.filter_map(|e| {
let e = expect_action(e, ErrAction::Read, ".");
e.path()
.as_os_str()
.as_encoded_bytes()
.ends_with(b".rs")
.then(|| e.into_path().into_os_string())
})
.collect();
// Start all format jobs first before waiting on the results.
let mut children = Vec::with_capacity(16);
for &path in &[
".",
"clippy_config",
"clippy_dev",
"clippy_lints",
"clippy_lints_internal",
"clippy_utils",
"rustc_tools_util",
"lintcheck",
] {
let mut cmd = Command::new(&cargo_path);
cmd.current_dir(clippy.path.join(path))
.args(["fmt"])
.env("RUSTFMT", &rustfmt_path)
.stdout(Stdio::null())
.stdin(Stdio::null())
.stderr(Stdio::piped());
if update_mode.is_check() {
cmd.arg("--check");
}
match cmd.spawn() {
Ok(x) => children.push(("cargo fmt", x)),
Err(ref e) => panic_action(&e, ErrAction::Run, "cargo fmt".as_ref()),
}
}
run_with_args_split(
let mut children: Vec<_> = split_args_for_threads(
32,
|| {
let mut cmd = Command::new(&rustfmt_path);
if update_mode.is_check() {
@ -312,66 +289,44 @@ fn run_rustfmt(clippy: &ClippyInfo, update_mode: UpdateMode) {
cmd.stdout(Stdio::null())
.stdin(Stdio::null())
.stderr(Stdio::piped())
.args(["--config", "show_parse_errors=false"]);
.args(["--unstable-features", "--skip-children"]);
cmd
},
|cmd| match cmd.spawn() {
Ok(x) => children.push(("rustfmt", x)),
Err(ref e) => panic_action(&e, ErrAction::Run, "rustfmt".as_ref()),
},
WalkDir::new("tests")
.into_iter()
.filter_entry(|p| p.path().file_name().is_none_or(|x| x != "skip_rustfmt"))
.filter_map(|e| {
let e = e.expect("error reading `tests`");
e.path()
.as_os_str()
.as_encoded_bytes()
.ends_with(b".rs")
.then(|| e.into_path().into_os_string())
}),
);
args.iter(),
)
.map(|mut cmd| expect_action(cmd.spawn(), ErrAction::Run, "rustfmt"))
.collect();
for (name, child) in &mut children {
match child.wait() {
Ok(status) => match (update_mode, status.exit_ok()) {
(UpdateMode::Check | UpdateMode::Change, Ok(())) => {},
(UpdateMode::Check, Err(_)) => {
let mut s = String::new();
if let Some(mut stderr) = child.stderr.take()
&& stderr.read_to_string(&mut s).is_ok()
{
eprintln!("{s}");
}
eprintln!("Formatting check failed!\nRun `cargo dev fmt` to update.");
process::exit(1);
},
(UpdateMode::Change, Err(e)) => {
let mut s = String::new();
if let Some(mut stderr) = child.stderr.take()
&& stderr.read_to_string(&mut s).is_ok()
{
eprintln!("{s}");
}
panic_action(&e, ErrAction::Run, name.as_ref());
},
for child in &mut children {
let status = expect_action(child.wait(), ErrAction::Run, "rustfmt");
match (update_mode, status.exit_ok()) {
(UpdateMode::Check | UpdateMode::Change, Ok(())) => {},
(UpdateMode::Check, Err(_)) => {
let mut s = String::new();
if let Some(mut stderr) = child.stderr.take()
&& stderr.read_to_string(&mut s).is_ok()
{
eprintln!("{s}");
}
eprintln!("Formatting check failed!\nRun `cargo dev fmt` to update.");
process::exit(1);
},
(UpdateMode::Change, e) => {
let mut s = String::new();
if let Some(mut stderr) = child.stderr.take()
&& stderr.read_to_string(&mut s).is_ok()
{
eprintln!("{s}");
}
expect_action(e, ErrAction::Run, "rustfmt");
},
Err(ref e) => panic_action(e, ErrAction::Run, name.as_ref()),
}
}
}
// the "main" function of cargo dev fmt
pub fn run(clippy: &ClippyInfo, update_mode: UpdateMode) {
if clippy.has_intellij_hook {
eprintln!(
"error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.\n\
Not formatting because that would format the local repo as well!\n\
Please revert the changes to `Cargo.toml`s with `cargo dev remove intellij`."
);
return;
}
run_rustfmt(clippy, update_mode);
pub fn run(update_mode: UpdateMode) {
run_rustfmt(update_mode);
fmt_syms(update_mode);
if let Err(e) = fmt_conf(update_mode.is_check()) {
e.display();

View file

@ -26,7 +26,7 @@ fn main() {
allow_staged,
allow_no_vcs,
} => dogfood::dogfood(fix, allow_dirty, allow_staged, allow_no_vcs),
DevCommand::Fmt { check } => fmt::run(&clippy, utils::UpdateMode::from_check(check)),
DevCommand::Fmt { check } => fmt::run(utils::UpdateMode::from_check(check)),
DevCommand::UpdateLints { check } => update_lints::update(utils::UpdateMode::from_check(check)),
DevCommand::NewLint {
pass,

View file

@ -1,13 +1,12 @@
use crate::update_lints::{RenamedLint, find_lint_decls, generate_lint_files, read_deprecated_lints};
use crate::utils::{
FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, Version, delete_dir_if_exists, delete_file_if_exists,
try_rename_dir, try_rename_file,
ErrAction, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, Version, delete_dir_if_exists,
delete_file_if_exists, expect_action, try_rename_dir, try_rename_file, walk_dir_no_dot_or_target,
};
use rustc_lexer::TokenKind;
use std::ffi::OsString;
use std::fs;
use std::path::Path;
use walkdir::WalkDir;
/// Runs the `rename_lint` command.
///
@ -133,17 +132,10 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b
}
let mut update_fn = file_update_fn(old_name, new_name, mod_edit);
for file in WalkDir::new(".").into_iter().filter_entry(|e| {
// Skip traversing some of the larger directories.
e.path()
.as_os_str()
.as_encoded_bytes()
.get(2..)
.is_none_or(|x| x != "target".as_bytes() && x != ".git".as_bytes())
}) {
let file = file.expect("error reading clippy directory");
if file.path().as_os_str().as_encoded_bytes().ends_with(b".rs") {
updater.update_file(file.path(), &mut update_fn);
for e in walk_dir_no_dot_or_target() {
let e = expect_action(e, ErrAction::Read, ".");
if e.path().as_os_str().as_encoded_bytes().ends_with(b".rs") {
updater.update_file(e.path(), &mut update_fn);
}
}
generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);

View file

@ -1,5 +1,5 @@
use crate::utils::{
ErrAction, File, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, panic_action, update_text_region_fn,
ErrAction, File, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, expect_action, update_text_region_fn,
};
use itertools::Itertools;
use std::collections::HashSet;
@ -201,10 +201,7 @@ pub fn find_lint_decls() -> Vec<Lint> {
/// Reads the source files from the given root directory
fn read_src_with_module(src_root: &Path) -> impl use<'_> + Iterator<Item = (DirEntry, String)> {
WalkDir::new(src_root).into_iter().filter_map(move |e| {
let e = match e {
Ok(e) => e,
Err(ref e) => panic_action(e, ErrAction::Read, src_root),
};
let e = expect_action(e, ErrAction::Read, src_root);
let path = e.path().as_os_str().as_encoded_bytes();
if let Some(path) = path.strip_suffix(b".rs")
&& let Some(path) = path.get("clippy_lints/src/".len()..)

View file

@ -1,14 +1,16 @@
use core::fmt::{self, Display};
use core::num::NonZero;
use core::ops::Range;
use core::slice;
use core::str::FromStr;
use rustc_lexer::{self as lexer, FrontmatterAllowed};
use std::env;
use std::ffi::OsStr;
use std::fs::{self, OpenOptions};
use std::io::{self, Read as _, Seek as _, SeekFrom, Write};
use std::path::{Path, PathBuf};
use std::process::{self, Command, ExitStatus, Stdio};
use std::{env, thread};
use walkdir::WalkDir;
#[cfg(not(windows))]
static CARGO_CLIPPY_EXE: &str = "cargo-clippy";
@ -45,6 +47,14 @@ pub fn panic_action(err: &impl Display, action: ErrAction, path: &Path) -> ! {
panic!("error {} `{}`: {}", action.as_str(), path.display(), *err)
}
#[track_caller]
pub fn expect_action<T>(res: Result<T, impl Display>, action: ErrAction, path: impl AsRef<Path>) -> T {
match res {
Ok(x) => x,
Err(ref e) => panic_action(e, action, path.as_ref()),
}
}
/// Wrapper around `std::fs::File` which panics with a path on failure.
pub struct File<'a> {
pub inner: fs::File,
@ -55,9 +65,9 @@ impl<'a> File<'a> {
#[track_caller]
pub fn open(path: &'a (impl AsRef<Path> + ?Sized), options: &mut OpenOptions) -> Self {
let path = path.as_ref();
match options.open(path) {
Ok(inner) => Self { inner, path },
Err(e) => panic_action(&e, ErrAction::Open, path),
Self {
inner: expect_action(options.open(path), ErrAction::Open, path),
path,
}
}
@ -84,10 +94,7 @@ impl<'a> File<'a> {
/// Read the entire contents of a file to the given buffer.
#[track_caller]
pub fn read_append_to_string<'dst>(&mut self, dst: &'dst mut String) -> &'dst mut String {
match self.inner.read_to_string(dst) {
Ok(_) => {},
Err(e) => panic_action(&e, ErrAction::Read, self.path),
}
expect_action(self.inner.read_to_string(dst), ErrAction::Read, self.path);
dst
}
@ -107,9 +114,7 @@ impl<'a> File<'a> {
},
Err(e) => Err(e),
};
if let Err(e) = res {
panic_action(&e, ErrAction::Write, self.path);
}
expect_action(res, ErrAction::Write, self.path);
}
}
@ -660,47 +665,91 @@ pub fn try_rename_dir(old_name: &Path, new_name: &Path) -> bool {
}
pub fn write_file(path: &Path, contents: &str) {
fs::write(path, contents).unwrap_or_else(|e| panic_action(&e, ErrAction::Write, path));
expect_action(fs::write(path, contents), ErrAction::Write, path);
}
#[must_use]
pub fn run_with_output(path: &(impl AsRef<Path> + ?Sized), cmd: &mut Command) -> Vec<u8> {
fn f(path: &Path, cmd: &mut Command) -> Vec<u8> {
match cmd
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::inherit())
.output()
{
Ok(x) => match x.status.exit_ok() {
Ok(()) => x.stdout,
Err(ref e) => panic_action(e, ErrAction::Run, path),
},
Err(ref e) => panic_action(e, ErrAction::Run, path),
}
let output = expect_action(
cmd.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::inherit())
.output(),
ErrAction::Run,
path,
);
expect_action(output.status.exit_ok(), ErrAction::Run, path);
output.stdout
}
f(path.as_ref(), cmd)
}
pub fn run_with_args_split(
mut make_cmd: impl FnMut() -> Command,
mut run_cmd: impl FnMut(&mut Command),
args: impl Iterator<Item: AsRef<OsStr>>,
) {
let mut cmd = make_cmd();
let mut len = 0;
for arg in args {
len += arg.as_ref().len();
cmd.arg(arg);
// Very conservative limit
if len > 10000 {
run_cmd(&mut cmd);
cmd = make_cmd();
len = 0;
/// Splits an argument list across multiple `Command` invocations.
///
/// The argument list will be split into a number of batches based on
/// `thread::available_parallelism`, with `min_batch_size` setting a lower bound on the size of each
/// batch.
///
/// If the size of the arguments would exceed the system limit additional batches will be created.
pub fn split_args_for_threads(
min_batch_size: usize,
make_cmd: impl FnMut() -> Command,
args: impl ExactSizeIterator<Item: AsRef<OsStr>>,
) -> impl Iterator<Item = Command> {
struct Iter<F, I> {
make_cmd: F,
args: I,
min_batch_size: usize,
batch_size: usize,
thread_count: usize,
}
impl<F, I> Iterator for Iter<F, I>
where
F: FnMut() -> Command,
I: ExactSizeIterator<Item: AsRef<OsStr>>,
{
type Item = Command;
fn next(&mut self) -> Option<Self::Item> {
if self.thread_count > 1 {
self.thread_count -= 1;
}
let mut cmd = (self.make_cmd)();
let mut cmd_len = 0usize;
for arg in self.args.by_ref().take(self.batch_size) {
cmd.arg(arg.as_ref());
// `+ 8` to account for the `argv` pointer on unix.
// Windows is complicated since the arguments are first converted to UTF-16ish,
// but this needs to account for the space between arguments and whatever additional
// is needed to escape within an argument.
cmd_len += arg.as_ref().len() + 8;
cmd_len += 8;
// Windows has a command length limit of 32767. For unix systems this is more
// complicated since the limit includes environment variables and room needs to be
// left to edit them once the program starts, but the total size comes from
// `getconf ARG_MAX`.
//
// For simplicity we use 30000 here under a few assumptions.
// * Individual arguments aren't super long (the final argument is still added)
// * `ARG_MAX` is set to a reasonable amount. Basically every system will be configured way above
// what windows supports, but POSIX only requires `4096`.
if cmd_len > 30000 {
self.batch_size = self.args.len().div_ceil(self.thread_count).max(self.min_batch_size);
break;
}
}
(cmd_len != 0).then_some(cmd)
}
}
if len != 0 {
run_cmd(&mut cmd);
let thread_count = thread::available_parallelism().map_or(1, NonZero::get);
let batch_size = args.len().div_ceil(thread_count).max(min_batch_size);
Iter {
make_cmd,
args,
min_batch_size,
batch_size,
thread_count,
}
}
@ -720,3 +769,12 @@ pub fn delete_dir_if_exists(path: &Path) {
Err(ref e) => panic_action(e, ErrAction::Delete, path),
}
}
/// Walks all items excluding top-level dot files/directories and any target directories.
pub fn walk_dir_no_dot_or_target() -> impl Iterator<Item = ::walkdir::Result<::walkdir::DirEntry>> {
WalkDir::new(".").into_iter().filter_entry(|e| {
e.path()
.file_name()
.is_none_or(|x| x != "target" && x.as_encoded_bytes().first().copied() != Some(b'.'))
})
}

View file

@ -5,7 +5,7 @@ use clippy_utils::macros::{MacroCall, macro_backtrace};
use clippy_utils::source::snippet_with_applicability;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Node};
use rustc_hir::{Closure, ClosureKind, CoroutineKind, Expr, ExprKind, LetStmt, LocalSource, Node, Stmt, StmtKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::impl_lint_pass;
use rustc_span::{Span, SyntaxContext, sym};
@ -60,6 +60,8 @@ impl LateLintPass<'_> for DbgMacro {
if cur_syntax_ctxt != self.prev_ctxt &&
let Some(macro_call) = first_dbg_macro_in_expansion(cx, expr.span) &&
!macro_call.span.in_external_macro(cx.sess().source_map()) &&
// avoids exprs generated by the desugaring of coroutines
!is_coroutine_desugar(expr) &&
self.checked_dbg_call_site.insert(macro_call.span) &&
// allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml
!(self.allow_dbg_in_tests && is_in_test(cx.tcx, expr.hir_id))
@ -73,50 +75,51 @@ impl LateLintPass<'_> for DbgMacro {
"the `dbg!` macro is intended as a debugging tool",
|diag| {
let mut applicability = Applicability::MachineApplicable;
let (sugg_span, suggestion) = match expr.peel_drop_temps().kind {
// dbg!()
ExprKind::Block(..) => {
// If the `dbg!` macro is a "free" statement and not contained within other expressions,
// remove the whole statement.
if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id)
&& let Some(semi_span) = cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span)
{
(macro_call.span.to(semi_span), String::new())
} else {
(macro_call.span, String::from("()"))
}
},
// dbg!(1)
ExprKind::Match(val, ..) => (
macro_call.span,
snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability)
.to_string(),
),
// dbg!(2, 3)
ExprKind::Tup(
[
Expr {
kind: ExprKind::Match(first, ..),
..
},
..,
Expr {
kind: ExprKind::Match(last, ..),
..
},
],
) => {
let snippet = snippet_with_applicability(
cx,
first.span.source_callsite().to(last.span.source_callsite()),
"..",
&mut applicability,
);
(macro_call.span, format!("({snippet})"))
},
_ => unreachable!(),
};
let (sugg_span, suggestion) =
match is_async_move_desugar(expr).unwrap_or(expr).peel_drop_temps().kind {
// dbg!()
ExprKind::Block(..) => {
// If the `dbg!` macro is a "free" statement and not contained within other expressions,
// remove the whole statement.
if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id)
&& let Some(semi_span) =
cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span)
{
(macro_call.span.to(semi_span), String::new())
} else {
(macro_call.span, String::from("()"))
}
},
// dbg!(1)
ExprKind::Match(val, ..) => (
macro_call.span,
snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability)
.to_string(),
),
// dbg!(2, 3)
ExprKind::Tup(
[
Expr {
kind: ExprKind::Match(first, ..),
..
},
..,
Expr {
kind: ExprKind::Match(last, ..),
..
},
],
) => {
let snippet = snippet_with_applicability(
cx,
first.span.source_callsite().to(last.span.source_callsite()),
"..",
&mut applicability,
);
(macro_call.span, format!("({snippet})"))
},
_ => unreachable!(),
};
diag.span_suggestion(
sugg_span,
@ -134,6 +137,35 @@ impl LateLintPass<'_> for DbgMacro {
}
}
fn is_coroutine_desugar(expr: &Expr<'_>) -> bool {
matches!(
expr.kind,
ExprKind::Closure(Closure {
kind: ClosureKind::Coroutine(CoroutineKind::Desugared(..)) | ClosureKind::CoroutineClosure(..),
..
})
)
}
fn is_async_move_desugar<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
if let ExprKind::Block(block, _) = expr.kind
&& let [
Stmt {
kind:
StmtKind::Let(LetStmt {
source: LocalSource::AsyncFn,
..
}),
..
},
] = block.stmts
{
return block.expr;
}
None
}
fn first_dbg_macro_in_expansion(cx: &LateContext<'_>, span: Span) -> Option<MacroCall> {
macro_backtrace(span).find(|mc| cx.tcx.is_diagnostic_item(sym::dbg_macro, mc.def_id))
}

View file

@ -305,7 +305,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
RefOp::Method { mutbl, is_ufcs }
if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
// Allow explicit deref in method chains. e.g. `foo.deref().bar()`
&& (is_ufcs || !in_postfix_position(cx, expr)) =>
&& (is_ufcs || !is_in_method_chain(cx, expr)) =>
{
let ty_changed_count = usize::from(!deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)));
self.state = Some((
@ -728,7 +728,13 @@ fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
}
}
fn in_postfix_position<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
fn is_in_method_chain<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
if let ExprKind::MethodCall(_, recv, _, _) = e.kind
&& matches!(recv.kind, ExprKind::MethodCall(..))
{
return true;
}
if let Some(parent) = get_parent_expr(cx, e)
&& parent.span.eq_ctxt(e.span)
{
@ -986,6 +992,15 @@ fn report<'tcx>(
);
},
State::DerefedBorrow(state) => {
// Do not suggest removing a non-mandatory `&` in `&*rawptr` in an `unsafe` context,
// as this may make rustc trigger its `dangerous_implicit_autorefs` lint.
if let ExprKind::AddrOf(BorrowKind::Ref, _, subexpr) = data.first_expr.kind
&& let ExprKind::Unary(UnOp::Deref, subsubexpr) = subexpr.kind
&& cx.typeck_results().expr_ty_adjusted(subsubexpr).is_raw_ptr()
{
return;
}
let mut app = Applicability::MachineApplicable;
let (snip, snip_is_macro) =
snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app);

View file

@ -106,8 +106,8 @@ impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]);
impl<'tcx> LateLintPass<'tcx> for DisallowedTypes {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
if let ItemKind::Use(path, UseKind::Single(_)) = &item.kind {
for res in &path.res {
self.check_res_emit(cx, res, item.span);
if let Some(res) = path.res.type_ns {
self.check_res_emit(cx, &res, item.span);
}
}
}

View file

@ -2,11 +2,10 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use itertools::Itertools;
use rustc_errors::Applicability;
use rustc_lint::LateContext;
use rustc_span::{BytePos, Span};
use std::cmp::Ordering;
use rustc_span::BytePos;
use std::ops::Range;
use super::{DOC_LAZY_CONTINUATION, DOC_OVERINDENTED_LIST_ITEMS};
use super::{DOC_LAZY_CONTINUATION, DOC_OVERINDENTED_LIST_ITEMS, Fragments};
fn map_container_to_text(c: &super::Container) -> &'static str {
match c {
@ -19,29 +18,27 @@ fn map_container_to_text(c: &super::Container) -> &'static str {
pub(super) fn check(
cx: &LateContext<'_>,
doc: &str,
range: Range<usize>,
mut span: Span,
cooked_range: Range<usize>,
fragments: &Fragments<'_>,
containers: &[super::Container],
) {
if doc[range.clone()].contains('\t') {
// We don't do tab stops correctly.
return;
}
// Blockquote
let ccount = doc[range.clone()].chars().filter(|c| *c == '>').count();
// Get blockquotes
let ccount = doc[cooked_range.clone()].chars().filter(|c| *c == '>').count();
let blockquote_level = containers
.iter()
.filter(|c| matches!(c, super::Container::Blockquote))
.count();
if ccount < blockquote_level {
if ccount < blockquote_level
&& let Some(mut span) = fragments.span(cx, cooked_range.clone())
{
span_lint_and_then(
cx,
DOC_LAZY_CONTINUATION,
span,
"doc quote line without `>` marker",
|diag| {
let mut doc_start_range = &doc[range];
let mut doc_start_range = &doc[cooked_range];
let mut suggested = String::new();
for c in containers {
let text = map_container_to_text(c);
@ -78,7 +75,7 @@ pub(super) fn check(
}
// List
let leading_spaces = doc[range].chars().filter(|c| *c == ' ').count();
let leading_spaces = doc[cooked_range.clone()].chars().filter(|c| *c == ' ').count();
let list_indentation = containers
.iter()
.map(|c| {
@ -89,36 +86,41 @@ pub(super) fn check(
}
})
.sum();
match leading_spaces.cmp(&list_indentation) {
Ordering::Less => span_lint_and_then(
cx,
DOC_LAZY_CONTINUATION,
span,
"doc list item without indentation",
|diag| {
// simpler suggestion style for indentation
let indent = list_indentation - leading_spaces;
diag.span_suggestion_verbose(
span.shrink_to_hi(),
"indent this line",
std::iter::repeat_n(" ", indent).join(""),
Applicability::MaybeIncorrect,
);
diag.help("if this is supposed to be its own paragraph, add a blank line");
},
),
Ordering::Greater => {
let sugg = std::iter::repeat_n(" ", list_indentation).join("");
span_lint_and_sugg(
if leading_spaces != list_indentation
&& let Some(span) = fragments.span(cx, cooked_range.clone())
{
if leading_spaces < list_indentation {
span_lint_and_then(
cx,
DOC_OVERINDENTED_LIST_ITEMS,
DOC_LAZY_CONTINUATION,
span,
"doc list item overindented",
format!("try using `{sugg}` ({list_indentation} spaces)"),
sugg,
Applicability::MaybeIncorrect,
"doc list item without indentation",
|diag| {
// simpler suggestion style for indentation
let indent = list_indentation - leading_spaces;
diag.span_suggestion_verbose(
span.shrink_to_hi(),
"indent this line",
std::iter::repeat_n(" ", indent).join(""),
Applicability::MaybeIncorrect,
);
diag.help("if this is supposed to be its own paragraph, add a blank line");
},
);
},
Ordering::Equal => {},
return;
}
let sugg = std::iter::repeat_n(" ", list_indentation).join("");
span_lint_and_sugg(
cx,
DOC_OVERINDENTED_LIST_ITEMS,
span,
"doc list item overindented",
format!("try using `{sugg}` ({list_indentation} spaces)"),
sugg,
Applicability::MaybeIncorrect,
);
}
}

View file

@ -6,13 +6,15 @@ use rustc_lint::LateContext;
use rustc_span::{BytePos, Pos, Span};
use url::Url;
use crate::doc::DOC_MARKDOWN;
use crate::doc::{DOC_MARKDOWN, Fragments};
use std::ops::Range;
pub fn check(
cx: &LateContext<'_>,
valid_idents: &FxHashSet<String>,
text: &str,
span: Span,
fragments: &Fragments<'_>,
fragment_range: Range<usize>,
code_level: isize,
blockquote_level: isize,
) {
@ -64,20 +66,31 @@ pub fn check(
close_parens += 1;
}
// Adjust for the current word
let offset = word.as_ptr() as usize - text.as_ptr() as usize;
let span = Span::new(
span.lo() + BytePos::from_usize(offset),
span.lo() + BytePos::from_usize(offset + word.len()),
span.ctxt(),
span.parent(),
);
// We'll use this offset to calculate the span to lint.
let fragment_offset = word.as_ptr() as usize - text.as_ptr() as usize;
check_word(cx, word, span, code_level, blockquote_level);
// Adjust for the current word
check_word(
cx,
word,
fragments,
&fragment_range,
fragment_offset,
code_level,
blockquote_level,
);
}
}
fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, blockquote_level: isize) {
fn check_word(
cx: &LateContext<'_>,
word: &str,
fragments: &Fragments<'_>,
range: &Range<usize>,
fragment_offset: usize,
code_level: isize,
blockquote_level: isize,
) {
/// Checks if a string is upper-camel-case, i.e., starts with an uppercase and
/// contains at least two uppercase letters (`Clippy` is ok) and one lower-case
/// letter (`NASA` is ok).
@ -117,6 +130,16 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, b
// try to get around the fact that `foo::bar` parses as a valid URL
&& !url.cannot_be_a_base()
{
let Some(fragment_span) = fragments.span(cx, range.clone()) else {
return;
};
let span = Span::new(
fragment_span.lo() + BytePos::from_usize(fragment_offset),
fragment_span.lo() + BytePos::from_usize(fragment_offset + word.len()),
fragment_span.ctxt(),
fragment_span.parent(),
);
span_lint_and_sugg(
cx,
DOC_MARKDOWN,
@ -137,6 +160,17 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, b
}
if has_underscore(word) || word.contains("::") || is_camel_case(word) || word.ends_with("()") {
let Some(fragment_span) = fragments.span(cx, range.clone()) else {
return;
};
let span = Span::new(
fragment_span.lo() + BytePos::from_usize(fragment_offset),
fragment_span.lo() + BytePos::from_usize(fragment_offset + word.len()),
fragment_span.ctxt(),
fragment_span.parent(),
);
span_lint_and_then(
cx,
DOC_MARKDOWN,

View file

@ -3,7 +3,6 @@
use clippy_config::Conf;
use clippy_utils::attrs::is_doc_hidden;
use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then};
use clippy_utils::source::snippet_opt;
use clippy_utils::{is_entrypoint_fn, is_trait_impl_item};
use pulldown_cmark::Event::{
Code, DisplayMath, End, FootnoteReference, HardBreak, Html, InlineHtml, InlineMath, Rule, SoftBreak, Start,
@ -730,7 +729,10 @@ struct Fragments<'a> {
}
impl Fragments<'_> {
fn span(self, cx: &LateContext<'_>, range: Range<usize>) -> Option<Span> {
/// get the span for the markdown range. Note that this function is not cheap, use it with
/// caution.
#[must_use]
fn span(&self, cx: &LateContext<'_>, range: Range<usize>) -> Option<Span> {
source_span_for_markdown_range(cx.tcx, self.doc, &range, self.fragments)
}
}
@ -1068,9 +1070,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
);
} else {
for (text, range, assoc_code_level) in text_to_check {
if let Some(span) = fragments.span(cx, range) {
markdown::check(cx, valid_idents, &text, span, assoc_code_level, blockquote_level);
}
markdown::check(cx, valid_idents, &text, &fragments, range, assoc_code_level, blockquote_level);
}
}
text_to_check = Vec::new();
@ -1081,26 +1081,27 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
| TaskListMarker(_) | Code(_) | Rule | InlineMath(..) | DisplayMath(..) => (),
SoftBreak | HardBreak => {
if !containers.is_empty()
&& let Some((next_event, next_range)) = events.peek()
&& let Some(next_span) = fragments.span(cx, next_range.clone())
&& let Some(span) = fragments.span(cx, range.clone())
&& !in_footnote_definition
// Tabs aren't handled correctly vvvv
&& !doc[range.clone()].contains('\t')
&& let Some((next_event, next_range)) = events.peek()
&& !matches!(next_event, End(_))
{
lazy_continuation::check(
cx,
doc,
range.end..next_range.start,
Span::new(span.hi(), next_span.lo(), span.ctxt(), span.parent()),
&fragments,
&containers[..],
);
}
if let Some(span) = fragments.span(cx, range.clone())
if event == HardBreak
&& !doc[range.clone()].trim().starts_with('\\')
&& let Some(span) = fragments.span(cx, range.clone())
&& !span.from_expansion()
&& let Some(snippet) = snippet_opt(cx, span)
&& !snippet.trim().starts_with('\\')
&& event == HardBreak {
{
collected_breaks.push(span);
}
},

View file

@ -51,7 +51,9 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants {
// so lint on the `use` statement directly.
if let ItemKind::Use(path, kind @ (UseKind::Single(_) | UseKind::Glob)) = item.kind
&& !item.span.in_external_macro(cx.sess().source_map())
&& let Some(def_id) = path.res[0].opt_def_id()
// use `present_items` because it could be in either type_ns or value_ns
&& let Some(res) = path.res.present_items().next()
&& let Some(def_id) = res.opt_def_id()
&& self.msrv.meets(cx, msrvs::NUMERIC_ASSOCIATED_CONSTANTS)
{
let module = if is_integer_module(cx, def_id) {

View file

@ -83,6 +83,13 @@ pub(super) fn check<'tcx>(
)[..],
);
}
// If the return type requires adjustments, we need to add a `.map` after the iterator
let inner_ret_adjust = cx.typeck_results().expr_adjustments(inner_ret);
if !inner_ret_adjust.is_empty() {
snippet.push_str(".map(|v| v as _)");
}
// Extends to `last_stmt` to include semicolon in case of `return None;`
let lint_span = span.to(last_stmt.span).to(last_ret.span);
span_lint_and_then(

Some files were not shown because too many files have changed in this diff Show more