Auto merge of #148356 - matthiaskrgr:rollup-mh4l2yi, r=matthiaskrgr
Rollup of 10 pull requests Successful merges: - rust-lang/rust#135602 (Tweak output of missing lifetime on associated type) - rust-lang/rust#139751 (Implement pin-project in pattern matching for `&pin mut|const T`) - rust-lang/rust#142682 (Update bundled musl to 1.2.5) - rust-lang/rust#148171 (Simplify code to generate line numbers in highlight) - rust-lang/rust#148263 (Unpin `libc` and `rustix` in `compiler` and `rustbook`) - rust-lang/rust#148301 ([rustdoc search] Include extern crates when filtering on `import`) - rust-lang/rust#148330 (Don't require dlltool with the dummy backend on MinGW) - rust-lang/rust#148338 (cleanup: upstream dropped amx-transpose functionality) - rust-lang/rust#148340 (Clippy subtree update) - rust-lang/rust#148343 (`nonpoison::Condvar` should take `MutexGuard` by reference) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
fca2e941f8
356 changed files with 8005 additions and 3376 deletions
28
Cargo.lock
28
Cargo.lock
|
|
@ -580,7 +580,7 @@ checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675"
|
|||
|
||||
[[package]]
|
||||
name = "clippy"
|
||||
version = "0.1.92"
|
||||
version = "0.1.93"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"askama",
|
||||
|
|
@ -607,7 +607,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clippy_config"
|
||||
version = "0.1.92"
|
||||
version = "0.1.93"
|
||||
dependencies = [
|
||||
"clippy_utils",
|
||||
"itertools",
|
||||
|
|
@ -630,7 +630,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clippy_lints"
|
||||
version = "0.1.92"
|
||||
version = "0.1.93"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"cargo_metadata 0.18.1",
|
||||
|
|
@ -662,7 +662,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clippy_utils"
|
||||
version = "0.1.92"
|
||||
version = "0.1.93"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"itertools",
|
||||
|
|
@ -1066,7 +1066,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "declare_clippy_lint"
|
||||
version = "0.1.92"
|
||||
version = "0.1.93"
|
||||
|
||||
[[package]]
|
||||
name = "derive-where"
|
||||
|
|
@ -1288,7 +1288,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2101,9 +2101,9 @@ checksum = "9fa0e2a1fcbe2f6be6c42e342259976206b383122fc152e872795338b5a3f3a7"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.174"
|
||||
version = "0.2.177"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||
|
||||
[[package]]
|
||||
name = "libdbus-sys"
|
||||
|
|
@ -2154,7 +2154,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-targets 0.53.3",
|
||||
"windows-targets 0.52.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2216,9 +2216,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.9.4"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
|
||||
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
|
||||
|
||||
[[package]]
|
||||
name = "litemap"
|
||||
|
|
@ -4897,15 +4897,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.0.8"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
|
||||
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -789,14 +789,14 @@ pub struct PatField {
|
|||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
#[derive(Encodable, Decodable, HashStable_Generic, Walkable)]
|
||||
pub enum ByRef {
|
||||
Yes(Mutability),
|
||||
Yes(Pinnedness, Mutability),
|
||||
No,
|
||||
}
|
||||
|
||||
impl ByRef {
|
||||
#[must_use]
|
||||
pub fn cap_ref_mutability(mut self, mutbl: Mutability) -> Self {
|
||||
if let ByRef::Yes(old_mutbl) = &mut self {
|
||||
if let ByRef::Yes(_, old_mutbl) = &mut self {
|
||||
*old_mutbl = cmp::min(*old_mutbl, mutbl);
|
||||
}
|
||||
self
|
||||
|
|
@ -814,20 +814,33 @@ pub struct BindingMode(pub ByRef, pub Mutability);
|
|||
|
||||
impl BindingMode {
|
||||
pub const NONE: Self = Self(ByRef::No, Mutability::Not);
|
||||
pub const REF: Self = Self(ByRef::Yes(Mutability::Not), Mutability::Not);
|
||||
pub const REF: Self = Self(ByRef::Yes(Pinnedness::Not, Mutability::Not), Mutability::Not);
|
||||
pub const REF_PIN: Self =
|
||||
Self(ByRef::Yes(Pinnedness::Pinned, Mutability::Not), Mutability::Not);
|
||||
pub const MUT: Self = Self(ByRef::No, Mutability::Mut);
|
||||
pub const REF_MUT: Self = Self(ByRef::Yes(Mutability::Mut), Mutability::Not);
|
||||
pub const MUT_REF: Self = Self(ByRef::Yes(Mutability::Not), Mutability::Mut);
|
||||
pub const MUT_REF_MUT: Self = Self(ByRef::Yes(Mutability::Mut), Mutability::Mut);
|
||||
pub const REF_MUT: Self = Self(ByRef::Yes(Pinnedness::Not, Mutability::Mut), Mutability::Not);
|
||||
pub const REF_PIN_MUT: Self =
|
||||
Self(ByRef::Yes(Pinnedness::Pinned, Mutability::Mut), Mutability::Not);
|
||||
pub const MUT_REF: Self = Self(ByRef::Yes(Pinnedness::Not, Mutability::Not), Mutability::Mut);
|
||||
pub const MUT_REF_PIN: Self =
|
||||
Self(ByRef::Yes(Pinnedness::Pinned, Mutability::Not), Mutability::Mut);
|
||||
pub const MUT_REF_MUT: Self =
|
||||
Self(ByRef::Yes(Pinnedness::Not, Mutability::Mut), Mutability::Mut);
|
||||
pub const MUT_REF_PIN_MUT: Self =
|
||||
Self(ByRef::Yes(Pinnedness::Pinned, Mutability::Mut), Mutability::Mut);
|
||||
|
||||
pub fn prefix_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::NONE => "",
|
||||
Self::REF => "ref ",
|
||||
Self::REF_PIN => "ref pin const ",
|
||||
Self::MUT => "mut ",
|
||||
Self::REF_MUT => "ref mut ",
|
||||
Self::REF_PIN_MUT => "ref pin mut ",
|
||||
Self::MUT_REF => "mut ref ",
|
||||
Self::MUT_REF_PIN => "mut ref pin ",
|
||||
Self::MUT_REF_MUT => "mut ref mut ",
|
||||
Self::MUT_REF_PIN_MUT => "mut ref pin mut ",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -368,6 +368,7 @@ macro_rules! common_visitor_and_walkers {
|
|||
crate::tokenstream::TokenStream,
|
||||
Movability,
|
||||
Mutability,
|
||||
Pinnedness,
|
||||
Result<(), rustc_span::ErrorGuaranteed>,
|
||||
rustc_data_structures::fx::FxHashMap<Symbol, usize>,
|
||||
rustc_span::ErrorGuaranteed,
|
||||
|
|
|
|||
|
|
@ -311,3 +311,10 @@ pub enum Pinnedness {
|
|||
Not,
|
||||
Pinned,
|
||||
}
|
||||
|
||||
impl Pinnedness {
|
||||
/// Return `true` if self is pinned
|
||||
pub fn is_pinned(self) -> bool {
|
||||
matches!(self, Self::Pinned)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1712,10 +1712,15 @@ impl<'a> State<'a> {
|
|||
if mutbl.is_mut() {
|
||||
self.word_nbsp("mut");
|
||||
}
|
||||
if let ByRef::Yes(rmutbl) = by_ref {
|
||||
if let ByRef::Yes(pinnedness, rmutbl) = by_ref {
|
||||
self.word_nbsp("ref");
|
||||
if pinnedness.is_pinned() {
|
||||
self.word_nbsp("pin");
|
||||
}
|
||||
if rmutbl.is_mut() {
|
||||
self.word_nbsp("mut");
|
||||
} else if pinnedness.is_pinned() {
|
||||
self.word_nbsp("const");
|
||||
}
|
||||
}
|
||||
self.print_ident(*ident);
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ pub(crate) mod must_use;
|
|||
pub(crate) mod no_implicit_prelude;
|
||||
pub(crate) mod non_exhaustive;
|
||||
pub(crate) mod path;
|
||||
pub(crate) mod pin_v2;
|
||||
pub(crate) mod proc_macro_attrs;
|
||||
pub(crate) mod prototype;
|
||||
pub(crate) mod repr;
|
||||
|
|
|
|||
21
compiler/rustc_attr_parsing/src/attributes/pin_v2.rs
Normal file
21
compiler/rustc_attr_parsing/src/attributes/pin_v2.rs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
use rustc_hir::Target;
|
||||
use rustc_hir::attrs::AttributeKind;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
|
||||
use crate::attributes::{NoArgsAttributeParser, OnDuplicate};
|
||||
use crate::context::Stage;
|
||||
use crate::target_checking::AllowedTargets;
|
||||
use crate::target_checking::Policy::Allow;
|
||||
|
||||
pub(crate) struct PinV2Parser;
|
||||
|
||||
impl<S: Stage> NoArgsAttributeParser<S> for PinV2Parser {
|
||||
const PATH: &[Symbol] = &[sym::pin_v2];
|
||||
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Warn;
|
||||
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[
|
||||
Allow(Target::Enum),
|
||||
Allow(Target::Struct),
|
||||
Allow(Target::Union),
|
||||
]);
|
||||
const CREATE: fn(Span) -> AttributeKind = AttributeKind::PinV2;
|
||||
}
|
||||
|
|
@ -47,6 +47,7 @@ use crate::attributes::must_use::MustUseParser;
|
|||
use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser;
|
||||
use crate::attributes::non_exhaustive::NonExhaustiveParser;
|
||||
use crate::attributes::path::PathParser as PathAttributeParser;
|
||||
use crate::attributes::pin_v2::PinV2Parser;
|
||||
use crate::attributes::proc_macro_attrs::{
|
||||
ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser,
|
||||
};
|
||||
|
|
@ -233,6 +234,7 @@ attribute_parsers!(
|
|||
Single<WithoutArgs<NonExhaustiveParser>>,
|
||||
Single<WithoutArgs<ParenSugarParser>>,
|
||||
Single<WithoutArgs<PassByValueParser>>,
|
||||
Single<WithoutArgs<PinV2Parser>>,
|
||||
Single<WithoutArgs<PointeeParser>>,
|
||||
Single<WithoutArgs<ProcMacroAttributeParser>>,
|
||||
Single<WithoutArgs<ProcMacroParser>>,
|
||||
|
|
|
|||
|
|
@ -1188,7 +1188,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
}
|
||||
|
||||
LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm {
|
||||
binding_mode: BindingMode(ByRef::Yes(_), _),
|
||||
binding_mode: BindingMode(ByRef::Yes(..), _),
|
||||
..
|
||||
})) => {
|
||||
let pattern_span: Span = local_decl.source_info.span;
|
||||
|
|
|
|||
|
|
@ -2582,6 +2582,16 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
|||
_ => bug!("Deref of unexpected type: {:?}", base_ty),
|
||||
}
|
||||
}
|
||||
// Check as the inner reference type if it is a field projection
|
||||
// from the `&pin` pattern
|
||||
ProjectionElem::Field(FieldIdx::ZERO, _)
|
||||
if let Some(adt) =
|
||||
place_base.ty(self.body(), self.infcx.tcx).ty.ty_adt_def()
|
||||
&& adt.is_pin()
|
||||
&& self.infcx.tcx.features().pin_ergonomics() =>
|
||||
{
|
||||
self.is_mutable(place_base, is_local_mutation_allowed)
|
||||
}
|
||||
// All other projections are owned by their base path, so mutable if
|
||||
// base path is mutable
|
||||
ProjectionElem::Field(..)
|
||||
|
|
|
|||
|
|
@ -893,6 +893,15 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||
EncodeCrossCrate::No, loop_match, experimental!(loop_match)
|
||||
),
|
||||
|
||||
// The `#[pin_v2]` attribute is part of the `pin_ergonomics` experiment
|
||||
// that allows structurally pinning, tracked in:
|
||||
//
|
||||
// - https://github.com/rust-lang/rust/issues/130494
|
||||
gated!(
|
||||
pin_v2, Normal, template!(Word), ErrorFollowing,
|
||||
EncodeCrossCrate::Yes, pin_ergonomics, experimental!(pin_v2),
|
||||
),
|
||||
|
||||
// ==========================================================================
|
||||
// Internal attributes: Stability, deprecation, and unsafe:
|
||||
// ==========================================================================
|
||||
|
|
|
|||
|
|
@ -637,6 +637,9 @@ pub enum AttributeKind {
|
|||
/// Represents `#[pattern_complexity_limit]`
|
||||
PatternComplexityLimit { attr_span: Span, limit_span: Span, limit: Limit },
|
||||
|
||||
/// Represents `#[pin_v2]`
|
||||
PinV2(Span),
|
||||
|
||||
/// Represents `#[pointee]`
|
||||
Pointee(Span),
|
||||
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ impl AttributeKind {
|
|||
PassByValue(..) => Yes,
|
||||
Path(..) => No,
|
||||
PatternComplexityLimit { .. } => No,
|
||||
PinV2(..) => Yes,
|
||||
Pointee(..) => No,
|
||||
ProcMacro(..) => No,
|
||||
ProcMacroAttribute(..) => No,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ use rustc_ast::{
|
|||
pub use rustc_ast::{
|
||||
AssignOp, AssignOpKind, AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind,
|
||||
BoundConstness, BoundPolarity, ByRef, CaptureBy, DelimArgs, ImplPolarity, IsAuto,
|
||||
MetaItemInner, MetaItemLit, Movability, Mutability, UnOp,
|
||||
MetaItemInner, MetaItemLit, Movability, Mutability, Pinnedness, UnOp,
|
||||
};
|
||||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
|
|
|
|||
|
|
@ -575,7 +575,7 @@ fn resolve_local<'tcx>(
|
|||
// & expression, and its lifetime would be extended to the end of the block (due
|
||||
// to a different rule, not the below code).
|
||||
match pat.kind {
|
||||
PatKind::Binding(hir::BindingMode(hir::ByRef::Yes(_), _), ..) => true,
|
||||
PatKind::Binding(hir::BindingMode(hir::ByRef::Yes(..), _), ..) => true,
|
||||
|
||||
PatKind::Struct(_, field_pats, _) => field_pats.iter().any(|fp| is_binding_pat(fp.pat)),
|
||||
|
||||
|
|
|
|||
|
|
@ -12,11 +12,12 @@ use std::assert_matches::debug_assert_matches;
|
|||
|
||||
use min_specialization::check_min_specialization;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
|
||||
use rustc_span::ErrorGuaranteed;
|
||||
use rustc_span::{ErrorGuaranteed, kw};
|
||||
|
||||
use crate::constrained_generic_params as cgp;
|
||||
use crate::errors::UnconstrainedGenericParameter;
|
||||
|
|
@ -150,6 +151,27 @@ pub(crate) fn enforce_impl_lifetime_params_are_constrained(
|
|||
const_param_note2: false,
|
||||
});
|
||||
diag.code(E0207);
|
||||
for p in &impl_generics.own_params {
|
||||
if p.name == kw::UnderscoreLifetime {
|
||||
let span = tcx.def_span(p.def_id);
|
||||
let Ok(snippet) = tcx.sess.source_map().span_to_snippet(span) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let (span, sugg) = if &snippet == "'_" {
|
||||
(span, param.name.to_string())
|
||||
} else {
|
||||
(span.shrink_to_hi(), format!("{} ", param.name))
|
||||
};
|
||||
diag.span_suggestion_verbose(
|
||||
span,
|
||||
"consider using the named lifetime here instead of an implicit \
|
||||
lifetime",
|
||||
sugg,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
res = Err(diag.emit());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1925,10 +1925,15 @@ impl<'a> State<'a> {
|
|||
if mutbl.is_mut() {
|
||||
self.word_nbsp("mut");
|
||||
}
|
||||
if let ByRef::Yes(rmutbl) = by_ref {
|
||||
if let ByRef::Yes(pinnedness, rmutbl) = by_ref {
|
||||
self.word_nbsp("ref");
|
||||
if pinnedness.is_pinned() {
|
||||
self.word_nbsp("pin");
|
||||
}
|
||||
if rmutbl.is_mut() {
|
||||
self.word_nbsp("mut");
|
||||
} else if pinnedness.is_pinned() {
|
||||
self.word_nbsp("const");
|
||||
}
|
||||
}
|
||||
self.print_ident(ident);
|
||||
|
|
|
|||
|
|
@ -227,6 +227,10 @@ hir_typeck_pass_to_variadic_function = can't pass `{$ty}` to variadic function
|
|||
.suggestion = cast the value to `{$cast_ty}`
|
||||
.teach_help = certain types, like `{$ty}`, must be cast before passing them to a variadic function to match the implicit cast that a C compiler would perform as part of C's numeric promotion rules
|
||||
|
||||
hir_typeck_project_on_non_pin_project_type = cannot project on type that is not `#[pin_v2]`
|
||||
.note = type defined here
|
||||
.suggestion = add `#[pin_v2]` here
|
||||
|
||||
hir_typeck_ptr_cast_add_auto_to_object = cannot add {$traits_len ->
|
||||
[1] auto trait {$traits}
|
||||
*[other] auto traits {$traits}
|
||||
|
|
|
|||
|
|
@ -1156,3 +1156,14 @@ pub(crate) struct ConstContinueBadLabel {
|
|||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_typeck_project_on_non_pin_project_type)]
|
||||
pub(crate) struct ProjectOnNonPinProjectType {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
#[note]
|
||||
pub def_span: Option<Span>,
|
||||
#[suggestion(code = "#[pin_v2]\n", applicability = "machine-applicable")]
|
||||
pub sugg_span: Option<Span>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -986,7 +986,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
|
|||
// of the pattern, as this just looks confusing, instead use the span
|
||||
// of the discriminant.
|
||||
match bm.0 {
|
||||
hir::ByRef::Yes(m) => {
|
||||
hir::ByRef::Yes(_, m) => {
|
||||
let bk = ty::BorrowKind::from_mutbl(m);
|
||||
self.delegate.borrow_mut().borrow(place, discr_place.hir_id, bk);
|
||||
}
|
||||
|
|
@ -1004,7 +1004,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
|
|||
// Deref patterns on boxes don't borrow, so we ignore them here.
|
||||
// HACK: this could be a fake pattern corresponding to a deref inserted by match
|
||||
// ergonomics, in which case `pat.hir_id` will be the id of the subpattern.
|
||||
if let hir::ByRef::Yes(mutability) =
|
||||
if let hir::ByRef::Yes(_, mutability) =
|
||||
self.cx.typeck_results().deref_pat_borrow_mode(place.place.ty(), subpattern)
|
||||
{
|
||||
let bk = ty::BorrowKind::from_mutbl(mutability);
|
||||
|
|
@ -1256,7 +1256,15 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
|
|||
.get(pat.hir_id)
|
||||
.expect("missing binding mode");
|
||||
|
||||
if matches!(bm.0, hir::ByRef::Yes(_)) {
|
||||
if let hir::ByRef::Yes(pinnedness, _) = bm.0 {
|
||||
let base_ty = if pinnedness.is_pinned() {
|
||||
base_ty.pinned_ty().ok_or_else(|| {
|
||||
debug!("By-pin-ref binding of non-`Pin` type: {base_ty:?}");
|
||||
self.cx.report_bug(pat.span, "by-pin-ref binding of non-`Pin` type")
|
||||
})?
|
||||
} else {
|
||||
base_ty
|
||||
};
|
||||
// a bind-by-ref means that the base_ty will be the type of the ident itself,
|
||||
// but what we want here is the type of the underlying value being borrowed.
|
||||
// So peel off one-level, turning the &T into T.
|
||||
|
|
@ -1264,7 +1272,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
|
|||
{
|
||||
Some(ty) => Ok(ty),
|
||||
None => {
|
||||
debug!("By-ref binding of non-derefable type");
|
||||
debug!("By-ref binding of non-derefable type: {base_ty:?}");
|
||||
Err(self
|
||||
.cx
|
||||
.report_bug(pat.span, "by-ref binding of non-derefable type"))
|
||||
|
|
@ -1706,6 +1714,18 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
|
|||
};
|
||||
self.pat_deref_place(pat.hir_id, place_with_id, pat, target_ty)?
|
||||
}
|
||||
adjustment::PatAdjust::PinDeref => {
|
||||
debug!("`PinDeref` of non-pinned-reference type: {:?}", adjust.source);
|
||||
let target_ty = adjust.source.pinned_ty().ok_or_else(|| {
|
||||
self.cx.report_bug(
|
||||
self.cx.tcx().hir_span(pat.hir_id),
|
||||
"`PinDeref` of non-pinned-reference type",
|
||||
)
|
||||
})?;
|
||||
let kind = ProjectionKind::Field(FieldIdx::ZERO, FIRST_VARIANT);
|
||||
place_with_id = self.cat_projection(pat.hir_id, place_with_id, target_ty, kind);
|
||||
self.cat_deref(pat.hir_id, place_with_id)?
|
||||
}
|
||||
};
|
||||
}
|
||||
drop(typeck_results); // explicitly release borrow of typeck results, just in case.
|
||||
|
|
@ -1877,7 +1897,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx
|
|||
// Deref patterns on boxes are lowered using a built-in deref.
|
||||
hir::ByRef::No => self.cat_deref(hir_id, base_place),
|
||||
// For other types, we create a temporary to match on.
|
||||
hir::ByRef::Yes(mutability) => {
|
||||
hir::ByRef::Yes(_, mutability) => {
|
||||
let re_erased = self.cx.tcx().lifetimes.re_erased;
|
||||
let ty = Ty::new_ref(self.cx.tcx(), re_erased, target_ty, mutability);
|
||||
// A deref pattern stores the result of `Deref::deref` or `DerefMut::deref_mut` ...
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use rustc_hir::{
|
|||
use rustc_hir_analysis::autoderef::report_autoderef_recursion_limit_error;
|
||||
use rustc_infer::infer::RegionVariableOrigin;
|
||||
use rustc_middle::traits::PatternOriginExpr;
|
||||
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
|
||||
use rustc_middle::ty::{self, Pinnedness, Ty, TypeVisitableExt};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
|
||||
use rustc_session::parse::feature_err;
|
||||
|
|
@ -403,7 +403,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let ty = self.check_pat_inner(pat, opt_path_res, adjust_mode, expected, pat_info);
|
||||
self.write_ty(pat.hir_id, ty);
|
||||
|
||||
// If we implicitly inserted overloaded dereferences before matching, check the pattern to
|
||||
// If we implicitly inserted overloaded dereferences before matching check the pattern to
|
||||
// see if the dereferenced types need `DerefMut` bounds.
|
||||
if let Some(derefed_tys) = self.typeck_results.borrow().pat_adjustments().get(pat.hir_id)
|
||||
&& derefed_tys.iter().any(|adjust| adjust.kind == PatAdjust::OverloadedDeref)
|
||||
|
|
@ -413,7 +413,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
pat,
|
||||
derefed_tys.iter().filter_map(|adjust| match adjust.kind {
|
||||
PatAdjust::OverloadedDeref => Some(adjust.source),
|
||||
PatAdjust::BuiltinDeref => None,
|
||||
PatAdjust::BuiltinDeref | PatAdjust::PinDeref => None,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
|
@ -471,7 +471,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
pat_info: PatInfo<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
#[cfg(debug_assertions)]
|
||||
if pat_info.binding_mode == ByRef::Yes(Mutability::Mut)
|
||||
if matches!(pat_info.binding_mode, ByRef::Yes(_, Mutability::Mut))
|
||||
&& pat_info.max_ref_mutbl != MutblCap::Mut
|
||||
&& self.downgrade_mut_inside_shared()
|
||||
{
|
||||
|
|
@ -489,12 +489,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let old_pat_info = pat_info;
|
||||
let pat_info = PatInfo { current_depth: old_pat_info.current_depth + 1, ..old_pat_info };
|
||||
|
||||
let adjust_binding_mode = |inner_pinnedness, inner_mutability| {
|
||||
match pat_info.binding_mode {
|
||||
// If default binding mode is by value, make it `ref`, `ref mut`, `ref pin const`
|
||||
// or `ref pin mut` (depending on whether we observe `&`, `&mut`, `&pin const` or
|
||||
// `&pin mut`).
|
||||
ByRef::No => ByRef::Yes(inner_pinnedness, inner_mutability),
|
||||
// When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`).
|
||||
// Pinnedness is preserved.
|
||||
ByRef::Yes(pinnedness, Mutability::Mut) => ByRef::Yes(pinnedness, inner_mutability),
|
||||
// Once a `ref`, always a `ref`.
|
||||
// This is because a `& &mut` cannot mutate the underlying value.
|
||||
// Pinnedness is preserved.
|
||||
ByRef::Yes(pinnedness, Mutability::Not) => ByRef::Yes(pinnedness, Mutability::Not),
|
||||
}
|
||||
};
|
||||
|
||||
match pat.kind {
|
||||
// Peel off a `&` or `&mut` from the scrutinee type. See the examples in
|
||||
// Peel off a `&` or `&mut`from the scrutinee type. See the examples in
|
||||
// `tests/ui/rfcs/rfc-2005-default-binding-mode`.
|
||||
_ if let AdjustMode::Peel { kind: peel_kind } = adjust_mode
|
||||
&& pat.default_binding_modes
|
||||
&& let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind()
|
||||
&& let &ty::Ref(_, inner_ty, inner_mutability) = expected.kind()
|
||||
&& self.should_peel_ref(peel_kind, expected) =>
|
||||
{
|
||||
debug!("inspecting {:?}", expected);
|
||||
|
|
@ -508,22 +524,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.or_default()
|
||||
.push(PatAdjustment { kind: PatAdjust::BuiltinDeref, source: expected });
|
||||
|
||||
let mut binding_mode = ByRef::Yes(match pat_info.binding_mode {
|
||||
// If default binding mode is by value, make it `ref` or `ref mut`
|
||||
// (depending on whether we observe `&` or `&mut`).
|
||||
ByRef::No |
|
||||
// When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`).
|
||||
ByRef::Yes(Mutability::Mut) => inner_mutability,
|
||||
// Once a `ref`, always a `ref`.
|
||||
// This is because a `& &mut` cannot mutate the underlying value.
|
||||
ByRef::Yes(Mutability::Not) => Mutability::Not,
|
||||
});
|
||||
let mut binding_mode = adjust_binding_mode(Pinnedness::Not, inner_mutability);
|
||||
|
||||
let mut max_ref_mutbl = pat_info.max_ref_mutbl;
|
||||
if self.downgrade_mut_inside_shared() {
|
||||
binding_mode = binding_mode.cap_ref_mutability(max_ref_mutbl.as_mutbl());
|
||||
}
|
||||
if binding_mode == ByRef::Yes(Mutability::Not) {
|
||||
if matches!(binding_mode, ByRef::Yes(_, Mutability::Not)) {
|
||||
max_ref_mutbl = MutblCap::Not;
|
||||
}
|
||||
debug!("default binding mode is now {:?}", binding_mode);
|
||||
|
|
@ -533,6 +540,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// Recurse with the new expected type.
|
||||
self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, new_pat_info)
|
||||
}
|
||||
// If `pin_ergonomics` is enabled, peel the `&pin` from the pinned reference type. See the
|
||||
// examples in `tests/ui/async-await/pin-ergonomics/`.
|
||||
_ if self.tcx.features().pin_ergonomics()
|
||||
&& let AdjustMode::Peel { kind: peel_kind } = adjust_mode
|
||||
&& pat.default_binding_modes
|
||||
&& self.should_peel_smart_pointer(peel_kind, expected)
|
||||
&& let Some(pinned_ty) = expected.pinned_ty()
|
||||
// Currently, only pinned reference is specially handled, leaving other
|
||||
// pinned types (e.g. `Pin<Box<T>>` to deref patterns) handled as a
|
||||
// deref pattern.
|
||||
&& let &ty::Ref(_, inner_ty, inner_mutability) = pinned_ty.kind() =>
|
||||
{
|
||||
debug!("scrutinee ty {expected:?} is a pinned reference, inserting pin deref");
|
||||
|
||||
// if the inner_ty is an ADT, make sure that it can be structurally pinned
|
||||
// (i.e., it is `#[pin_v2]`).
|
||||
if let Some(adt) = inner_ty.ty_adt_def()
|
||||
&& !adt.is_pin_project()
|
||||
&& !adt.is_pin()
|
||||
{
|
||||
let def_span: Option<Span> = self.tcx.hir_span_if_local(adt.did());
|
||||
let sugg_span = def_span.map(|span| span.shrink_to_lo());
|
||||
self.dcx().emit_err(crate::errors::ProjectOnNonPinProjectType {
|
||||
span: pat.span,
|
||||
def_span,
|
||||
sugg_span,
|
||||
});
|
||||
}
|
||||
|
||||
let binding_mode = adjust_binding_mode(Pinnedness::Pinned, inner_mutability);
|
||||
// If the pinnedness is `Not`, it means the pattern is unpinned
|
||||
// and thus requires an `Unpin` bound.
|
||||
if matches!(binding_mode, ByRef::Yes(Pinnedness::Not, _)) {
|
||||
self.register_bound(
|
||||
inner_ty,
|
||||
self.tcx.require_lang_item(hir::LangItem::Unpin, pat.span),
|
||||
self.misc(pat.span),
|
||||
)
|
||||
}
|
||||
debug!("default binding mode is now {:?}", binding_mode);
|
||||
|
||||
// Use the old pat info to keep `current_depth` to its old value.
|
||||
let new_pat_info = PatInfo { binding_mode, ..old_pat_info };
|
||||
|
||||
self.check_deref_pattern(
|
||||
pat,
|
||||
opt_path_res,
|
||||
adjust_mode,
|
||||
expected,
|
||||
inner_ty,
|
||||
PatAdjust::PinDeref,
|
||||
new_pat_info,
|
||||
)
|
||||
}
|
||||
// If `deref_patterns` is enabled, peel a smart pointer from the scrutinee type. See the
|
||||
// examples in `tests/ui/pattern/deref_patterns/`.
|
||||
_ if self.tcx.features().deref_patterns()
|
||||
|
|
@ -540,35 +601,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
&& pat.default_binding_modes
|
||||
&& self.should_peel_smart_pointer(peel_kind, expected) =>
|
||||
{
|
||||
debug!("scrutinee ty {expected:?} is a smart pointer, inserting overloaded deref");
|
||||
debug!("scrutinee ty {expected:?} is a smart pointer, inserting pin deref");
|
||||
|
||||
// The scrutinee is a smart pointer; implicitly dereference it. This adds a
|
||||
// requirement that `expected: DerefPure`.
|
||||
let mut inner_ty = self.deref_pat_target(pat.span, expected);
|
||||
let inner_ty = self.deref_pat_target(pat.span, expected);
|
||||
// Once we've checked `pat`, we'll add a `DerefMut` bound if it contains any
|
||||
// `ref mut` bindings. See `Self::register_deref_mut_bounds_if_needed`.
|
||||
|
||||
let mut typeck_results = self.typeck_results.borrow_mut();
|
||||
let mut pat_adjustments_table = typeck_results.pat_adjustments_mut();
|
||||
let pat_adjustments = pat_adjustments_table.entry(pat.hir_id).or_default();
|
||||
// We may reach the recursion limit if a user matches on a type `T` satisfying
|
||||
// `T: Deref<Target = T>`; error gracefully in this case.
|
||||
// FIXME(deref_patterns): If `deref_patterns` stabilizes, it may make sense to move
|
||||
// this check out of this branch. Alternatively, this loop could be implemented with
|
||||
// autoderef and this check removed. For now though, don't break code compiling on
|
||||
// stable with lots of `&`s and a low recursion limit, if anyone's done that.
|
||||
if self.tcx.recursion_limit().value_within_limit(pat_adjustments.len()) {
|
||||
// Preserve the smart pointer type for THIR lowering and closure upvar analysis.
|
||||
pat_adjustments
|
||||
.push(PatAdjustment { kind: PatAdjust::OverloadedDeref, source: expected });
|
||||
} else {
|
||||
let guar = report_autoderef_recursion_limit_error(self.tcx, pat.span, expected);
|
||||
inner_ty = Ty::new_error(self.tcx, guar);
|
||||
}
|
||||
drop(typeck_results);
|
||||
|
||||
// Recurse, using the old pat info to keep `current_depth` to its old value.
|
||||
// Peeling smart pointers does not update the default binding mode.
|
||||
self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, old_pat_info)
|
||||
self.check_deref_pattern(
|
||||
pat,
|
||||
opt_path_res,
|
||||
adjust_mode,
|
||||
expected,
|
||||
inner_ty,
|
||||
PatAdjust::OverloadedDeref,
|
||||
old_pat_info,
|
||||
)
|
||||
}
|
||||
PatKind::Missing | PatKind::Wild | PatKind::Err(_) => expected,
|
||||
// We allow any type here; we ensure that the type is uninhabited during match checking.
|
||||
|
|
@ -647,6 +696,44 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_deref_pattern(
|
||||
&self,
|
||||
pat: &'tcx Pat<'tcx>,
|
||||
opt_path_res: Option<Result<ResolvedPat<'tcx>, ErrorGuaranteed>>,
|
||||
adjust_mode: AdjustMode,
|
||||
expected: Ty<'tcx>,
|
||||
mut inner_ty: Ty<'tcx>,
|
||||
pat_adjust_kind: PatAdjust,
|
||||
pat_info: PatInfo<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
debug_assert!(
|
||||
!matches!(pat_adjust_kind, PatAdjust::BuiltinDeref),
|
||||
"unexpected deref pattern for builtin reference type {expected:?}",
|
||||
);
|
||||
|
||||
let mut typeck_results = self.typeck_results.borrow_mut();
|
||||
let mut pat_adjustments_table = typeck_results.pat_adjustments_mut();
|
||||
let pat_adjustments = pat_adjustments_table.entry(pat.hir_id).or_default();
|
||||
// We may reach the recursion limit if a user matches on a type `T` satisfying
|
||||
// `T: Deref<Target = T>`; error gracefully in this case.
|
||||
// FIXME(deref_patterns): If `deref_patterns` stabilizes, it may make sense to move
|
||||
// this check out of this branch. Alternatively, this loop could be implemented with
|
||||
// autoderef and this check removed. For now though, don't break code compiling on
|
||||
// stable with lots of `&`s and a low recursion limit, if anyone's done that.
|
||||
if self.tcx.recursion_limit().value_within_limit(pat_adjustments.len()) {
|
||||
// Preserve the smart pointer type for THIR lowering and closure upvar analysis.
|
||||
pat_adjustments.push(PatAdjustment { kind: pat_adjust_kind, source: expected });
|
||||
} else {
|
||||
let guar = report_autoderef_recursion_limit_error(self.tcx, pat.span, expected);
|
||||
inner_ty = Ty::new_error(self.tcx, guar);
|
||||
}
|
||||
drop(typeck_results);
|
||||
|
||||
// Recurse, using the old pat info to keep `current_depth` to its old value.
|
||||
// Peeling smart pointers does not update the default binding mode.
|
||||
self.check_pat_inner(pat, opt_path_res, adjust_mode, inner_ty, pat_info)
|
||||
}
|
||||
|
||||
/// How should the binding mode and expected type be adjusted?
|
||||
///
|
||||
/// When the pattern contains a path, `opt_path_res` must be `Some(path_res)`.
|
||||
|
|
@ -1061,7 +1148,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
// Determine the binding mode...
|
||||
let bm = match user_bind_annot {
|
||||
BindingMode(ByRef::No, Mutability::Mut) if let ByRef::Yes(def_br_mutbl) = def_br => {
|
||||
BindingMode(ByRef::No, Mutability::Mut) if let ByRef::Yes(_, def_br_mutbl) = def_br => {
|
||||
// Only mention the experimental `mut_ref` feature if if we're in edition 2024 and
|
||||
// using other experimental matching features compatible with it.
|
||||
if pat.span.at_least_rust_2024()
|
||||
|
|
@ -1091,8 +1178,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl),
|
||||
BindingMode(ByRef::Yes(user_br_mutbl), _) => {
|
||||
if let ByRef::Yes(def_br_mutbl) = def_br {
|
||||
BindingMode(ByRef::Yes(_, user_br_mutbl), _) => {
|
||||
if let ByRef::Yes(_, def_br_mutbl) = def_br {
|
||||
// `ref`/`ref mut` overrides the binding mode on edition <= 2021
|
||||
self.add_rust_2024_migration_desugared_pat(
|
||||
pat_info.top_info.hir_id,
|
||||
|
|
@ -1108,7 +1195,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
};
|
||||
|
||||
if bm.0 == ByRef::Yes(Mutability::Mut)
|
||||
if matches!(bm.0, ByRef::Yes(_, Mutability::Mut))
|
||||
&& let MutblCap::WeaklyNot(and_pat_span) = pat_info.max_ref_mutbl
|
||||
{
|
||||
let mut err = struct_span_code_err!(
|
||||
|
|
@ -1136,7 +1223,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
let local_ty = self.local_ty(pat.span, pat.hir_id);
|
||||
let eq_ty = match bm.0 {
|
||||
ByRef::Yes(mutbl) => {
|
||||
ByRef::Yes(Pinnedness::Not, mutbl) => {
|
||||
// If the binding is like `ref x | ref mut x`,
|
||||
// then `x` is assigned a value of type `&M T` where M is the
|
||||
// mutability and T is the expected type.
|
||||
|
|
@ -1146,6 +1233,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// See (note_1) for an explanation.
|
||||
self.new_ref_ty(pat.span, mutbl, expected)
|
||||
}
|
||||
// Wrapping the type into `Pin` if the binding is like `ref pin const|mut x`
|
||||
ByRef::Yes(Pinnedness::Pinned, mutbl) => Ty::new_adt(
|
||||
self.tcx,
|
||||
self.tcx.adt_def(self.tcx.require_lang_item(hir::LangItem::Pin, pat.span)),
|
||||
self.tcx.mk_args(&[self.new_ref_ty(pat.span, mutbl, expected).into()]),
|
||||
),
|
||||
// Otherwise, the type of x is the expected type `T`.
|
||||
ByRef::No => expected, // As above, `T <: typeof(x)` is required, but we use equality, see (note_1).
|
||||
};
|
||||
|
|
@ -2605,7 +2698,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
expected = self.try_structurally_resolve_type(pat.span, expected);
|
||||
// Determine whether we're consuming an inherited reference and resetting the default
|
||||
// binding mode, based on edition and enabled experimental features.
|
||||
if let ByRef::Yes(inh_mut) = pat_info.binding_mode {
|
||||
if let ByRef::Yes(inh_pin, inh_mut) = pat_info.binding_mode
|
||||
// FIXME(pin_ergonomics): since `&pin` pattern is supported, the condition here
|
||||
// should be adjusted to `pat_pin == inh_pin`
|
||||
&& (!self.tcx.features().pin_ergonomics() || inh_pin == Pinnedness::Not)
|
||||
{
|
||||
match self.ref_pat_matches_inherited_ref(pat.span.edition()) {
|
||||
InheritedRefMatchRule::EatOuter => {
|
||||
// ref pattern attempts to consume inherited reference
|
||||
|
|
@ -3126,8 +3223,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// If the user-provided binding modifier doesn't match the default binding mode, we'll
|
||||
// need to suggest reference patterns, which can affect other bindings.
|
||||
// For simplicity, we opt to suggest making the pattern fully explicit.
|
||||
info.suggest_eliding_modes &=
|
||||
user_bind_annot == BindingMode(ByRef::Yes(def_br_mutbl), Mutability::Not);
|
||||
info.suggest_eliding_modes &= matches!(
|
||||
user_bind_annot,
|
||||
BindingMode(ByRef::Yes(_, mutbl), Mutability::Not) if mutbl == def_br_mutbl
|
||||
);
|
||||
if user_bind_annot == BindingMode(ByRef::No, Mutability::Mut) {
|
||||
info.bad_mut_modifiers = true;
|
||||
"`mut` binding modifier"
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use std::{env, thread};
|
|||
|
||||
use rustc_ast as ast;
|
||||
use rustc_attr_parsing::{ShouldEmit, validate_attr};
|
||||
use rustc_codegen_ssa::back::archive::ArArchiveBuilderBuilder;
|
||||
use rustc_codegen_ssa::back::archive::{ArArchiveBuilderBuilder, ArchiveBuilderBuilder};
|
||||
use rustc_codegen_ssa::back::link::link_binary;
|
||||
use rustc_codegen_ssa::target_features::{self, cfg_target_feature};
|
||||
use rustc_codegen_ssa::traits::CodegenBackend;
|
||||
|
|
@ -440,7 +440,7 @@ impl CodegenBackend for DummyCodegenBackend {
|
|||
|
||||
link_binary(
|
||||
sess,
|
||||
&ArArchiveBuilderBuilder,
|
||||
&DummyArchiveBuilderBuilder,
|
||||
codegen_results,
|
||||
metadata,
|
||||
outputs,
|
||||
|
|
@ -449,6 +449,28 @@ impl CodegenBackend for DummyCodegenBackend {
|
|||
}
|
||||
}
|
||||
|
||||
struct DummyArchiveBuilderBuilder;
|
||||
|
||||
impl ArchiveBuilderBuilder for DummyArchiveBuilderBuilder {
|
||||
fn new_archive_builder<'a>(
|
||||
&self,
|
||||
sess: &'a Session,
|
||||
) -> Box<dyn rustc_codegen_ssa::back::archive::ArchiveBuilder + 'a> {
|
||||
ArArchiveBuilderBuilder.new_archive_builder(sess)
|
||||
}
|
||||
|
||||
fn create_dll_import_lib(
|
||||
&self,
|
||||
sess: &Session,
|
||||
_lib_name: &str,
|
||||
_items: Vec<rustc_codegen_ssa::back::archive::ImportLibraryItem>,
|
||||
output_path: &Path,
|
||||
) {
|
||||
// Build an empty static library to avoid calling an external dlltool on mingw
|
||||
ArArchiveBuilderBuilder.new_archive_builder(sess).build(output_path);
|
||||
}
|
||||
}
|
||||
|
||||
// This is used for rustdoc, but it uses similar machinery to codegen backend
|
||||
// loading, so we leave the code here. It is potentially useful for other tools
|
||||
// that want to invoke the rustc binary while linking to rustc as well.
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for StaticMutRefs {
|
|||
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &Stmt<'_>) {
|
||||
if let hir::StmtKind::Let(loc) = stmt.kind
|
||||
&& let hir::PatKind::Binding(ba, _, _, _) = loc.pat.kind
|
||||
&& let hir::ByRef::Yes(m) = ba.0
|
||||
&& let hir::ByRef::Yes(_, m) = ba.0
|
||||
&& let Some(init) = loc.init
|
||||
&& let Some(err_span) = path_is_static_mut(init, init.span)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -232,4 +232,7 @@ pub enum PatAdjust {
|
|||
/// An implicit call to `Deref(Mut)::deref(_mut)` before matching, such as when matching the
|
||||
/// pattern `[..]` against a scrutinee of type `Vec<T>`.
|
||||
OverloadedDeref,
|
||||
/// An implicit dereference before matching a `&pin` reference (under feature `pin_ergonomics`),
|
||||
/// which will be lowered as a builtin deref of the private field `__pointer` in `Pin`
|
||||
PinDeref,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,6 +55,10 @@ bitflags::bitflags! {
|
|||
const IS_UNSAFE_CELL = 1 << 9;
|
||||
/// Indicates whether the type is `UnsafePinned`.
|
||||
const IS_UNSAFE_PINNED = 1 << 10;
|
||||
/// Indicates whether the type is `Pin`.
|
||||
const IS_PIN = 1 << 11;
|
||||
/// Indicates whether the type is `#[pin_project]`.
|
||||
const IS_PIN_PROJECT = 1 << 12;
|
||||
}
|
||||
}
|
||||
rustc_data_structures::external_bitflags_debug! { AdtFlags }
|
||||
|
|
@ -284,6 +288,10 @@ impl AdtDefData {
|
|||
debug!("found non-exhaustive variant list for {:?}", did);
|
||||
flags = flags | AdtFlags::IS_VARIANT_LIST_NON_EXHAUSTIVE;
|
||||
}
|
||||
if find_attr!(tcx.get_all_attrs(did), AttributeKind::PinV2(..)) {
|
||||
debug!("found pin-project type {:?}", did);
|
||||
flags |= AdtFlags::IS_PIN_PROJECT;
|
||||
}
|
||||
|
||||
flags |= match kind {
|
||||
AdtKind::Enum => AdtFlags::IS_ENUM,
|
||||
|
|
@ -313,6 +321,9 @@ impl AdtDefData {
|
|||
if tcx.is_lang_item(did, LangItem::UnsafePinned) {
|
||||
flags |= AdtFlags::IS_UNSAFE_PINNED;
|
||||
}
|
||||
if tcx.is_lang_item(did, LangItem::Pin) {
|
||||
flags |= AdtFlags::IS_PIN;
|
||||
}
|
||||
|
||||
AdtDefData { did, variants, flags, repr }
|
||||
}
|
||||
|
|
@ -428,6 +439,19 @@ impl<'tcx> AdtDef<'tcx> {
|
|||
self.flags().contains(AdtFlags::IS_MANUALLY_DROP)
|
||||
}
|
||||
|
||||
/// Returns `true` if this is `Pin<T>`.
|
||||
#[inline]
|
||||
pub fn is_pin(self) -> bool {
|
||||
self.flags().contains(AdtFlags::IS_PIN)
|
||||
}
|
||||
|
||||
/// Returns `true` is this is `#[pin_v2]` for the purposes
|
||||
/// of structural pinning.
|
||||
#[inline]
|
||||
pub fn is_pin_project(self) -> bool {
|
||||
self.flags().contains(AdtFlags::IS_PIN_PROJECT)
|
||||
}
|
||||
|
||||
/// Returns `true` if this type has a destructor.
|
||||
pub fn has_dtor(self, tcx: TyCtxt<'tcx>) -> bool {
|
||||
self.destructor(tcx).is_some()
|
||||
|
|
|
|||
|
|
@ -1349,6 +1349,23 @@ impl<'tcx> Ty<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn pinned_ty(self) -> Option<Ty<'tcx>> {
|
||||
match self.kind() {
|
||||
Adt(def, args) if def.is_pin() => Some(args.type_at(0)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pinned_ref(self) -> Option<(Ty<'tcx>, ty::Mutability)> {
|
||||
if let Adt(def, args) = self.kind()
|
||||
&& def.is_pin()
|
||||
&& let &ty::Ref(_, ty, mutbl) = args.type_at(0).kind()
|
||||
{
|
||||
return Some((ty, mutbl));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Panics if called on any type other than `Box<T>`.
|
||||
pub fn expect_boxed_ty(self) -> Ty<'tcx> {
|
||||
self.boxed_ty()
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdMap};
|
|||
use rustc_hir::hir_id::OwnerId;
|
||||
use rustc_hir::{
|
||||
self as hir, BindingMode, ByRef, HirId, ItemLocalId, ItemLocalMap, ItemLocalSet, Mutability,
|
||||
Pinnedness,
|
||||
};
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_macros::{HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable};
|
||||
|
|
@ -479,7 +480,7 @@ impl<'tcx> TypeckResults<'tcx> {
|
|||
let mut has_ref_mut = false;
|
||||
pat.walk(|pat| {
|
||||
if let hir::PatKind::Binding(_, id, _, _) = pat.kind
|
||||
&& let Some(BindingMode(ByRef::Yes(Mutability::Mut), _)) =
|
||||
&& let Some(BindingMode(ByRef::Yes(_, Mutability::Mut), _)) =
|
||||
self.pat_binding_modes().get(id)
|
||||
{
|
||||
has_ref_mut = true;
|
||||
|
|
@ -503,7 +504,7 @@ impl<'tcx> TypeckResults<'tcx> {
|
|||
ByRef::No
|
||||
} else {
|
||||
let mutable = self.pat_has_ref_mut_binding(inner);
|
||||
ByRef::Yes(if mutable { Mutability::Mut } else { Mutability::Not })
|
||||
ByRef::Yes(Pinnedness::Not, if mutable { Mutability::Mut } else { Mutability::Not })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use rustc_abi::FieldIdx;
|
||||
use rustc_hir::ByRef;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::thir::*;
|
||||
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
|
||||
use rustc_middle::ty::{self, Pinnedness, Ty, TypeVisitableExt};
|
||||
|
||||
use crate::builder::Builder;
|
||||
use crate::builder::expr::as_place::{PlaceBase, PlaceBuilder};
|
||||
|
|
@ -299,7 +300,24 @@ impl<'tcx> MatchPairTree<'tcx> {
|
|||
None
|
||||
}
|
||||
|
||||
PatKind::DerefPattern { ref subpattern, borrow: ByRef::Yes(mutability) } => {
|
||||
PatKind::DerefPattern { ref subpattern, borrow: ByRef::Yes(Pinnedness::Pinned, _) } => {
|
||||
let Some(ref_ty) = pattern.ty.pinned_ty() else {
|
||||
rustc_middle::bug!("RefPin pattern on non-`Pin` type {:?}", pattern.ty);
|
||||
};
|
||||
MatchPairTree::for_pattern(
|
||||
place_builder.field(FieldIdx::ZERO, ref_ty).deref(),
|
||||
subpattern,
|
||||
cx,
|
||||
&mut subpairs,
|
||||
extra_data,
|
||||
);
|
||||
None
|
||||
}
|
||||
|
||||
PatKind::DerefPattern {
|
||||
ref subpattern,
|
||||
borrow: ByRef::Yes(Pinnedness::Not, mutability),
|
||||
} => {
|
||||
// Create a new temporary for each deref pattern.
|
||||
// FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls?
|
||||
let temp = cx.temp(
|
||||
|
|
|
|||
|
|
@ -5,20 +5,21 @@
|
|||
//! This also includes code for pattern bindings in `let` statements and
|
||||
//! function parameters.
|
||||
|
||||
use std::assert_matches::debug_assert_matches;
|
||||
use std::borrow::Borrow;
|
||||
use std::mem;
|
||||
use std::sync::Arc;
|
||||
|
||||
use itertools::{Itertools, Position};
|
||||
use rustc_abi::VariantIdx;
|
||||
use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
use rustc_hir::{BindingMode, ByRef, LetStmt, LocalSource, Node};
|
||||
use rustc_middle::bug;
|
||||
use rustc_hir::{BindingMode, ByRef, LangItem, LetStmt, LocalSource, Node, Pinnedness};
|
||||
use rustc_middle::middle::region;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::thir::{self, *};
|
||||
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation, Ty, ValTree, ValTreeKind};
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use rustc_pattern_analysis::constructor::RangeEnd;
|
||||
use rustc_pattern_analysis::rustc::{DeconstructedPat, RustcPatCtxt};
|
||||
use rustc_span::{BytePos, Pos, Span, Symbol, sym};
|
||||
|
|
@ -917,6 +918,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
visit_subpat(self, subpattern, &user_tys.deref(), f);
|
||||
}
|
||||
|
||||
PatKind::DerefPattern { ref subpattern, borrow: ByRef::Yes(Pinnedness::Pinned, _) } => {
|
||||
visit_subpat(self, subpattern, &user_tys.leaf(FieldIdx::ZERO).deref(), f);
|
||||
}
|
||||
|
||||
PatKind::DerefPattern { ref subpattern, .. } => {
|
||||
visit_subpat(self, subpattern, &ProjectedUserTypesNode::None, f);
|
||||
}
|
||||
|
|
@ -2747,9 +2752,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, binding.source);
|
||||
self.cfg.push_assign(block, source_info, ref_for_guard, rvalue);
|
||||
}
|
||||
ByRef::Yes(mutbl) => {
|
||||
// The arm binding will be by reference, so eagerly create it now. Drops must
|
||||
// be scheduled to emit `StorageDead` on the guard's failure/break branches.
|
||||
ByRef::Yes(pinnedness, mutbl) => {
|
||||
// The arm binding will be by reference, so eagerly create it now // be scheduled to emit `StorageDead` on the guard's failure/break branches.
|
||||
let value_for_arm = self.storage_live_binding(
|
||||
block,
|
||||
binding.var_id,
|
||||
|
|
@ -2761,6 +2765,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
let rvalue =
|
||||
Rvalue::Ref(re_erased, util::ref_pat_borrow_kind(mutbl), binding.source);
|
||||
let rvalue = match pinnedness {
|
||||
ty::Pinnedness::Not => rvalue,
|
||||
ty::Pinnedness::Pinned => {
|
||||
self.pin_borrowed_local(block, value_for_arm.local, rvalue, source_info)
|
||||
}
|
||||
};
|
||||
self.cfg.push_assign(block, source_info, value_for_arm, rvalue);
|
||||
// For the guard binding, take a shared reference to that reference.
|
||||
let rvalue = Rvalue::Ref(re_erased, BorrowKind::Shared, value_for_arm);
|
||||
|
|
@ -2797,14 +2807,59 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}
|
||||
let rvalue = match binding.binding_mode.0 {
|
||||
ByRef::No => Rvalue::Use(self.consume_by_copy_or_move(binding.source)),
|
||||
ByRef::Yes(mutbl) => {
|
||||
Rvalue::Ref(re_erased, util::ref_pat_borrow_kind(mutbl), binding.source)
|
||||
ByRef::Yes(pinnedness, mutbl) => {
|
||||
let rvalue =
|
||||
Rvalue::Ref(re_erased, util::ref_pat_borrow_kind(mutbl), binding.source);
|
||||
match pinnedness {
|
||||
ty::Pinnedness::Not => rvalue,
|
||||
ty::Pinnedness::Pinned => {
|
||||
self.pin_borrowed_local(block, local.local, rvalue, source_info)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
self.cfg.push_assign(block, source_info, local, rvalue);
|
||||
}
|
||||
}
|
||||
|
||||
/// Given an rvalue `&[mut]borrow` and a local `local`, generate the pinned borrow for it:
|
||||
/// ```ignore (illustrative)
|
||||
/// pinned_temp = &borrow;
|
||||
/// local = Pin { __pointer: move pinned_temp };
|
||||
/// ```
|
||||
fn pin_borrowed_local(
|
||||
&mut self,
|
||||
block: BasicBlock,
|
||||
local: Local,
|
||||
borrow: Rvalue<'tcx>,
|
||||
source_info: SourceInfo,
|
||||
) -> Rvalue<'tcx> {
|
||||
debug_assert_matches!(borrow, Rvalue::Ref(..));
|
||||
|
||||
let local_ty = self.local_decls[local].ty;
|
||||
|
||||
let pinned_ty = local_ty.pinned_ty().unwrap_or_else(|| {
|
||||
span_bug!(
|
||||
source_info.span,
|
||||
"expect type `Pin` for a pinned binding, found type {:?}",
|
||||
local_ty
|
||||
)
|
||||
});
|
||||
let pinned_temp =
|
||||
Place::from(self.local_decls.push(LocalDecl::new(pinned_ty, source_info.span)));
|
||||
self.cfg.push_assign(block, source_info, pinned_temp, borrow);
|
||||
Rvalue::Aggregate(
|
||||
Box::new(AggregateKind::Adt(
|
||||
self.tcx.require_lang_item(LangItem::Pin, source_info.span),
|
||||
FIRST_VARIANT,
|
||||
self.tcx.mk_args(&[pinned_ty.into()]),
|
||||
None,
|
||||
None,
|
||||
)),
|
||||
std::iter::once(Operand::Move(pinned_temp)).collect(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Each binding (`ref mut var`/`ref var`/`mut var`/`var`, where the bound
|
||||
/// `var` has type `T` in the arm body) in a pattern maps to 2 locals. The
|
||||
/// first local is a binding for occurrences of `var` in the guard, which
|
||||
|
|
|
|||
|
|
@ -383,7 +383,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
|
|||
}
|
||||
visit::walk_pat(self, pat);
|
||||
}
|
||||
PatKind::Binding { mode: BindingMode(ByRef::Yes(rm), _), ty, .. } => {
|
||||
PatKind::Binding { mode: BindingMode(ByRef::Yes(_, rm), _), ty, .. } => {
|
||||
if self.inside_adt {
|
||||
let ty::Ref(_, ty, _) = ty.kind() else {
|
||||
span_bug!(
|
||||
|
|
|
|||
|
|
@ -797,7 +797,7 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat:
|
|||
// We have `x @ pat` where `x` is by-move. Reject all borrows in `pat`.
|
||||
let mut conflicts_ref = Vec::new();
|
||||
sub.each_binding(|_, mode, _, span| {
|
||||
if matches!(mode, ByRef::Yes(_)) {
|
||||
if matches!(mode, ByRef::Yes(..)) {
|
||||
conflicts_ref.push(span)
|
||||
}
|
||||
});
|
||||
|
|
@ -813,7 +813,7 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat:
|
|||
return;
|
||||
}
|
||||
ByRef::No => return,
|
||||
ByRef::Yes(m) => m,
|
||||
ByRef::Yes(_, m) => m,
|
||||
};
|
||||
|
||||
// We now have `ref $mut_outer binding @ sub` (semantically).
|
||||
|
|
@ -823,7 +823,7 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat:
|
|||
let mut conflicts_mut_ref = Vec::new();
|
||||
sub.each_binding(|name, mode, ty, span| {
|
||||
match mode {
|
||||
ByRef::Yes(mut_inner) => match (mut_outer, mut_inner) {
|
||||
ByRef::Yes(_, mut_inner) => match (mut_outer, mut_inner) {
|
||||
// Both sides are `ref`.
|
||||
(Mutability::Not, Mutability::Not) => {}
|
||||
// 2x `ref mut`.
|
||||
|
|
|
|||
|
|
@ -212,7 +212,7 @@ impl<'a> PatMigration<'a> {
|
|||
}
|
||||
if !self.info.suggest_eliding_modes
|
||||
&& explicit_ba.0 == ByRef::No
|
||||
&& let ByRef::Yes(mutbl) = mode.0
|
||||
&& let ByRef::Yes(_, mutbl) = mode.0
|
||||
{
|
||||
// If we can't fix the pattern by eliding modifiers, we'll need to make the pattern
|
||||
// fully explicit. i.e. we'll need to suggest reference patterns for this.
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use rustc_abi::{FieldIdx, Integer};
|
|||
use rustc_errors::codes::*;
|
||||
use rustc_hir::def::{CtorOf, DefKind, Res};
|
||||
use rustc_hir::pat_util::EnumerateAndAdjustIterator;
|
||||
use rustc_hir::{self as hir, LangItem, RangeEnd};
|
||||
use rustc_hir::{self as hir, ByRef, LangItem, Mutability, Pinnedness, RangeEnd};
|
||||
use rustc_index::Idx;
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_middle::mir::interpret::LitToConstInput;
|
||||
|
|
@ -114,6 +114,16 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
let borrow = self.typeck_results.deref_pat_borrow_mode(adjust.source, pat);
|
||||
PatKind::DerefPattern { subpattern: thir_pat, borrow }
|
||||
}
|
||||
PatAdjust::PinDeref => {
|
||||
let mutable = self.typeck_results.pat_has_ref_mut_binding(pat);
|
||||
PatKind::DerefPattern {
|
||||
subpattern: thir_pat,
|
||||
borrow: ByRef::Yes(
|
||||
Pinnedness::Pinned,
|
||||
if mutable { Mutability::Mut } else { Mutability::Not },
|
||||
),
|
||||
}
|
||||
}
|
||||
};
|
||||
Box::new(Pat { span, ty: adjust.source, kind })
|
||||
});
|
||||
|
|
@ -354,11 +364,22 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
|||
// A ref x pattern is the same node used for x, and as such it has
|
||||
// x's type, which is &T, where we want T (the type being matched).
|
||||
let var_ty = ty;
|
||||
if let hir::ByRef::Yes(_) = mode.0 {
|
||||
if let ty::Ref(_, rty, _) = ty.kind() {
|
||||
ty = *rty;
|
||||
} else {
|
||||
bug!("`ref {}` has wrong type {}", ident, ty);
|
||||
if let hir::ByRef::Yes(pinnedness, _) = mode.0 {
|
||||
match pinnedness {
|
||||
hir::Pinnedness::Pinned
|
||||
if let Some(pty) = ty.pinned_ty()
|
||||
&& let &ty::Ref(_, rty, _) = pty.kind() =>
|
||||
{
|
||||
debug_assert!(
|
||||
self.tcx.features().pin_ergonomics(),
|
||||
"`pin_ergonomics` must be enabled to have a by-pin-ref binding"
|
||||
);
|
||||
ty = rty;
|
||||
}
|
||||
hir::Pinnedness::Not if let &ty::Ref(_, rty, _) = ty.kind() => {
|
||||
ty = rty;
|
||||
}
|
||||
_ => bug!("`ref {}` has wrong type {}", ident, ty),
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ use rustc_ast::tokenstream::{
|
|||
use rustc_ast::util::case::Case;
|
||||
use rustc_ast::{
|
||||
self as ast, AnonConst, AttrArgs, AttrId, ByRef, Const, CoroutineKind, DUMMY_NODE_ID,
|
||||
DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, Recovered, Safety, StrLit,
|
||||
Visibility, VisibilityKind,
|
||||
DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, Mutability, Pinnedness, Recovered,
|
||||
Safety, StrLit, Visibility, VisibilityKind,
|
||||
};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
|
|
@ -1317,7 +1317,12 @@ impl<'a> Parser<'a> {
|
|||
|
||||
/// Parses reference binding mode (`ref`, `ref mut`, or nothing).
|
||||
fn parse_byref(&mut self) -> ByRef {
|
||||
if self.eat_keyword(exp!(Ref)) { ByRef::Yes(self.parse_mutability()) } else { ByRef::No }
|
||||
if self.eat_keyword(exp!(Ref)) {
|
||||
// FIXME(pin_ergonomics): support `ref pin const|mut` bindings
|
||||
ByRef::Yes(Pinnedness::Not, self.parse_mutability())
|
||||
} else {
|
||||
ByRef::No
|
||||
}
|
||||
}
|
||||
|
||||
/// Possibly parses mutability (`const` or `mut`).
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ use rustc_ast::util::parser::ExprPrecedence;
|
|||
use rustc_ast::visit::{self, Visitor};
|
||||
use rustc_ast::{
|
||||
self as ast, Arm, AttrVec, BindingMode, ByRef, Expr, ExprKind, LocalKind, MacCall, Mutability,
|
||||
Pat, PatField, PatFieldsRest, PatKind, Path, QSelf, RangeEnd, RangeSyntax, Stmt, StmtKind,
|
||||
Pat, PatField, PatFieldsRest, PatKind, Path, Pinnedness, QSelf, RangeEnd, RangeSyntax, Stmt,
|
||||
StmtKind,
|
||||
};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_errors::{Applicability, Diag, DiagArgValue, PResult, StashKey};
|
||||
|
|
@ -778,7 +779,11 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
// Parse ref ident @ pat / ref mut ident @ pat
|
||||
let mutbl = self.parse_mutability();
|
||||
self.parse_pat_ident(BindingMode(ByRef::Yes(mutbl), Mutability::Not), syntax_loc)?
|
||||
self.parse_pat_ident(
|
||||
// FIXME(pin_ergonomics): support `ref pin const|mut` bindings
|
||||
BindingMode(ByRef::Yes(Pinnedness::Not, mutbl), Mutability::Not),
|
||||
syntax_loc,
|
||||
)?
|
||||
} else if self.eat_keyword(exp!(Box)) {
|
||||
self.parse_pat_box()?
|
||||
} else if self.check_inline_const(0) {
|
||||
|
|
@ -1093,7 +1098,7 @@ impl<'a> Parser<'a> {
|
|||
self.ban_mut_general_pat(mut_span, &pat, changed_any_binding);
|
||||
}
|
||||
|
||||
if matches!(pat.kind, PatKind::Ident(BindingMode(ByRef::Yes(_), Mutability::Mut), ..)) {
|
||||
if matches!(pat.kind, PatKind::Ident(BindingMode(ByRef::Yes(..), Mutability::Mut), ..)) {
|
||||
self.psess.gated_spans.gate(sym::mut_ref, pat.span);
|
||||
}
|
||||
Ok(pat.kind)
|
||||
|
|
|
|||
|
|
@ -283,7 +283,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
| AttributeKind::ObjcSelector { .. }
|
||||
| AttributeKind::RustcCoherenceIsCore(..)
|
||||
| AttributeKind::DebuggerVisualizer(..)
|
||||
| AttributeKind::RustcMain,
|
||||
| AttributeKind::RustcMain
|
||||
| AttributeKind::PinV2(..),
|
||||
) => { /* do nothing */ }
|
||||
Attribute::Unparsed(attr_item) => {
|
||||
style = Some(attr_item.style);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
|
|||
use rustc_data_structures::unord::{UnordMap, UnordSet};
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_errors::{
|
||||
Applicability, DiagArgValue, ErrorGuaranteed, IntoDiagArg, StashKey, Suggestions,
|
||||
Applicability, Diag, DiagArgValue, ErrorGuaranteed, IntoDiagArg, StashKey, Suggestions,
|
||||
pluralize,
|
||||
};
|
||||
use rustc_hir::def::Namespace::{self, *};
|
||||
use rustc_hir::def::{self, CtorKind, DefKind, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS};
|
||||
|
|
@ -377,6 +378,7 @@ enum LifetimeBinderKind {
|
|||
Function,
|
||||
Closure,
|
||||
ImplBlock,
|
||||
ImplAssocType,
|
||||
}
|
||||
|
||||
impl LifetimeBinderKind {
|
||||
|
|
@ -387,6 +389,7 @@ impl LifetimeBinderKind {
|
|||
PolyTrait => "bound",
|
||||
WhereBound => "bound",
|
||||
Item | ConstItem => "item",
|
||||
ImplAssocType => "associated type",
|
||||
ImplBlock => "impl block",
|
||||
Function => "function",
|
||||
Closure => "closure",
|
||||
|
|
@ -1874,9 +1877,13 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
ty: ty.span,
|
||||
});
|
||||
} else {
|
||||
self.r.dcx().emit_err(errors::AnonymousLifetimeNonGatReportError {
|
||||
lifetime: lifetime.ident.span,
|
||||
});
|
||||
let mut err = self.r.dcx().create_err(
|
||||
errors::AnonymousLifetimeNonGatReportError {
|
||||
lifetime: lifetime.ident.span,
|
||||
},
|
||||
);
|
||||
self.point_at_impl_lifetimes(&mut err, i, lifetime.ident.span);
|
||||
err.emit();
|
||||
}
|
||||
} else {
|
||||
self.r.dcx().emit_err(errors::ElidedAnonymousLifetimeReportError {
|
||||
|
|
@ -1913,6 +1920,47 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
self.report_missing_lifetime_specifiers(vec![missing_lifetime], None);
|
||||
}
|
||||
|
||||
fn point_at_impl_lifetimes(&mut self, err: &mut Diag<'_>, i: usize, lifetime: Span) {
|
||||
let Some((rib, span)) = self.lifetime_ribs[..i]
|
||||
.iter()
|
||||
.rev()
|
||||
.skip(1)
|
||||
.filter_map(|rib| match rib.kind {
|
||||
LifetimeRibKind::Generics { span, kind: LifetimeBinderKind::ImplBlock, .. } => {
|
||||
Some((rib, span))
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.next()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
if !rib.bindings.is_empty() {
|
||||
err.span_label(
|
||||
span,
|
||||
format!(
|
||||
"there {} named lifetime{} specified on the impl block you could use",
|
||||
if rib.bindings.len() == 1 { "is a" } else { "are" },
|
||||
pluralize!(rib.bindings.len()),
|
||||
),
|
||||
);
|
||||
if rib.bindings.len() == 1 {
|
||||
err.span_suggestion_verbose(
|
||||
lifetime.shrink_to_hi(),
|
||||
"consider using the lifetime from the impl block",
|
||||
format!("{} ", rib.bindings.keys().next().unwrap()),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
err.span_label(
|
||||
span,
|
||||
"you could add a lifetime on the impl block, if the trait or the self type can \
|
||||
have one",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
fn resolve_elided_lifetime(&mut self, anchor_id: NodeId, span: Span) {
|
||||
let id = self.r.next_node_id();
|
||||
|
|
@ -3352,7 +3400,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
|
|||
&generics.params,
|
||||
RibKind::AssocItem,
|
||||
item.id,
|
||||
LifetimeBinderKind::Item,
|
||||
LifetimeBinderKind::ImplAssocType,
|
||||
generics.span,
|
||||
|this| {
|
||||
this.with_lifetime_rib(LifetimeRibKind::AnonymousReportError, |this| {
|
||||
|
|
|
|||
|
|
@ -3178,6 +3178,9 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
|
|||
{
|
||||
continue;
|
||||
}
|
||||
if let LifetimeBinderKind::ImplAssocType = kind {
|
||||
continue;
|
||||
}
|
||||
|
||||
if !span.can_be_used_for_suggestions()
|
||||
&& suggest_note
|
||||
|
|
|
|||
|
|
@ -27,10 +27,8 @@ tracing = "0.1"
|
|||
# tidy-alphabetical-end
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
# FIXME: Remove this pin once this rustix issue is resolved
|
||||
# https://github.com/bytecodealliance/rustix/issues/1496
|
||||
# tidy-alphabetical-start
|
||||
libc = "=0.2.174"
|
||||
libc = "0.2"
|
||||
# tidy-alphabetical-end
|
||||
|
||||
[target.'cfg(windows)'.dependencies.windows]
|
||||
|
|
|
|||
|
|
@ -1668,6 +1668,7 @@ symbols! {
|
|||
pin,
|
||||
pin_ergonomics,
|
||||
pin_macro,
|
||||
pin_v2,
|
||||
platform_intrinsics,
|
||||
plugin,
|
||||
plugin_registrar,
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ pub(crate) fn target() -> Target {
|
|||
Target {
|
||||
llvm_target: "aarch64-unknown-linux-musl".into(),
|
||||
metadata: TargetMetadata {
|
||||
description: Some("ARM64 Linux with musl 1.2.3".into()),
|
||||
description: Some("ARM64 Linux with musl 1.2.5".into()),
|
||||
tier: Some(2),
|
||||
host_tools: Some(true),
|
||||
std: Some(true),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ pub(crate) fn target() -> Target {
|
|||
Target {
|
||||
llvm_target: "arm-unknown-linux-musleabi".into(),
|
||||
metadata: TargetMetadata {
|
||||
description: Some("Armv6 Linux with musl 1.2.3".into()),
|
||||
description: Some("Armv6 Linux with musl 1.2.5".into()),
|
||||
tier: Some(2),
|
||||
host_tools: Some(false),
|
||||
std: Some(true),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ pub(crate) fn target() -> Target {
|
|||
Target {
|
||||
llvm_target: "arm-unknown-linux-musleabihf".into(),
|
||||
metadata: TargetMetadata {
|
||||
description: Some("Armv6 Linux with musl 1.2.3, hardfloat".into()),
|
||||
description: Some("Armv6 Linux with musl 1.2.5, hardfloat".into()),
|
||||
tier: Some(2),
|
||||
host_tools: Some(false),
|
||||
std: Some(true),
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ pub(crate) fn target() -> Target {
|
|||
Target {
|
||||
llvm_target: "armv5te-unknown-linux-musleabi".into(),
|
||||
metadata: TargetMetadata {
|
||||
description: Some("Armv5TE Linux with musl 1.2.3".into()),
|
||||
description: Some("Armv5TE Linux with musl 1.2.5".into()),
|
||||
tier: Some(2),
|
||||
host_tools: Some(false),
|
||||
std: Some(true),
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ pub(crate) fn target() -> Target {
|
|||
Target {
|
||||
llvm_target: "armv7-unknown-linux-musleabi".into(),
|
||||
metadata: TargetMetadata {
|
||||
description: Some("Armv7-A Linux with musl 1.2.3".into()),
|
||||
description: Some("Armv7-A Linux with musl 1.2.5".into()),
|
||||
tier: Some(2),
|
||||
host_tools: Some(false),
|
||||
std: Some(true),
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ pub(crate) fn target() -> Target {
|
|||
Target {
|
||||
llvm_target: "armv7-unknown-linux-musleabihf".into(),
|
||||
metadata: TargetMetadata {
|
||||
description: Some("Armv7-A Linux with musl 1.2.3, hardfloat".into()),
|
||||
description: Some("Armv7-A Linux with musl 1.2.5, hardfloat".into()),
|
||||
tier: Some(2),
|
||||
host_tools: Some(false),
|
||||
std: Some(true),
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ pub(crate) fn target() -> Target {
|
|||
Target {
|
||||
llvm_target: "hexagon-unknown-linux-musl".into(),
|
||||
metadata: TargetMetadata {
|
||||
description: Some("Hexagon Linux with musl 1.2.3".into()),
|
||||
description: Some("Hexagon Linux with musl 1.2.5".into()),
|
||||
tier: Some(3),
|
||||
host_tools: Some(false),
|
||||
std: Some(true),
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ pub(crate) fn target() -> Target {
|
|||
Target {
|
||||
llvm_target: "i686-unknown-linux-musl".into(),
|
||||
metadata: TargetMetadata {
|
||||
description: Some("32-bit Linux with musl 1.2.3".into()),
|
||||
description: Some("32-bit Linux with musl 1.2.5".into()),
|
||||
tier: Some(2),
|
||||
host_tools: Some(false),
|
||||
std: Some(true),
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ pub(crate) fn target() -> Target {
|
|||
// LLVM doesn't recognize "muslabi64" yet.
|
||||
llvm_target: "mips64-unknown-linux-musl".into(),
|
||||
metadata: TargetMetadata {
|
||||
description: Some("MIPS64 for OpenWrt Linux musl 1.2.3".into()),
|
||||
description: Some("MIPS64 for OpenWrt Linux musl 1.2.5".into()),
|
||||
tier: Some(3),
|
||||
host_tools: Some(false),
|
||||
std: Some(true),
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ pub(crate) fn target() -> Target {
|
|||
// LLVM doesn't recognize "muslabi64" yet.
|
||||
llvm_target: "mips64-unknown-linux-musl".into(),
|
||||
metadata: TargetMetadata {
|
||||
description: Some("MIPS64 Linux, N64 ABI, musl 1.2.3".into()),
|
||||
description: Some("MIPS64 Linux, N64 ABI, musl 1.2.5".into()),
|
||||
tier: Some(3),
|
||||
host_tools: Some(false),
|
||||
std: Some(true),
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ pub(crate) fn target() -> Target {
|
|||
// LLVM doesn't recognize "muslabi64" yet.
|
||||
llvm_target: "mips64el-unknown-linux-musl".into(),
|
||||
metadata: TargetMetadata {
|
||||
description: Some("MIPS64 Linux, N64 ABI, musl 1.2.3".into()),
|
||||
description: Some("MIPS64 Linux, N64 ABI, musl 1.2.5".into()),
|
||||
tier: Some(3),
|
||||
host_tools: Some(false),
|
||||
std: Some(true),
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ pub(crate) fn target() -> Target {
|
|||
Target {
|
||||
llvm_target: "mips-unknown-linux-musl".into(),
|
||||
metadata: TargetMetadata {
|
||||
description: Some("MIPS Linux with musl 1.2.3".into()),
|
||||
description: Some("MIPS Linux with musl 1.2.5".into()),
|
||||
tier: Some(3),
|
||||
host_tools: Some(false),
|
||||
std: Some(true),
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ pub(crate) fn target() -> Target {
|
|||
Target {
|
||||
llvm_target: "mipsel-unknown-linux-musl".into(),
|
||||
metadata: TargetMetadata {
|
||||
description: Some("MIPS (little endian) Linux with musl 1.2.3".into()),
|
||||
description: Some("MIPS (little endian) Linux with musl 1.2.5".into()),
|
||||
tier: Some(3),
|
||||
host_tools: Some(false),
|
||||
std: Some(true),
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ pub(crate) fn target() -> Target {
|
|||
Target {
|
||||
llvm_target: "powerpc64-unknown-linux-musl".into(),
|
||||
metadata: TargetMetadata {
|
||||
description: Some("64-bit PowerPC Linux with musl 1.2.3".into()),
|
||||
description: Some("64-bit PowerPC Linux with musl 1.2.5".into()),
|
||||
tier: Some(3),
|
||||
host_tools: Some(false),
|
||||
std: Some(true),
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ pub(crate) fn target() -> Target {
|
|||
Target {
|
||||
llvm_target: "powerpc64le-unknown-linux-musl".into(),
|
||||
metadata: TargetMetadata {
|
||||
description: Some("64-bit PowerPC Linux with musl 1.2.3, Little Endian".into()),
|
||||
description: Some("64-bit PowerPC Linux with musl 1.2.5, Little Endian".into()),
|
||||
tier: Some(2),
|
||||
host_tools: Some(true),
|
||||
std: Some(true),
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ pub(crate) fn target() -> Target {
|
|||
Target {
|
||||
llvm_target: "powerpc-unknown-linux-musl".into(),
|
||||
metadata: TargetMetadata {
|
||||
description: Some("PowerPC Linux with musl 1.2.3".into()),
|
||||
description: Some("PowerPC Linux with musl 1.2.5".into()),
|
||||
tier: Some(3),
|
||||
host_tools: Some(false),
|
||||
std: Some(true),
|
||||
|
|
|
|||
|
|
@ -6,9 +6,7 @@ pub(crate) fn target() -> Target {
|
|||
Target {
|
||||
llvm_target: "riscv32-unknown-linux-musl".into(),
|
||||
metadata: TargetMetadata {
|
||||
description: Some(
|
||||
"RISC-V Linux (kernel 5.4, musl 1.2.3 + RISCV32 support patches".into(),
|
||||
),
|
||||
description: Some("RISC-V Linux (kernel 5.4, musl 1.2.5)".into()),
|
||||
tier: Some(3),
|
||||
host_tools: Some(false),
|
||||
std: Some(true),
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ pub(crate) fn target() -> Target {
|
|||
Target {
|
||||
llvm_target: "riscv64-unknown-linux-musl".into(),
|
||||
metadata: TargetMetadata {
|
||||
description: Some("RISC-V Linux (kernel 4.20, musl 1.2.3)".into()),
|
||||
description: Some("RISC-V Linux (kernel 4.20, musl 1.2.5)".into()),
|
||||
tier: Some(3),
|
||||
host_tools: Some(false),
|
||||
std: Some(true),
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ pub(crate) fn target() -> Target {
|
|||
Target {
|
||||
llvm_target: "s390x-unknown-linux-musl".into(),
|
||||
metadata: TargetMetadata {
|
||||
description: Some("S390x Linux (kernel 3.2, musl 1.2.3)".into()),
|
||||
description: Some("S390x Linux (kernel 3.2, musl 1.2.5)".into()),
|
||||
tier: Some(3),
|
||||
host_tools: Some(false),
|
||||
std: Some(true),
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ pub(crate) fn target() -> Target {
|
|||
Target {
|
||||
llvm_target: "armv7-unknown-linux-musleabihf".into(),
|
||||
metadata: TargetMetadata {
|
||||
description: Some("Thumb2-mode ARMv7-A Linux with NEON, musl 1.2.3".into()),
|
||||
description: Some("Thumb2-mode ARMv7-A Linux with NEON, musl 1.2.5".into()),
|
||||
tier: Some(3),
|
||||
host_tools: Some(false),
|
||||
std: Some(true),
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ pub(crate) fn target() -> Target {
|
|||
Target {
|
||||
llvm_target: "x86_64-unknown-linux-musl".into(),
|
||||
metadata: TargetMetadata {
|
||||
description: Some("64-bit Unikraft with musl 1.2.3".into()),
|
||||
description: Some("64-bit Unikraft with musl 1.2.5".into()),
|
||||
tier: Some(3),
|
||||
host_tools: Some(false),
|
||||
std: Some(true),
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ pub(crate) fn target() -> Target {
|
|||
Target {
|
||||
llvm_target: "x86_64-unknown-linux-musl".into(),
|
||||
metadata: TargetMetadata {
|
||||
description: Some("64-bit Linux with musl 1.2.3".into()),
|
||||
description: Some("64-bit Linux with musl 1.2.5".into()),
|
||||
tier: Some(2),
|
||||
host_tools: Some(true),
|
||||
std: Some(true),
|
||||
|
|
|
|||
|
|
@ -386,7 +386,6 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
|
|||
("amx-movrs", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]),
|
||||
("amx-tf32", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]),
|
||||
("amx-tile", Unstable(sym::x86_amx_intrinsics), &[]),
|
||||
("amx-transpose", Unstable(sym::x86_amx_intrinsics), &["amx-tile"]),
|
||||
("apxf", Unstable(sym::apx_target_feature), &[]),
|
||||
("avx", Stable, &["sse4.2"]),
|
||||
("avx2", Stable, &["avx"]),
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ impl Barrier {
|
|||
let local_gen = lock.generation_id;
|
||||
lock.count += 1;
|
||||
if lock.count < self.num_threads {
|
||||
let _guard = self.cvar.wait_while(lock, |state| local_gen == state.generation_id);
|
||||
self.cvar.wait_while(&mut lock, |state| local_gen == state.generation_id);
|
||||
BarrierWaitResult(false)
|
||||
} else {
|
||||
lock.count = 0;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use crate::fmt;
|
||||
use crate::ops::DerefMut;
|
||||
use crate::sync::WaitTimeoutResult;
|
||||
use crate::sync::nonpoison::{MutexGuard, mutex};
|
||||
use crate::sys::sync as sys;
|
||||
|
|
@ -38,7 +39,7 @@ use crate::time::{Duration, Instant};
|
|||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut started = lock.lock();
|
||||
/// while !*started {
|
||||
/// started = cvar.wait(started);
|
||||
/// cvar.wait(&mut started);
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
|
|
@ -115,16 +116,15 @@ impl Condvar {
|
|||
/// let mut started = lock.lock();
|
||||
/// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
|
||||
/// while !*started {
|
||||
/// started = cvar.wait(started);
|
||||
/// cvar.wait(&mut started);
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> MutexGuard<'a, T> {
|
||||
pub fn wait<T>(&self, guard: &mut MutexGuard<'_, T>) {
|
||||
unsafe {
|
||||
let lock = mutex::guard_lock(&guard);
|
||||
let lock = mutex::guard_lock(guard);
|
||||
self.inner.wait(lock);
|
||||
}
|
||||
guard
|
||||
}
|
||||
|
||||
/// Blocks the current thread until the provided condition becomes false.
|
||||
|
|
@ -167,21 +167,17 @@ impl Condvar {
|
|||
/// // Wait for the thread to start up.
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// // As long as the value inside the `Mutex<bool>` is `true`, we wait.
|
||||
/// let _guard = cvar.wait_while(lock.lock(), |pending| { *pending });
|
||||
/// let mut guard = lock.lock();
|
||||
/// cvar.wait_while(&mut guard, |pending| { *pending });
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
pub fn wait_while<'a, T, F>(
|
||||
&self,
|
||||
mut guard: MutexGuard<'a, T>,
|
||||
mut condition: F,
|
||||
) -> MutexGuard<'a, T>
|
||||
pub fn wait_while<T, F>(&self, guard: &mut MutexGuard<'_, T>, mut condition: F)
|
||||
where
|
||||
F: FnMut(&mut T) -> bool,
|
||||
{
|
||||
while condition(&mut *guard) {
|
||||
guard = self.wait(guard);
|
||||
while condition(guard.deref_mut()) {
|
||||
self.wait(guard);
|
||||
}
|
||||
guard
|
||||
}
|
||||
|
||||
/// Waits on this condition variable for a notification, timing out after a
|
||||
|
|
@ -206,7 +202,7 @@ impl Condvar {
|
|||
/// The returned [`WaitTimeoutResult`] value indicates if the timeout is
|
||||
/// known to have elapsed.
|
||||
///
|
||||
/// Like [`wait`], the lock specified will be re-acquired when this function
|
||||
/// Like [`wait`], the lock specified will have been re-acquired when this function
|
||||
/// returns, regardless of whether the timeout elapsed or not.
|
||||
///
|
||||
/// [`wait`]: Self::wait
|
||||
|
|
@ -239,9 +235,8 @@ impl Condvar {
|
|||
/// let mut started = lock.lock();
|
||||
/// // as long as the value inside the `Mutex<bool>` is `false`, we wait
|
||||
/// loop {
|
||||
/// let result = cvar.wait_timeout(started, Duration::from_millis(10));
|
||||
/// let result = cvar.wait_timeout(&mut started, Duration::from_millis(10));
|
||||
/// // 10 milliseconds have passed, or maybe the value changed!
|
||||
/// started = result.0;
|
||||
/// if *started == true {
|
||||
/// // We received the notification and the value has been updated, we can leave.
|
||||
/// break
|
||||
|
|
@ -249,16 +244,16 @@ impl Condvar {
|
|||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
pub fn wait_timeout<'a, T>(
|
||||
pub fn wait_timeout<T>(
|
||||
&self,
|
||||
guard: MutexGuard<'a, T>,
|
||||
guard: &mut MutexGuard<'_, T>,
|
||||
dur: Duration,
|
||||
) -> (MutexGuard<'a, T>, WaitTimeoutResult) {
|
||||
) -> WaitTimeoutResult {
|
||||
let success = unsafe {
|
||||
let lock = mutex::guard_lock(&guard);
|
||||
let lock = mutex::guard_lock(guard);
|
||||
self.inner.wait_timeout(lock, dur)
|
||||
};
|
||||
(guard, WaitTimeoutResult(!success))
|
||||
WaitTimeoutResult(!success)
|
||||
}
|
||||
|
||||
/// Waits on this condition variable for a notification, timing out after a
|
||||
|
|
@ -277,7 +272,7 @@ impl Condvar {
|
|||
/// The returned [`WaitTimeoutResult`] value indicates if the timeout is
|
||||
/// known to have elapsed without the condition being met.
|
||||
///
|
||||
/// Like [`wait_while`], the lock specified will be re-acquired when this
|
||||
/// Like [`wait_while`], the lock specified will have been re-acquired when this
|
||||
/// function returns, regardless of whether the timeout elapsed or not.
|
||||
///
|
||||
/// [`wait_while`]: Self::wait_while
|
||||
|
|
@ -307,37 +302,39 @@ impl Condvar {
|
|||
///
|
||||
/// // wait for the thread to start up
|
||||
/// let (lock, cvar) = &*pair;
|
||||
/// let mut guard = lock.lock();
|
||||
/// let result = cvar.wait_timeout_while(
|
||||
/// lock.lock(),
|
||||
/// &mut guard,
|
||||
/// Duration::from_millis(100),
|
||||
/// |&mut pending| pending,
|
||||
/// );
|
||||
/// if result.1.timed_out() {
|
||||
/// if result.timed_out() {
|
||||
/// // timed-out without the condition ever evaluating to false.
|
||||
/// }
|
||||
/// // access the locked mutex via result.0
|
||||
/// // access the locked mutex via guard
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
pub fn wait_timeout_while<'a, T, F>(
|
||||
pub fn wait_timeout_while<T, F>(
|
||||
&self,
|
||||
mut guard: MutexGuard<'a, T>,
|
||||
guard: &mut MutexGuard<'_, T>,
|
||||
dur: Duration,
|
||||
mut condition: F,
|
||||
) -> (MutexGuard<'a, T>, WaitTimeoutResult)
|
||||
) -> WaitTimeoutResult
|
||||
where
|
||||
F: FnMut(&mut T) -> bool,
|
||||
{
|
||||
let start = Instant::now();
|
||||
loop {
|
||||
if !condition(&mut *guard) {
|
||||
return (guard, WaitTimeoutResult(false));
|
||||
}
|
||||
|
||||
while condition(guard.deref_mut()) {
|
||||
let timeout = match dur.checked_sub(start.elapsed()) {
|
||||
Some(timeout) => timeout,
|
||||
None => return (guard, WaitTimeoutResult(true)),
|
||||
None => return WaitTimeoutResult(true),
|
||||
};
|
||||
guard = self.wait_timeout(guard, timeout).0;
|
||||
|
||||
self.wait_timeout(guard, timeout);
|
||||
}
|
||||
|
||||
WaitTimeoutResult(false)
|
||||
}
|
||||
|
||||
/// Wakes up one blocked thread on this condvar.
|
||||
|
|
@ -378,7 +375,7 @@ impl Condvar {
|
|||
/// let mut started = lock.lock();
|
||||
/// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
|
||||
/// while !*started {
|
||||
/// started = cvar.wait(started);
|
||||
/// cvar.wait(&mut started);
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
|
|
@ -422,7 +419,7 @@ impl Condvar {
|
|||
/// let mut started = lock.lock();
|
||||
/// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
|
||||
/// while !*started {
|
||||
/// started = cvar.wait(started);
|
||||
/// cvar.wait(&mut started);
|
||||
/// }
|
||||
/// ```
|
||||
#[unstable(feature = "nonpoison_condvar", issue = "134645")]
|
||||
|
|
|
|||
|
|
@ -17,256 +17,469 @@ nonpoison_and_poison_unwrap_test!(
|
|||
}
|
||||
);
|
||||
|
||||
#[test]
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
nonpoison_and_poison_unwrap_test!(
|
||||
name: notify_one,
|
||||
test_body: {
|
||||
use locks::{Condvar, Mutex};
|
||||
fn poison_notify_one() {
|
||||
use std::sync::poison::{Condvar, Mutex};
|
||||
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let m2 = m.clone();
|
||||
let c = Arc::new(Condvar::new());
|
||||
let c2 = c.clone();
|
||||
|
||||
let g = m.lock().unwrap();
|
||||
let _t = thread::spawn(move || {
|
||||
let _g = m2.lock().unwrap();
|
||||
c2.notify_one();
|
||||
});
|
||||
|
||||
let g = c.wait(g).unwrap();
|
||||
drop(g);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
fn nonpoison_notify_one() {
|
||||
use std::sync::nonpoison::{Condvar, Mutex};
|
||||
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let m2 = m.clone();
|
||||
let c = Arc::new(Condvar::new());
|
||||
let c2 = c.clone();
|
||||
|
||||
let mut g = m.lock();
|
||||
let _t = thread::spawn(move || {
|
||||
let _g = m2.lock();
|
||||
c2.notify_one();
|
||||
});
|
||||
|
||||
c.wait(&mut g);
|
||||
drop(g);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
fn poison_notify_all() {
|
||||
use std::sync::poison::{Condvar, Mutex};
|
||||
|
||||
const N: usize = 10;
|
||||
|
||||
let data = Arc::new((Mutex::new(0), Condvar::new()));
|
||||
let (tx, rx) = channel();
|
||||
for _ in 0..N {
|
||||
let data = data.clone();
|
||||
let tx = tx.clone();
|
||||
thread::spawn(move || {
|
||||
let &(ref lock, ref cond) = &*data;
|
||||
let mut cnt = lock.lock().unwrap();
|
||||
*cnt += 1;
|
||||
if *cnt == N {
|
||||
tx.send(()).unwrap();
|
||||
}
|
||||
while *cnt != 0 {
|
||||
cnt = cond.wait(cnt).unwrap();
|
||||
}
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
}
|
||||
drop(tx);
|
||||
|
||||
let &(ref lock, ref cond) = &*data;
|
||||
rx.recv().unwrap();
|
||||
let mut cnt = lock.lock().unwrap();
|
||||
*cnt = 0;
|
||||
cond.notify_all();
|
||||
drop(cnt);
|
||||
|
||||
for _ in 0..N {
|
||||
rx.recv().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
fn nonpoison_notify_all() {
|
||||
use std::sync::nonpoison::{Condvar, Mutex};
|
||||
|
||||
const N: usize = 10;
|
||||
|
||||
let data = Arc::new((Mutex::new(0), Condvar::new()));
|
||||
let (tx, rx) = channel();
|
||||
for _ in 0..N {
|
||||
let data = data.clone();
|
||||
let tx = tx.clone();
|
||||
thread::spawn(move || {
|
||||
let &(ref lock, ref cond) = &*data;
|
||||
let mut cnt = lock.lock();
|
||||
*cnt += 1;
|
||||
if *cnt == N {
|
||||
tx.send(()).unwrap();
|
||||
}
|
||||
while *cnt != 0 {
|
||||
cond.wait(&mut cnt);
|
||||
}
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
}
|
||||
drop(tx);
|
||||
|
||||
let &(ref lock, ref cond) = &*data;
|
||||
rx.recv().unwrap();
|
||||
let mut cnt = lock.lock();
|
||||
*cnt = 0;
|
||||
cond.notify_all();
|
||||
drop(cnt);
|
||||
|
||||
for _ in 0..N {
|
||||
rx.recv().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
fn poison_test_mutex_arc_condvar() {
|
||||
use std::sync::poison::{Condvar, Mutex};
|
||||
|
||||
struct Packet<T>(Arc<(Mutex<T>, Condvar)>);
|
||||
|
||||
let packet = Packet(Arc::new((Mutex::new(false), Condvar::new())));
|
||||
let packet2 = Packet(packet.0.clone());
|
||||
|
||||
let (tx, rx) = channel();
|
||||
|
||||
let _t = thread::spawn(move || {
|
||||
// Wait until our parent has taken the lock.
|
||||
rx.recv().unwrap();
|
||||
let &(ref lock, ref cvar) = &*packet2.0;
|
||||
|
||||
// Set the data to `true` and wake up our parent.
|
||||
let mut guard = lock.lock().unwrap();
|
||||
*guard = true;
|
||||
cvar.notify_one();
|
||||
});
|
||||
|
||||
let &(ref lock, ref cvar) = &*packet.0;
|
||||
let mut guard = lock.lock().unwrap();
|
||||
// Wake up our child.
|
||||
tx.send(()).unwrap();
|
||||
|
||||
// Wait until our child has set the data to `true`.
|
||||
assert!(!*guard);
|
||||
while !*guard {
|
||||
guard = cvar.wait(guard).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
fn nonpoison_test_mutex_arc_condvar() {
|
||||
use std::sync::nonpoison::{Condvar, Mutex};
|
||||
|
||||
struct Packet<T>(Arc<(Mutex<T>, Condvar)>);
|
||||
|
||||
let packet = Packet(Arc::new((Mutex::new(false), Condvar::new())));
|
||||
let packet2 = Packet(packet.0.clone());
|
||||
|
||||
let (tx, rx) = channel();
|
||||
|
||||
let _t = thread::spawn(move || {
|
||||
// Wait until our parent has taken the lock.
|
||||
rx.recv().unwrap();
|
||||
let &(ref lock, ref cvar) = &*packet2.0;
|
||||
|
||||
// Set the data to `true` and wake up our parent.
|
||||
let mut guard = lock.lock();
|
||||
*guard = true;
|
||||
cvar.notify_one();
|
||||
});
|
||||
|
||||
let &(ref lock, ref cvar) = &*packet.0;
|
||||
let mut guard = lock.lock();
|
||||
// Wake up our child.
|
||||
tx.send(()).unwrap();
|
||||
|
||||
// Wait until our child has set the data to `true`.
|
||||
assert!(!*guard);
|
||||
while !*guard {
|
||||
cvar.wait(&mut guard);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
fn poison_wait_while() {
|
||||
use std::sync::poison::{Condvar, Mutex};
|
||||
|
||||
let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
let pair2 = pair.clone();
|
||||
|
||||
// Inside of our lock, spawn a new thread, and then wait for it to start.
|
||||
thread::spawn(move || {
|
||||
let &(ref lock, ref cvar) = &*pair2;
|
||||
let mut started = lock.lock().unwrap();
|
||||
*started = true;
|
||||
// We notify the condvar that the value has changed.
|
||||
cvar.notify_one();
|
||||
});
|
||||
|
||||
// Wait for the thread to start up.
|
||||
let &(ref lock, ref cvar) = &*pair;
|
||||
let guard = cvar.wait_while(lock.lock().unwrap(), |started| !*started).unwrap();
|
||||
assert!(*guard);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
fn nonpoison_wait_while() {
|
||||
use std::sync::nonpoison::{Condvar, Mutex};
|
||||
|
||||
let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
let pair2 = pair.clone();
|
||||
|
||||
// Inside of our lock, spawn a new thread, and then wait for it to start.
|
||||
thread::spawn(move || {
|
||||
let &(ref lock, ref cvar) = &*pair2;
|
||||
let mut started = lock.lock();
|
||||
*started = true;
|
||||
// We notify the condvar that the value has changed.
|
||||
cvar.notify_one();
|
||||
});
|
||||
|
||||
// Wait for the thread to start up.
|
||||
let &(ref lock, ref cvar) = &*pair;
|
||||
let mut guard = lock.lock();
|
||||
cvar.wait_while(&mut guard, |started| !*started);
|
||||
assert!(*guard);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
fn poison_wait_timeout_wait() {
|
||||
use std::sync::poison::{Condvar, Mutex};
|
||||
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let c = Arc::new(Condvar::new());
|
||||
|
||||
loop {
|
||||
let g = m.lock().unwrap();
|
||||
let (_g, no_timeout) = c.wait_timeout(g, Duration::from_millis(1)).unwrap();
|
||||
// spurious wakeups mean this isn't necessarily true
|
||||
// so execute test again, if not timeout
|
||||
if !no_timeout.timed_out() {
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
fn nonpoison_wait_timeout_wait() {
|
||||
use std::sync::nonpoison::{Condvar, Mutex};
|
||||
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let c = Arc::new(Condvar::new());
|
||||
|
||||
loop {
|
||||
let mut g = m.lock();
|
||||
let no_timeout = c.wait_timeout(&mut g, Duration::from_millis(1));
|
||||
// spurious wakeups mean this isn't necessarily true
|
||||
// so execute test again, if not timeout
|
||||
if !no_timeout.timed_out() {
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
fn poison_wait_timeout_while_wait() {
|
||||
use std::sync::poison::{Condvar, Mutex};
|
||||
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let c = Arc::new(Condvar::new());
|
||||
|
||||
let g = m.lock().unwrap();
|
||||
let (_g, wait) = c.wait_timeout_while(g, Duration::from_millis(1), |_| true).unwrap();
|
||||
// no spurious wakeups. ensure it timed-out
|
||||
assert!(wait.timed_out());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
fn nonpoison_wait_timeout_while_wait() {
|
||||
use std::sync::nonpoison::{Condvar, Mutex};
|
||||
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let c = Arc::new(Condvar::new());
|
||||
|
||||
let mut g = m.lock();
|
||||
let wait = c.wait_timeout_while(&mut g, Duration::from_millis(1), |_| true);
|
||||
// no spurious wakeups. ensure it timed-out
|
||||
assert!(wait.timed_out());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
fn poison_wait_timeout_while_instant_satisfy() {
|
||||
use std::sync::poison::{Condvar, Mutex};
|
||||
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let c = Arc::new(Condvar::new());
|
||||
|
||||
let g = m.lock().unwrap();
|
||||
let (_g, wait) = c.wait_timeout_while(g, Duration::from_millis(0), |_| false).unwrap();
|
||||
// ensure it didn't time-out even if we were not given any time.
|
||||
assert!(!wait.timed_out());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
fn nonpoison_wait_timeout_while_instant_satisfy() {
|
||||
use std::sync::nonpoison::{Condvar, Mutex};
|
||||
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let c = Arc::new(Condvar::new());
|
||||
|
||||
let mut g = m.lock();
|
||||
let wait = c.wait_timeout_while(&mut g, Duration::from_millis(0), |_| false);
|
||||
// ensure it didn't time-out even if we were not given any time.
|
||||
assert!(!wait.timed_out());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
fn poison_wait_timeout_while_wake() {
|
||||
use std::sync::poison::{Condvar, Mutex};
|
||||
|
||||
let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
let pair_copy = pair.clone();
|
||||
|
||||
let &(ref m, ref c) = &*pair;
|
||||
let g = m.lock().unwrap();
|
||||
let _t = thread::spawn(move || {
|
||||
let &(ref lock, ref cvar) = &*pair_copy;
|
||||
let mut started = lock.lock().unwrap();
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
*started = true;
|
||||
cvar.notify_one();
|
||||
});
|
||||
|
||||
let (g2, wait) = c
|
||||
.wait_timeout_while(g, Duration::from_millis(u64::MAX), |&mut notified| !notified)
|
||||
.unwrap();
|
||||
// ensure it didn't time-out even if we were not given any time.
|
||||
assert!(!wait.timed_out());
|
||||
assert!(*g2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
fn nonpoison_wait_timeout_while_wake() {
|
||||
use std::sync::nonpoison::{Condvar, Mutex};
|
||||
|
||||
let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
let pair_copy = pair.clone();
|
||||
|
||||
let &(ref m, ref c) = &*pair;
|
||||
let mut g = m.lock();
|
||||
let _t = thread::spawn(move || {
|
||||
let &(ref lock, ref cvar) = &*pair_copy;
|
||||
let mut started = lock.lock();
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
*started = true;
|
||||
cvar.notify_one();
|
||||
});
|
||||
|
||||
let wait =
|
||||
c.wait_timeout_while(&mut g, Duration::from_millis(u64::MAX), |&mut notified| !notified);
|
||||
// ensure it didn't time-out even if we were not given any time.
|
||||
assert!(!wait.timed_out());
|
||||
assert!(*g);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
fn poison_wait_timeout_wake() {
|
||||
use std::sync::poison::{Condvar, Mutex};
|
||||
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let c = Arc::new(Condvar::new());
|
||||
|
||||
loop {
|
||||
let g = m.lock().unwrap();
|
||||
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let m2 = m.clone();
|
||||
let c = Arc::new(Condvar::new());
|
||||
let c2 = c.clone();
|
||||
let m2 = m.clone();
|
||||
|
||||
let g = maybe_unwrap(m.lock());
|
||||
let _t = thread::spawn(move || {
|
||||
let _g = maybe_unwrap(m2.lock());
|
||||
let notified = Arc::new(AtomicBool::new(false));
|
||||
let notified_copy = notified.clone();
|
||||
|
||||
let t = thread::spawn(move || {
|
||||
let _g = m2.lock().unwrap();
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
notified_copy.store(true, Ordering::Relaxed);
|
||||
c2.notify_one();
|
||||
});
|
||||
let g = maybe_unwrap(c.wait(g));
|
||||
drop(g);
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
nonpoison_and_poison_unwrap_test!(
|
||||
name: notify_all,
|
||||
test_body: {
|
||||
use locks::{Condvar, Mutex};
|
||||
|
||||
const N: usize = 10;
|
||||
|
||||
let data = Arc::new((Mutex::new(0), Condvar::new()));
|
||||
let (tx, rx) = channel();
|
||||
for _ in 0..N {
|
||||
let data = data.clone();
|
||||
let tx = tx.clone();
|
||||
thread::spawn(move || {
|
||||
let &(ref lock, ref cond) = &*data;
|
||||
let mut cnt = maybe_unwrap(lock.lock());
|
||||
*cnt += 1;
|
||||
if *cnt == N {
|
||||
tx.send(()).unwrap();
|
||||
}
|
||||
while *cnt != 0 {
|
||||
cnt = maybe_unwrap(cond.wait(cnt));
|
||||
}
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
}
|
||||
drop(tx);
|
||||
|
||||
let &(ref lock, ref cond) = &*data;
|
||||
rx.recv().unwrap();
|
||||
let mut cnt = maybe_unwrap(lock.lock());
|
||||
*cnt = 0;
|
||||
cond.notify_all();
|
||||
drop(cnt);
|
||||
|
||||
for _ in 0..N {
|
||||
rx.recv().unwrap();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
nonpoison_and_poison_unwrap_test!(
|
||||
name: test_mutex_arc_condvar,
|
||||
test_body: {
|
||||
use locks::{Condvar, Mutex};
|
||||
|
||||
struct Packet<T>(Arc<(Mutex<T>, Condvar)>);
|
||||
|
||||
let packet = Packet(Arc::new((Mutex::new(false), Condvar::new())));
|
||||
let packet2 = Packet(packet.0.clone());
|
||||
|
||||
let (tx, rx) = channel();
|
||||
|
||||
let _t = thread::spawn(move || {
|
||||
// Wait until our parent has taken the lock.
|
||||
rx.recv().unwrap();
|
||||
let &(ref lock, ref cvar) = &*packet2.0;
|
||||
|
||||
// Set the data to `true` and wake up our parent.
|
||||
let mut guard = maybe_unwrap(lock.lock());
|
||||
*guard = true;
|
||||
cvar.notify_one();
|
||||
});
|
||||
|
||||
let &(ref lock, ref cvar) = &*packet.0;
|
||||
let mut guard = maybe_unwrap(lock.lock());
|
||||
// Wake up our child.
|
||||
tx.send(()).unwrap();
|
||||
|
||||
// Wait until our child has set the data to `true`.
|
||||
assert!(!*guard);
|
||||
while !*guard {
|
||||
guard = maybe_unwrap(cvar.wait(guard));
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
nonpoison_and_poison_unwrap_test!(
|
||||
name: wait_while,
|
||||
test_body: {
|
||||
use locks::{Condvar, Mutex};
|
||||
|
||||
let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
let pair2 = pair.clone();
|
||||
|
||||
// Inside of our lock, spawn a new thread, and then wait for it to start.
|
||||
thread::spawn(move || {
|
||||
let &(ref lock, ref cvar) = &*pair2;
|
||||
let mut started = maybe_unwrap(lock.lock());
|
||||
*started = true;
|
||||
// We notify the condvar that the value has changed.
|
||||
cvar.notify_one();
|
||||
});
|
||||
|
||||
// Wait for the thread to start up.
|
||||
let &(ref lock, ref cvar) = &*pair;
|
||||
let guard = cvar.wait_while(maybe_unwrap(lock.lock()), |started| !*started);
|
||||
assert!(*maybe_unwrap(guard));
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
nonpoison_and_poison_unwrap_test!(
|
||||
name: wait_timeout_wait,
|
||||
test_body: {
|
||||
use locks::{Condvar, Mutex};
|
||||
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let c = Arc::new(Condvar::new());
|
||||
|
||||
loop {
|
||||
let g = maybe_unwrap(m.lock());
|
||||
let (_g, no_timeout) = maybe_unwrap(c.wait_timeout(g, Duration::from_millis(1)));
|
||||
// spurious wakeups mean this isn't necessarily true
|
||||
// so execute test again, if not timeout
|
||||
if !no_timeout.timed_out() {
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
nonpoison_and_poison_unwrap_test!(
|
||||
name: wait_timeout_while_wait,
|
||||
test_body: {
|
||||
use locks::{Condvar, Mutex};
|
||||
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let c = Arc::new(Condvar::new());
|
||||
|
||||
let g = maybe_unwrap(m.lock());
|
||||
let (_g, wait) = maybe_unwrap(c.wait_timeout_while(g, Duration::from_millis(1), |_| true));
|
||||
// no spurious wakeups. ensure it timed-out
|
||||
assert!(wait.timed_out());
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
nonpoison_and_poison_unwrap_test!(
|
||||
name: wait_timeout_while_instant_satisfy,
|
||||
test_body: {
|
||||
use locks::{Condvar, Mutex};
|
||||
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let c = Arc::new(Condvar::new());
|
||||
|
||||
let g = maybe_unwrap(m.lock());
|
||||
let (_g, wait) =
|
||||
maybe_unwrap(c.wait_timeout_while(g, Duration::from_millis(0), |_| false));
|
||||
// ensure it didn't time-out even if we were not given any time.
|
||||
assert!(!wait.timed_out());
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
nonpoison_and_poison_unwrap_test!(
|
||||
name: wait_timeout_while_wake,
|
||||
test_body: {
|
||||
use locks::{Condvar, Mutex};
|
||||
|
||||
let pair = Arc::new((Mutex::new(false), Condvar::new()));
|
||||
let pair_copy = pair.clone();
|
||||
|
||||
let &(ref m, ref c) = &*pair;
|
||||
let g = maybe_unwrap(m.lock());
|
||||
let _t = thread::spawn(move || {
|
||||
let &(ref lock, ref cvar) = &*pair_copy;
|
||||
let mut started = maybe_unwrap(lock.lock());
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
*started = true;
|
||||
cvar.notify_one();
|
||||
});
|
||||
let (g2, wait) = maybe_unwrap(c.wait_timeout_while(
|
||||
g,
|
||||
Duration::from_millis(u64::MAX),
|
||||
|&mut notified| !notified
|
||||
));
|
||||
// ensure it didn't time-out even if we were not given any time.
|
||||
assert!(!wait.timed_out());
|
||||
assert!(*g2);
|
||||
}
|
||||
);
|
||||
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
nonpoison_and_poison_unwrap_test!(
|
||||
name: wait_timeout_wake,
|
||||
test_body: {
|
||||
use locks::{Condvar, Mutex};
|
||||
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let c = Arc::new(Condvar::new());
|
||||
|
||||
loop {
|
||||
let g = maybe_unwrap(m.lock());
|
||||
|
||||
let c2 = c.clone();
|
||||
let m2 = m.clone();
|
||||
|
||||
let notified = Arc::new(AtomicBool::new(false));
|
||||
let notified_copy = notified.clone();
|
||||
|
||||
let t = thread::spawn(move || {
|
||||
let _g = maybe_unwrap(m2.lock());
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
notified_copy.store(true, Ordering::Relaxed);
|
||||
c2.notify_one();
|
||||
});
|
||||
let (g, timeout_res) =
|
||||
maybe_unwrap(c.wait_timeout(g, Duration::from_millis(u64::MAX)));
|
||||
assert!(!timeout_res.timed_out());
|
||||
// spurious wakeups mean this isn't necessarily true
|
||||
// so execute test again, if not notified
|
||||
if !notified.load(Ordering::Relaxed) {
|
||||
t.join().unwrap();
|
||||
continue;
|
||||
}
|
||||
drop(g);
|
||||
|
||||
let (g, timeout_res) = c.wait_timeout(g, Duration::from_millis(u64::MAX)).unwrap();
|
||||
assert!(!timeout_res.timed_out());
|
||||
// spurious wakeups mean this isn't necessarily true
|
||||
// so execute test again, if not notified
|
||||
if !notified.load(Ordering::Relaxed) {
|
||||
t.join().unwrap();
|
||||
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
drop(g);
|
||||
|
||||
t.join().unwrap();
|
||||
|
||||
break;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(any(target_os = "emscripten", target_os = "wasi")))] // No threads.
|
||||
fn nonpoison_wait_timeout_wake() {
|
||||
use std::sync::nonpoison::{Condvar, Mutex};
|
||||
|
||||
let m = Arc::new(Mutex::new(()));
|
||||
let c = Arc::new(Condvar::new());
|
||||
|
||||
loop {
|
||||
let mut g = m.lock();
|
||||
|
||||
let c2 = c.clone();
|
||||
let m2 = m.clone();
|
||||
|
||||
let notified = Arc::new(AtomicBool::new(false));
|
||||
let notified_copy = notified.clone();
|
||||
|
||||
let t = thread::spawn(move || {
|
||||
let _g = m2.lock();
|
||||
thread::sleep(Duration::from_millis(1));
|
||||
notified_copy.store(true, Ordering::Relaxed);
|
||||
c2.notify_one();
|
||||
});
|
||||
|
||||
let timeout_res = c.wait_timeout(&mut g, Duration::from_millis(u64::MAX));
|
||||
assert!(!timeout_res.timed_out());
|
||||
// spurious wakeups mean this isn't necessarily true
|
||||
// so execute test again, if not notified
|
||||
if !notified.load(Ordering::Relaxed) {
|
||||
t.join().unwrap();
|
||||
continue;
|
||||
}
|
||||
drop(g);
|
||||
|
||||
t.join().unwrap();
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Some platforms internally cast the timeout duration into nanoseconds.
|
||||
// If they fail to consider overflow during the conversion (I'm looking
|
||||
|
|
@ -274,42 +487,112 @@ nonpoison_and_poison_unwrap_test!(
|
|||
// timeout for durations that are slightly longer than u64::MAX nanoseconds.
|
||||
// `std` should guard against this by clamping the timeout.
|
||||
// See #37440 for context.
|
||||
nonpoison_and_poison_unwrap_test!(
|
||||
name: timeout_nanoseconds,
|
||||
test_body: {
|
||||
use locks::Mutex;
|
||||
use locks::Condvar;
|
||||
#[test]
|
||||
fn poison_timeout_nanoseconds() {
|
||||
use std::sync::poison::{Condvar, Mutex};
|
||||
|
||||
let sent = Mutex::new(false);
|
||||
let cond = Condvar::new();
|
||||
let sent = Mutex::new(false);
|
||||
let cond = Condvar::new();
|
||||
|
||||
thread::scope(|s| {
|
||||
s.spawn(|| {
|
||||
// Sleep so that the other thread has a chance to encounter the
|
||||
// timeout.
|
||||
thread::sleep(Duration::from_secs(2));
|
||||
maybe_unwrap(sent.set(true));
|
||||
cond.notify_all();
|
||||
});
|
||||
thread::scope(|s| {
|
||||
s.spawn(|| {
|
||||
// Sleep so that the other thread has a chance to encounter the
|
||||
// timeout.
|
||||
thread::sleep(Duration::from_secs(2));
|
||||
*sent.lock().unwrap() = true;
|
||||
cond.notify_all();
|
||||
});
|
||||
|
||||
let mut guard = maybe_unwrap(sent.lock());
|
||||
// Loop until `sent` is set by the thread to guard against spurious
|
||||
// wakeups. If the `wait_timeout` happens just before the signal by
|
||||
// the other thread, such a spurious wakeup might prevent the
|
||||
// miscalculated timeout from occurring, but this is basically just
|
||||
// a smoke test anyway.
|
||||
loop {
|
||||
if *guard {
|
||||
break;
|
||||
}
|
||||
|
||||
// If there is internal overflow, this call will return almost
|
||||
// immediately, before the other thread has reached the `notify_all`,
|
||||
// and indicate a timeout.
|
||||
let (g, res) = maybe_unwrap(cond.wait_timeout(guard, Duration::from_secs(u64::MAX.div_ceil(1_000_000_000))));
|
||||
assert!(!res.timed_out());
|
||||
guard = g;
|
||||
let mut guard = sent.lock().unwrap();
|
||||
// Loop until `sent` is set by the thread to guard against spurious
|
||||
// wakeups. If the `wait_timeout` happens just before the signal by
|
||||
// the other thread, such a spurious wakeup might prevent the
|
||||
// miscalculated timeout from occurring, but this is basically just
|
||||
// a smoke test anyway.
|
||||
loop {
|
||||
if *guard {
|
||||
break;
|
||||
}
|
||||
})
|
||||
|
||||
// If there is internal overflow, this call will return almost
|
||||
// immediately, before the other thread has reached the `notify_all`,
|
||||
// and indicate a timeout.
|
||||
let (g, res) = cond
|
||||
.wait_timeout(guard, Duration::from_secs(u64::MAX.div_ceil(1_000_000_000)))
|
||||
.unwrap();
|
||||
assert!(!res.timed_out());
|
||||
guard = g;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nonpoison_timeout_nanoseconds() {
|
||||
use std::sync::nonpoison::{Condvar, Mutex};
|
||||
|
||||
let sent = Mutex::new(false);
|
||||
let cond = Condvar::new();
|
||||
|
||||
thread::scope(|s| {
|
||||
s.spawn(|| {
|
||||
// Sleep so that the other thread has a chance to encounter the
|
||||
// timeout.
|
||||
thread::sleep(Duration::from_secs(2));
|
||||
sent.set(true);
|
||||
cond.notify_all();
|
||||
});
|
||||
|
||||
let mut guard = sent.lock();
|
||||
// Loop until `sent` is set by the thread to guard against spurious
|
||||
// wakeups. If the `wait_timeout` happens just before the signal by
|
||||
// the other thread, such a spurious wakeup might prevent the
|
||||
// miscalculated timeout from occurring, but this is basically just
|
||||
// a smoke test anyway.
|
||||
loop {
|
||||
if *guard {
|
||||
break;
|
||||
}
|
||||
|
||||
// If there is internal overflow, this call will return almost
|
||||
// immediately, before the other thread has reached the `notify_all`,
|
||||
// and indicate a timeout.
|
||||
let res = cond
|
||||
.wait_timeout(&mut guard, Duration::from_secs(u64::MAX.div_ceil(1_000_000_000)));
|
||||
assert!(!res.timed_out());
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
|
||||
fn test_arc_condvar_poison() {
|
||||
use std::sync::poison::{Condvar, Mutex};
|
||||
|
||||
struct Packet<T>(Arc<(Mutex<T>, Condvar)>);
|
||||
|
||||
let packet = Packet(Arc::new((Mutex::new(1), Condvar::new())));
|
||||
let packet2 = Packet(packet.0.clone());
|
||||
let (tx, rx) = channel();
|
||||
|
||||
let _t = thread::spawn(move || -> () {
|
||||
rx.recv().unwrap();
|
||||
let &(ref lock, ref cvar) = &*packet2.0;
|
||||
let _g = lock.lock().unwrap();
|
||||
cvar.notify_one();
|
||||
// Parent should fail when it wakes up.
|
||||
panic!();
|
||||
});
|
||||
|
||||
let &(ref lock, ref cvar) = &*packet.0;
|
||||
let mut lock = lock.lock().unwrap();
|
||||
tx.send(()).unwrap();
|
||||
while *lock == 1 {
|
||||
match cvar.wait(lock) {
|
||||
Ok(l) => {
|
||||
lock = l;
|
||||
assert_eq!(*lock, 1);
|
||||
}
|
||||
Err(..) => break,
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use std::ops::FnMut;
|
|||
use std::panic::{self, AssertUnwindSafe};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::mpsc::channel;
|
||||
use std::sync::{Arc, Condvar, MappedMutexGuard, Mutex, MutexGuard, TryLockError};
|
||||
use std::sync::{Arc, MappedMutexGuard, Mutex, MutexGuard, TryLockError};
|
||||
use std::{hint, mem, thread};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -423,38 +423,6 @@ fn test_replace_poison() {
|
|||
inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20));
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
|
||||
fn test_arc_condvar_poison() {
|
||||
struct Packet<T>(Arc<(Mutex<T>, Condvar)>);
|
||||
|
||||
let packet = Packet(Arc::new((Mutex::new(1), Condvar::new())));
|
||||
let packet2 = Packet(packet.0.clone());
|
||||
let (tx, rx) = channel();
|
||||
|
||||
let _t = thread::spawn(move || -> () {
|
||||
rx.recv().unwrap();
|
||||
let &(ref lock, ref cvar) = &*packet2.0;
|
||||
let _g = lock.lock().unwrap();
|
||||
cvar.notify_one();
|
||||
// Parent should fail when it wakes up.
|
||||
panic!();
|
||||
});
|
||||
|
||||
let &(ref lock, ref cvar) = &*packet.0;
|
||||
let mut lock = lock.lock().unwrap();
|
||||
tx.send(()).unwrap();
|
||||
while *lock == 1 {
|
||||
match cvar.wait(lock) {
|
||||
Ok(l) => {
|
||||
lock = l;
|
||||
assert_eq!(*lock, 1);
|
||||
}
|
||||
Err(..) => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
|
||||
fn test_mutex_arc_poison() {
|
||||
|
|
|
|||
|
|
@ -93,7 +93,6 @@ features! {
|
|||
/// * `"amx-fp8"`
|
||||
/// * `"amx-movrs"`
|
||||
/// * `"amx-tf32"`
|
||||
/// * `"amx-transpose"`
|
||||
/// * `"f16c"`
|
||||
/// * `"fma"`
|
||||
/// * `"bmi1"`
|
||||
|
|
@ -231,8 +230,6 @@ features! {
|
|||
/// AMX-MOVRS (Matrix MOVERS operations)
|
||||
@FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_tf32: "amx-tf32";
|
||||
/// AMX-TF32 (TensorFloat32 Operations)
|
||||
@FEATURE: #[unstable(feature = "x86_amx_intrinsics", issue = "126622")] amx_transpose: "amx-transpose";
|
||||
/// AMX-TRANSPOSE (Matrix Transpose Operations)
|
||||
@FEATURE: #[unstable(feature = "apx_target_feature", issue = "139284")] apxf: "apxf";
|
||||
/// APX-F (Advanced Performance Extensions - Foundation)
|
||||
@FEATURE: #[unstable(feature = "avx10_target_feature", issue = "138843")] avx10_1: "avx10.1";
|
||||
|
|
|
|||
|
|
@ -285,7 +285,6 @@ pub(crate) fn detect_features() -> cache::Initializer {
|
|||
unsafe { __cpuid_count(0x1e_u32, 1) };
|
||||
|
||||
enable(amx_feature_flags_eax, 4, Feature::amx_fp8);
|
||||
enable(amx_feature_flags_eax, 5, Feature::amx_transpose);
|
||||
enable(amx_feature_flags_eax, 6, Feature::amx_tf32);
|
||||
enable(amx_feature_flags_eax, 7, Feature::amx_avx512);
|
||||
enable(amx_feature_flags_eax, 8, Feature::amx_movrs);
|
||||
|
|
|
|||
|
|
@ -76,7 +76,6 @@ fn dump() {
|
|||
println!("widekl: {:?}", is_x86_feature_detected!("widekl"));
|
||||
println!("movrs: {:?}", is_x86_feature_detected!("movrs"));
|
||||
println!("amx-fp8: {:?}", is_x86_feature_detected!("amx-fp8"));
|
||||
println!("amx-transpose: {:?}", is_x86_feature_detected!("amx-transpose"));
|
||||
println!("amx-tf32: {:?}", is_x86_feature_detected!("amx-tf32"));
|
||||
println!("amx-avx512: {:?}", is_x86_feature_detected!("amx-avx512"));
|
||||
println!("amx-movrs: {:?}", is_x86_feature_detected!("amx-movrs"));
|
||||
|
|
|
|||
|
|
@ -11,3 +11,4 @@ CT_BINUTILS_V_2_32=y
|
|||
CT_GLIBC_V_2_17=y
|
||||
CT_GCC_V_8=y
|
||||
CT_CC_LANG_CXX=y
|
||||
CT_MUSL_V_1_2_5=y
|
||||
|
|
|
|||
|
|
@ -12,3 +12,4 @@ CT_BINUTILS_EXTRA_CONFIG_ARRAY="--enable-compressed-debug-sections=none"
|
|||
CT_GLIBC_V_2_17=y
|
||||
CT_GCC_V_8=y
|
||||
CT_CC_LANG_CXX=y
|
||||
CT_MUSL_V_1_2_5=y
|
||||
|
|
|
|||
|
|
@ -11,6 +11,6 @@ CT_ARCH_ARCH="powerpc64le"
|
|||
CT_KERNEL_LINUX=y
|
||||
CT_LINUX_V_4_19=y
|
||||
CT_LIBC_MUSL=y
|
||||
CT_MUSL_V_1_2_3=y
|
||||
CT_MUSL_V_1_2_5=y
|
||||
CT_CC_LANG_CXX=y
|
||||
CT_GETTEXT_NEEDED=y
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#
|
||||
# Versions of the toolchain components are configurable in `musl-cross-make/Makefile` and
|
||||
# musl unlike GLIBC is forward compatible so upgrading it shouldn't break old distributions.
|
||||
# Right now we have: Binutils 2.31.1, GCC 9.2.0, musl 1.2.3.
|
||||
# Right now we have: Binutils 2.31.1, GCC 9.2.0, musl 1.2.5.
|
||||
|
||||
# ignore-tidy-linelength
|
||||
|
||||
|
|
@ -45,11 +45,11 @@ export CFLAGS="-fPIC -g1 $CFLAGS"
|
|||
|
||||
git clone https://github.com/richfelker/musl-cross-make # -b v0.9.9
|
||||
cd musl-cross-make
|
||||
# A version that includes support for building musl 1.2.3
|
||||
git checkout fe915821b652a7fa37b34a596f47d8e20bc72338
|
||||
# A version that includes support for building musl 1.2.5
|
||||
git checkout 3635262e4524c991552789af6f36211a335a77b3
|
||||
|
||||
hide_output make -j$(nproc) TARGET=$TARGET MUSL_VER=1.2.3 LINUX_HEADERS_SITE=$LINUX_HEADERS_SITE LINUX_VER=$LINUX_VER
|
||||
hide_output make install TARGET=$TARGET MUSL_VER=1.2.3 LINUX_HEADERS_SITE=$LINUX_HEADERS_SITE LINUX_VER=$LINUX_VER OUTPUT=$OUTPUT
|
||||
hide_output make -j$(nproc) TARGET=$TARGET MUSL_VER=1.2.5 LINUX_HEADERS_SITE=$LINUX_HEADERS_SITE LINUX_VER=$LINUX_VER
|
||||
hide_output make install TARGET=$TARGET MUSL_VER=1.2.5 LINUX_HEADERS_SITE=$LINUX_HEADERS_SITE LINUX_VER=$LINUX_VER OUTPUT=$OUTPUT
|
||||
|
||||
cd -
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ shift
|
|||
# Apparently applying `-fPIC` everywhere allows them to link successfully.
|
||||
export CFLAGS="-fPIC $CFLAGS"
|
||||
|
||||
MUSL=musl-1.2.3
|
||||
MUSL=musl-1.2.5
|
||||
|
||||
# may have been downloaded in a previous run
|
||||
if [ ! -d $MUSL ]; then
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ so Rustup may install the documentation for a similar tier 1 target instead.
|
|||
target | notes
|
||||
-------|-------
|
||||
[`aarch64-pc-windows-gnullvm`](platform-support/windows-gnullvm.md) | ARM64 MinGW (Windows 10+), LLVM ABI
|
||||
[`aarch64-unknown-linux-musl`](platform-support/aarch64-unknown-linux-musl.md) | ARM64 Linux with musl 1.2.3
|
||||
[`aarch64-unknown-linux-musl`](platform-support/aarch64-unknown-linux-musl.md) | ARM64 Linux with musl 1.2.5
|
||||
[`aarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ARM64 OpenHarmony
|
||||
`arm-unknown-linux-gnueabi` | Armv6 Linux (kernel 3.2+, glibc 2.17)
|
||||
`arm-unknown-linux-gnueabihf` | Armv6 Linux, hardfloat (kernel 3.2+, glibc 2.17)
|
||||
|
|
@ -101,14 +101,14 @@ target | notes
|
|||
`powerpc-unknown-linux-gnu` | PowerPC Linux (kernel 3.2+, glibc 2.17)
|
||||
`powerpc64-unknown-linux-gnu` | PPC64 Linux (kernel 3.2+, glibc 2.17)
|
||||
[`powerpc64le-unknown-linux-gnu`](platform-support/powerpc64le-unknown-linux-gnu.md) | PPC64LE Linux (kernel 3.10+, glibc 2.17)
|
||||
[`powerpc64le-unknown-linux-musl`](platform-support/powerpc64le-unknown-linux-musl.md) | PPC64LE Linux (kernel 4.19+, musl 1.2.3)
|
||||
[`powerpc64le-unknown-linux-musl`](platform-support/powerpc64le-unknown-linux-musl.md) | PPC64LE Linux (kernel 4.19+, musl 1.2.5)
|
||||
[`riscv64gc-unknown-linux-gnu`](platform-support/riscv64gc-unknown-linux-gnu.md) | RISC-V Linux (kernel 4.20+, glibc 2.29)
|
||||
[`s390x-unknown-linux-gnu`](platform-support/s390x-unknown-linux-gnu.md) | S390x Linux (kernel 3.2+, glibc 2.17)
|
||||
[`x86_64-apple-darwin`](platform-support/apple-darwin.md) | 64-bit macOS (10.12+, Sierra+)
|
||||
[`x86_64-pc-windows-gnullvm`](platform-support/windows-gnullvm.md) | 64-bit x86 MinGW (Windows 10+), LLVM ABI
|
||||
[`x86_64-unknown-freebsd`](platform-support/freebsd.md) | 64-bit x86 FreeBSD
|
||||
[`x86_64-unknown-illumos`](platform-support/illumos.md) | illumos
|
||||
`x86_64-unknown-linux-musl` | 64-bit Linux with musl 1.2.3
|
||||
`x86_64-unknown-linux-musl` | 64-bit Linux with musl 1.2.5
|
||||
[`x86_64-unknown-linux-ohos`](platform-support/openharmony.md) | x86_64 OpenHarmony
|
||||
[`x86_64-unknown-netbsd`](platform-support/netbsd.md) | NetBSD/amd64
|
||||
[`x86_64-pc-solaris`](platform-support/solaris.md) | 64-bit x86 Solaris 11.4
|
||||
|
|
@ -153,26 +153,26 @@ target | std | notes
|
|||
[`aarch64-unknown-none-softfloat`](platform-support/aarch64-unknown-none.md) | * | Bare ARM64, softfloat
|
||||
[`aarch64-unknown-uefi`](platform-support/unknown-uefi.md) | ? | ARM64 UEFI
|
||||
[`arm-linux-androideabi`](platform-support/android.md) | ✓ | Armv6 Android
|
||||
`arm-unknown-linux-musleabi` | ✓ | Armv6 Linux with musl 1.2.3
|
||||
`arm-unknown-linux-musleabihf` | ✓ | Armv6 Linux with musl 1.2.3, hardfloat
|
||||
`arm-unknown-linux-musleabi` | ✓ | Armv6 Linux with musl 1.2.5
|
||||
`arm-unknown-linux-musleabihf` | ✓ | Armv6 Linux with musl 1.2.5, hardfloat
|
||||
[`arm64ec-pc-windows-msvc`](platform-support/arm64ec-pc-windows-msvc.md) | ✓ | Arm64EC Windows MSVC
|
||||
[`armv5te-unknown-linux-gnueabi`](platform-support/armv5te-unknown-linux-gnueabi.md) | ✓ | Armv5TE Linux (kernel 4.4+, glibc 2.23)
|
||||
`armv5te-unknown-linux-musleabi` | ✓ | Armv5TE Linux with musl 1.2.3
|
||||
`armv5te-unknown-linux-musleabi` | ✓ | Armv5TE Linux with musl 1.2.5
|
||||
[`armv7-linux-androideabi`](platform-support/android.md) | ✓ | Armv7-A Android
|
||||
[`armv7-unknown-linux-gnueabi`](platform-support/armv7-unknown-linux-gnueabi.md) | ✓ | Armv7-A Linux (kernel 4.15+, glibc 2.27)
|
||||
`armv7-unknown-linux-musleabi` | ✓ | Armv7-A Linux with musl 1.2.3
|
||||
`armv7-unknown-linux-musleabihf` | ✓ | Armv7-A Linux with musl 1.2.3, hardfloat
|
||||
`armv7-unknown-linux-musleabi` | ✓ | Armv7-A Linux with musl 1.2.5
|
||||
`armv7-unknown-linux-musleabihf` | ✓ | Armv7-A Linux with musl 1.2.5, hardfloat
|
||||
[`armv7a-none-eabi`](platform-support/armv7a-none-eabi.md) | * | Bare Armv7-A
|
||||
[`armv7a-none-eabihf`](platform-support/armv7a-none-eabi.md) | * | | Bare Armv7-A, hardfloat
|
||||
[`armv7r-none-eabi`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R
|
||||
[`armv7r-none-eabihf`](platform-support/armv7r-none-eabi.md) | * | Bare Armv7-R, hardfloat
|
||||
[`armv8r-none-eabihf`](platform-support/armv8r-none-eabihf.md) | * | | Bare Armv8-R, hardfloat
|
||||
`i586-unknown-linux-gnu` | ✓ | 32-bit Linux (kernel 3.2+, glibc 2.17, original Pentium) [^x86_32-floats-x87]
|
||||
`i586-unknown-linux-musl` | ✓ | 32-bit Linux (musl 1.2.3, original Pentium) [^x86_32-floats-x87]
|
||||
`i586-unknown-linux-musl` | ✓ | 32-bit Linux (musl 1.2.5, original Pentium) [^x86_32-floats-x87]
|
||||
[`i686-linux-android`](platform-support/android.md) | ✓ | 32-bit x86 Android ([Pentium 4 plus various extensions](https://developer.android.com/ndk/guides/abis.html#x86)) [^x86_32-floats-return-ABI]
|
||||
[`i686-pc-windows-gnullvm`](platform-support/windows-gnullvm.md) | ✓ | 32-bit x86 MinGW (Windows 10+, Pentium 4), LLVM ABI [^x86_32-floats-return-ABI]
|
||||
[`i686-unknown-freebsd`](platform-support/freebsd.md) | ✓ | 32-bit x86 FreeBSD (Pentium 4) [^x86_32-floats-return-ABI]
|
||||
`i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 (Pentium 4) [^x86_32-floats-return-ABI]
|
||||
`i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.5 (Pentium 4) [^x86_32-floats-return-ABI]
|
||||
[`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI (Pentium 4, softfloat) [^win32-msvc-alignment]
|
||||
[`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64D ABI)
|
||||
[`loongarch64-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64S ABI)
|
||||
|
|
@ -182,7 +182,7 @@ target | std | notes
|
|||
[`riscv32imac-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | Bare RISC-V (RV32IMAC ISA)
|
||||
[`riscv32imafc-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | Bare RISC-V (RV32IMAFC ISA)
|
||||
[`riscv32imc-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | Bare RISC-V (RV32IMC ISA)
|
||||
[`riscv64gc-unknown-linux-musl`](platform-support/riscv64gc-unknown-linux-musl.md) | RISC-V Linux (kernel 4.20+, musl 1.2.3)
|
||||
[`riscv64gc-unknown-linux-musl`](platform-support/riscv64gc-unknown-linux-musl.md) | RISC-V Linux (kernel 4.20+, musl 1.2.5)
|
||||
`riscv64gc-unknown-none-elf` | * | Bare RISC-V (RV64IMAFDC ISA)
|
||||
`riscv64imac-unknown-none-elf` | * | Bare RISC-V (RV64IMAC ISA)
|
||||
`sparc64-unknown-linux-gnu` | ✓ | SPARC Linux (kernel 4.4+, glibc 2.23)
|
||||
|
|
@ -313,7 +313,7 @@ target | std | host | notes
|
|||
`bpfel-unknown-none` | * | | BPF (little endian)
|
||||
`csky-unknown-linux-gnuabiv2` | ✓ | | C-SKY abiv2 Linux (little endian)
|
||||
`csky-unknown-linux-gnuabiv2hf` | ✓ | | C-SKY abiv2 Linux, hardfloat (little endian)
|
||||
[`hexagon-unknown-linux-musl`](platform-support/hexagon-unknown-linux-musl.md) | ✓ | | Hexagon Linux with musl 1.2.3
|
||||
[`hexagon-unknown-linux-musl`](platform-support/hexagon-unknown-linux-musl.md) | ✓ | | Hexagon Linux with musl 1.2.5
|
||||
[`hexagon-unknown-none-elf`](platform-support/hexagon-unknown-none-elf.md)| * | | Bare Hexagon (v60+, HVX)
|
||||
[`i386-apple-ios`](platform-support/apple-ios.md) | ✓ | | 32-bit x86 iOS (Penryn) [^x86_32-floats-return-ABI]
|
||||
[`i586-unknown-netbsd`](platform-support/netbsd.md) | ✓ | | 32-bit x86 (original Pentium) [^x86_32-floats-x87]
|
||||
|
|
@ -336,17 +336,17 @@ target | std | host | notes
|
|||
[`m68k-unknown-linux-gnu`](platform-support/m68k-unknown-linux-gnu.md) | ? | | Motorola 680x0 Linux
|
||||
[`m68k-unknown-none-elf`](platform-support/m68k-unknown-none-elf.md) | | | Motorola 680x0
|
||||
`mips-unknown-linux-gnu` | ✓ | ✓ | MIPS Linux (kernel 4.4, glibc 2.23)
|
||||
`mips-unknown-linux-musl` | ✓ | | MIPS Linux with musl 1.2.3
|
||||
`mips-unknown-linux-musl` | ✓ | | MIPS Linux with musl 1.2.5
|
||||
`mips-unknown-linux-uclibc` | ✓ | | MIPS Linux with uClibc
|
||||
[`mips64-openwrt-linux-musl`](platform-support/mips64-openwrt-linux-musl.md) | ? | | MIPS64 for OpenWrt Linux musl 1.2.3
|
||||
[`mips64-openwrt-linux-musl`](platform-support/mips64-openwrt-linux-musl.md) | ? | | MIPS64 for OpenWrt Linux musl 1.2.5
|
||||
`mips64-unknown-linux-gnuabi64` | ✓ | ✓ | MIPS64 Linux, N64 ABI (kernel 4.4, glibc 2.23)
|
||||
[`mips64-unknown-linux-muslabi64`](platform-support/mips64-unknown-linux-muslabi64.md) | ✓ | ✓ | MIPS64 Linux, N64 ABI, musl 1.2.3
|
||||
[`mips64-unknown-linux-muslabi64`](platform-support/mips64-unknown-linux-muslabi64.md) | ✓ | ✓ | MIPS64 Linux, N64 ABI, musl 1.2.5
|
||||
`mips64el-unknown-linux-gnuabi64` | ✓ | ✓ | MIPS64 (little endian) Linux, N64 ABI (kernel 4.4, glibc 2.23)
|
||||
`mips64el-unknown-linux-muslabi64` | ✓ | | MIPS64 (little endian) Linux, N64 ABI, musl 1.2.3
|
||||
`mips64el-unknown-linux-muslabi64` | ✓ | | MIPS64 (little endian) Linux, N64 ABI, musl 1.2.5
|
||||
`mipsel-sony-psp` | * | | MIPS (LE) Sony PlayStation Portable (PSP)
|
||||
[`mipsel-sony-psx`](platform-support/mipsel-sony-psx.md) | * | | MIPS (LE) Sony PlayStation 1 (PSX)
|
||||
[`mipsel-unknown-linux-gnu`](platform-support/mipsel-unknown-linux-gnu.md) | ✓ | ✓ | MIPS (little endian) Linux (kernel 4.4, glibc 2.23)
|
||||
`mipsel-unknown-linux-musl` | ✓ | | MIPS (little endian) Linux with musl 1.2.3
|
||||
`mipsel-unknown-linux-musl` | ✓ | | MIPS (little endian) Linux with musl 1.2.5
|
||||
`mipsel-unknown-linux-uclibc` | ✓ | | MIPS (LE) Linux with uClibc
|
||||
[`mipsel-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | 32-bit MIPS (LE), requires mips32 cpu support
|
||||
`mipsel-unknown-none` | * | | Bare MIPS (LE) softfloat
|
||||
|
|
@ -360,15 +360,15 @@ target | std | host | notes
|
|||
[`powerpc-unknown-freebsd`](platform-support/freebsd.md) | ? | | PowerPC FreeBSD
|
||||
[`powerpc-unknown-helenos`](platform-support/helenos.md) | ✓ | | PowerPC HelenOS
|
||||
[`powerpc-unknown-linux-gnuspe`](platform-support/powerpc-unknown-linux-gnuspe.md) | ✓ | | PowerPC SPE Linux
|
||||
`powerpc-unknown-linux-musl` | ? | | PowerPC Linux with musl 1.2.3
|
||||
[`powerpc-unknown-linux-muslspe`](platform-support/powerpc-unknown-linux-muslspe.md) | ? | | PowerPC SPE Linux with musl 1.2.3
|
||||
`powerpc-unknown-linux-musl` | ? | | PowerPC Linux with musl 1.2.5
|
||||
[`powerpc-unknown-linux-muslspe`](platform-support/powerpc-unknown-linux-muslspe.md) | ? | | PowerPC SPE Linux with musl 1.2.5
|
||||
[`powerpc-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | NetBSD 32-bit powerpc systems
|
||||
[`powerpc-unknown-openbsd`](platform-support/powerpc-unknown-openbsd.md) | * | |
|
||||
[`powerpc-wrs-vxworks`](platform-support/vxworks.md) | ✓ | |
|
||||
[`powerpc-wrs-vxworks-spe`](platform-support/vxworks.md) | ✓ | |
|
||||
[`powerpc64-ibm-aix`](platform-support/aix.md) | ? | | 64-bit AIX (7.2 and newer)
|
||||
[`powerpc64-unknown-freebsd`](platform-support/freebsd.md) | ✓ | ✓ | PPC64 FreeBSD (ELFv2)
|
||||
[`powerpc64-unknown-linux-musl`](platform-support/powerpc64-unknown-linux-musl.md) | ✓ | ✓ | PPC64 Linux (kernel 4.19, musl 1.2.3)
|
||||
[`powerpc64-unknown-linux-musl`](platform-support/powerpc64-unknown-linux-musl.md) | ✓ | ✓ | PPC64 Linux (kernel 4.19, musl 1.2.5)
|
||||
[`powerpc64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/powerpc64
|
||||
[`powerpc64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | |
|
||||
[`powerpc64le-unknown-freebsd`](platform-support/freebsd.md) | ✓ | ✓ | PPC64LE FreeBSD
|
||||
|
|
@ -377,7 +377,7 @@ target | std | host | notes
|
|||
[`riscv32em-unknown-none-elf`](platform-support/riscv32e-unknown-none-elf.md) | * | | Bare RISC-V (RV32EM ISA)
|
||||
[`riscv32emc-unknown-none-elf`](platform-support/riscv32e-unknown-none-elf.md) | * | | Bare RISC-V (RV32EMC ISA)
|
||||
`riscv32gc-unknown-linux-gnu` | ✓ | | RISC-V Linux (kernel 5.4, glibc 2.33)
|
||||
`riscv32gc-unknown-linux-musl` | ? | | RISC-V Linux (kernel 5.4, musl 1.2.3 + RISCV32 support patches)
|
||||
`riscv32gc-unknown-linux-musl` | ? | | RISC-V Linux (kernel 5.4, musl 1.2.5)
|
||||
[`riscv32im-risc0-zkvm-elf`](platform-support/riscv32im-risc0-zkvm-elf.md) | ? | | RISC Zero's zero-knowledge Virtual Machine (RV32IM ISA)
|
||||
[`riscv32ima-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | | Bare RISC-V (RV32IMA ISA)
|
||||
[`riscv32imac-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF
|
||||
|
|
@ -399,7 +399,7 @@ target | std | host | notes
|
|||
[`riscv64gc-unknown-redox`](platform-support/redox.md) | ✓ | | RISC-V 64bit Redox OS
|
||||
[`riscv64imac-unknown-nuttx-elf`](platform-support/nuttx.md) | ✓ | | RISC-V 64bit with NuttX
|
||||
[`riscv64a23-unknown-linux-gnu`](platform-support/riscv64a23-unknown-linux-gnu.md) | ✓ | ✓ | RISC-V Linux (kernel 6.8.0+, glibc 2.39)
|
||||
[`s390x-unknown-linux-musl`](platform-support/s390x-unknown-linux-musl.md) | ✓ | | S390x Linux (kernel 3.2, musl 1.2.3)
|
||||
[`s390x-unknown-linux-musl`](platform-support/s390x-unknown-linux-musl.md) | ✓ | | S390x Linux (kernel 3.2, musl 1.2.5)
|
||||
`sparc-unknown-linux-gnu` | ✓ | | 32-bit SPARC Linux
|
||||
[`sparc-unknown-none-elf`](./platform-support/sparc-unknown-none-elf.md) | * | | Bare 32-bit SPARC V7+
|
||||
[`sparc64-unknown-helenos`](platform-support/helenos.md) | ✓ | | sparc64 HelenOS
|
||||
|
|
@ -415,7 +415,7 @@ target | std | host | notes
|
|||
[`thumbv7em-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv7EM with NuttX
|
||||
[`thumbv7em-nuttx-eabihf`](platform-support/nuttx.md) | ✓ | | ARMv7EM with NuttX, hardfloat
|
||||
[`thumbv7m-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv7M with NuttX
|
||||
`thumbv7neon-unknown-linux-musleabihf` | ? | | Thumb2-mode Armv7-A Linux with NEON, musl 1.2.3
|
||||
`thumbv7neon-unknown-linux-musleabihf` | ? | | Thumb2-mode Armv7-A Linux with NEON, musl 1.2.5
|
||||
[`thumbv8m.base-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv8M Baseline with NuttX
|
||||
[`thumbv8m.main-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv8M Mainline with NuttX
|
||||
[`thumbv8m.main-nuttx-eabihf`](platform-support/nuttx.md) | ✓ | | ARMv8M Mainline with NuttX, hardfloat
|
||||
|
|
@ -429,7 +429,7 @@ target | std | host | notes
|
|||
[`x86_64-pc-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | x86 64-bit QNX Neutrino 7.1 RTOS with default network stack (io-pkt) |
|
||||
[`x86_64-pc-nto-qnx710_iosock`](platform-support/nto-qnx.md) | ✓ | | x86 64-bit QNX Neutrino 7.1 RTOS with new network stack (io-sock) |
|
||||
[`x86_64-pc-nto-qnx800`](platform-support/nto-qnx.md) | ✓ | | x86 64-bit QNX Neutrino 8.0 RTOS |
|
||||
[`x86_64-unikraft-linux-musl`](platform-support/unikraft-linux-musl.md) | ✓ | | 64-bit Unikraft with musl 1.2.3
|
||||
[`x86_64-unikraft-linux-musl`](platform-support/unikraft-linux-musl.md) | ✓ | | 64-bit Unikraft with musl 1.2.5
|
||||
`x86_64-unknown-dragonfly` | ✓ | ✓ | 64-bit DragonFlyBSD
|
||||
`x86_64-unknown-haiku` | ✓ | ✓ | 64-bit Haiku
|
||||
[`x86_64-unknown-hermit`](platform-support/hermit.md) | ✓ | | x86_64 Hermit
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ IBM z/Architecture (s390x) targets (including IBM Z and LinuxONE) running Linux.
|
|||
This target requires:
|
||||
|
||||
* Linux Kernel version 3.2 or later
|
||||
* musl 1.2.3 or later
|
||||
* musl 1.2.5 or later
|
||||
|
||||
Code generated by the target uses the z/Architecture ISA assuming a minimum
|
||||
architecture level of z10 (Eighth Edition of the z/Architecture Principles
|
||||
|
|
|
|||
|
|
@ -343,7 +343,7 @@ struct TokenHandler<'a, 'tcx, F: Write> {
|
|||
/// We need to keep the `Class` for each element because it could contain a `Span` which is
|
||||
/// used to generate links.
|
||||
href_context: Option<HrefContext<'a, 'tcx>>,
|
||||
write_line_number: fn(u32) -> String,
|
||||
line_number_kind: LineNumberKind,
|
||||
line: u32,
|
||||
max_lines: u32,
|
||||
}
|
||||
|
|
@ -355,10 +355,10 @@ impl<F: Write> std::fmt::Debug for TokenHandler<'_, '_, F> {
|
|||
}
|
||||
|
||||
impl<'a, F: Write> TokenHandler<'a, '_, F> {
|
||||
fn handle_backline(&mut self) -> Option<String> {
|
||||
fn handle_backline(&mut self) -> Option<impl Display + use<F>> {
|
||||
self.line += 1;
|
||||
if self.line < self.max_lines {
|
||||
return Some((self.write_line_number)(self.line));
|
||||
return Some(self.line_number_kind.render(self.line));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
@ -376,8 +376,7 @@ impl<'a, F: Write> TokenHandler<'a, '_, F> {
|
|||
if text == "\n"
|
||||
&& let Some(backline) = self.handle_backline()
|
||||
{
|
||||
self.out.write_str(&text).unwrap();
|
||||
self.out.write_str(&backline).unwrap();
|
||||
write!(self.out, "{text}{backline}").unwrap();
|
||||
} else {
|
||||
self.push_token_without_backline_check(class, text, true);
|
||||
}
|
||||
|
|
@ -437,20 +436,29 @@ impl<F: Write> Drop for TokenHandler<'_, '_, F> {
|
|||
}
|
||||
}
|
||||
|
||||
fn scraped_line_number(line: u32) -> String {
|
||||
// https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr
|
||||
// Do not show "1 2 3 4 5 ..." in web search results.
|
||||
format!("<span data-nosnippet>{line}</span>")
|
||||
/// Represents the type of line number to be generated as HTML.
|
||||
#[derive(Clone, Copy)]
|
||||
enum LineNumberKind {
|
||||
/// Used for scraped code examples.
|
||||
Scraped,
|
||||
/// Used for source code pages.
|
||||
Normal,
|
||||
/// Code examples in documentation don't have line number generated by rustdoc.
|
||||
Empty,
|
||||
}
|
||||
|
||||
fn line_number(line: u32) -> String {
|
||||
// https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr
|
||||
// Do not show "1 2 3 4 5 ..." in web search results.
|
||||
format!("<a href=#{line} id={line} data-nosnippet>{line}</a>")
|
||||
}
|
||||
|
||||
fn empty_line_number(_: u32) -> String {
|
||||
String::new()
|
||||
impl LineNumberKind {
|
||||
fn render(self, line: u32) -> impl Display {
|
||||
fmt::from_fn(move |f| {
|
||||
match self {
|
||||
// https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#data-nosnippet-attr
|
||||
// Do not show "1 2 3 4 5 ..." in web search results.
|
||||
Self::Scraped => write!(f, "<span data-nosnippet>{line}</span>"),
|
||||
Self::Normal => write!(f, "<a href=#{line} id={line} data-nosnippet>{line}</a>"),
|
||||
Self::Empty => Ok(()),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn get_next_expansion(
|
||||
|
|
@ -534,15 +542,15 @@ pub(super) fn write_code(
|
|||
let mut token_handler = TokenHandler {
|
||||
out,
|
||||
href_context,
|
||||
write_line_number: match line_info {
|
||||
line_number_kind: match line_info {
|
||||
Some(line_info) => {
|
||||
if line_info.is_scraped_example {
|
||||
scraped_line_number
|
||||
LineNumberKind::Scraped
|
||||
} else {
|
||||
line_number
|
||||
LineNumberKind::Normal
|
||||
}
|
||||
}
|
||||
None => empty_line_number,
|
||||
None => LineNumberKind::Empty,
|
||||
},
|
||||
line: 0,
|
||||
max_lines: u32::MAX,
|
||||
|
|
@ -552,8 +560,12 @@ pub(super) fn write_code(
|
|||
if let Some(line_info) = line_info {
|
||||
token_handler.line = line_info.start_line - 1;
|
||||
token_handler.max_lines = line_info.max_lines;
|
||||
if let Some(text) = token_handler.handle_backline() {
|
||||
token_handler.push_token_without_backline_check(None, Cow::Owned(text), false);
|
||||
if let Some(backline) = token_handler.handle_backline() {
|
||||
token_handler.push_token_without_backline_check(
|
||||
None,
|
||||
Cow::Owned(backline.to_string()),
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3906,6 +3906,8 @@ class DocSearch {
|
|||
return name === "traitalias";
|
||||
case "macro":
|
||||
return name === "attr" || name === "derive";
|
||||
case "import":
|
||||
return name === "externcrate";
|
||||
}
|
||||
|
||||
// No match
|
||||
|
|
|
|||
|
|
@ -8,6 +8,89 @@ document.
|
|||
|
||||
[e9b7045...master](https://github.com/rust-lang/rust-clippy/compare/e9b7045...master)
|
||||
|
||||
## Rust 1.91
|
||||
|
||||
Current stable, released 2025-10-30
|
||||
|
||||
[View all 146 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2025-07-25T21%3A05%3A11Z..2025-09-04T22%3A34%3A27Z+base%3Amaster)
|
||||
|
||||
### New Lints
|
||||
|
||||
* Added [`possible_missing_else`] to `suspicious`
|
||||
[#15317](https://github.com/rust-lang/rust-clippy/pull/15317)
|
||||
|
||||
### Moves and Deprecations
|
||||
|
||||
* Moved [`cognitive_complexity`] from `nursery` to `restriction`
|
||||
[#15415](https://github.com/rust-lang/rust-clippy/pull/15415)
|
||||
* Moved [`declare_interior_mutable_const`] from `style` to `suspicious`
|
||||
[#15454](https://github.com/rust-lang/rust-clippy/pull/15454)
|
||||
* Moved [`crosspointer_transmute`] from `complexity` to `suspicious`
|
||||
[#15403](https://github.com/rust-lang/rust-clippy/pull/15403)
|
||||
|
||||
### Enhancements
|
||||
|
||||
* [`excessive_precision`] added `const_literal_digits_threshold` option to suppress overly precise constants.
|
||||
[#15193](https://github.com/rust-lang/rust-clippy/pull/15193)
|
||||
* [`unwrap_in_result`] rewritten for better accuracy; now lints on `.unwrap()` and `.expect()`
|
||||
directly and no longer mixes `Result` and `Option`.
|
||||
[#15445](https://github.com/rust-lang/rust-clippy/pull/15445)
|
||||
* [`panic`] now works in `const` contexts.
|
||||
[#15565](https://github.com/rust-lang/rust-clippy/pull/15565)
|
||||
* [`implicit_clone`] now also lints `to_string` calls (merging [`string_to_string`] behavior).
|
||||
[#14177](https://github.com/rust-lang/rust-clippy/pull/14177)
|
||||
* [`collapsible_match`] improved suggestions to handle necessary ref/dereferencing.
|
||||
[#14221](https://github.com/rust-lang/rust-clippy/pull/14221)
|
||||
* [`map_identity`] now suggests making variables mutable when required; recognizes tuple struct restructuring.
|
||||
[#15261](https://github.com/rust-lang/rust-clippy/pull/15261)
|
||||
* [`option_map_unit_fn`] preserves `unsafe` blocks in suggestions.
|
||||
[#15570](https://github.com/rust-lang/rust-clippy/pull/15570)
|
||||
* [`unnecessary_mut_passed`] provides structured, clearer fix suggestions.
|
||||
[#15438](https://github.com/rust-lang/rust-clippy/pull/15438)
|
||||
* [`float_equality_without_abs`] now checks `f16` and `f128` types.
|
||||
[#15054](https://github.com/rust-lang/rust-clippy/pull/15054)
|
||||
* [`doc_markdown`] expanded whitelist (`InfiniBand`, `RoCE`, `PowerPC`) and improved handling of
|
||||
identifiers like NixOS.
|
||||
[#15558](https://github.com/rust-lang/rust-clippy/pull/15558)
|
||||
* [`clone_on_ref_ptr`] now suggests fully qualified paths to avoid resolution errors.
|
||||
[#15561](https://github.com/rust-lang/rust-clippy/pull/15561)
|
||||
* [`manual_assert`] simplifies boolean expressions in suggested fixes.
|
||||
[#15368](https://github.com/rust-lang/rust-clippy/pull/15368)
|
||||
* [`four_forward_slashes`] warns about bare CR in comments and avoids invalid autofixes.
|
||||
[#15175](https://github.com/rust-lang/rust-clippy/pull/15175)
|
||||
|
||||
### False Positive Fixes
|
||||
|
||||
* [`alloc_instead_of_core`] fixed FP when `alloc` is an alias
|
||||
[#15581](https://github.com/rust-lang/rust-clippy/pull/15581)
|
||||
* [`needless_range_loop`] fixed FP and FN when meeting multidimensional array
|
||||
[#15486](https://github.com/rust-lang/rust-clippy/pull/15486)
|
||||
* [`semicolon_inside_block`] fixed FP when attribute over expr is not enabled
|
||||
[#15476](https://github.com/rust-lang/rust-clippy/pull/15476)
|
||||
* [`unnested_or_patterns`] fixed FP on structs with only shorthand field patterns
|
||||
[#15343](https://github.com/rust-lang/rust-clippy/pull/15343)
|
||||
* [`match_ref_pats`] fixed FP on match scrutinee of never type
|
||||
[#15474](https://github.com/rust-lang/rust-clippy/pull/15474)
|
||||
* [`infinite_loop`] fixed FP in async blocks that are not awaited
|
||||
[#15157](https://github.com/rust-lang/rust-clippy/pull/15157)
|
||||
* [`iter_on_single_items`] fixed FP on function pointers and let statements
|
||||
[#15013](https://github.com/rust-lang/rust-clippy/pull/15013)
|
||||
|
||||
### ICE Fixes
|
||||
|
||||
* [`len_zero`] fix ICE when fn len has a return type without generic type params
|
||||
[#15660](https://github.com/rust-lang/rust-clippy/pull/15660)
|
||||
|
||||
### Documentation Improvements
|
||||
|
||||
* [`cognitive_complexity`] corrected documentation to state lint is in `restriction`, not `nursery`
|
||||
[#15563](https://github.com/rust-lang/rust-clippy/pull/15563)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* [`doc_broken_link`] optimized by 99.77% (12M → 27k instructions)
|
||||
[#15385](https://github.com/rust-lang/rust-clippy/pull/15385)
|
||||
|
||||
## Rust 1.90
|
||||
|
||||
Current stable, released 2025-09-18
|
||||
|
|
@ -6253,6 +6336,7 @@ Released 2018-09-13
|
|||
[`empty_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_drop
|
||||
[`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum
|
||||
[`empty_enum_variants_with_brackets`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum_variants_with_brackets
|
||||
[`empty_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enums
|
||||
[`empty_line_after_doc_comments`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_doc_comments
|
||||
[`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr
|
||||
[`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop
|
||||
|
|
@ -6583,6 +6667,7 @@ Released 2018-09-13
|
|||
[`needless_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_else
|
||||
[`needless_for_each`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_for_each
|
||||
[`needless_if`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_if
|
||||
[`needless_ifs`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_ifs
|
||||
[`needless_late_init`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init
|
||||
[`needless_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_lifetimes
|
||||
[`needless_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_match
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
# The Rust Code of Conduct
|
||||
|
||||
The Code of Conduct for this repository [can be found online](https://www.rust-lang.org/conduct.html).
|
||||
The Code of Conduct for this repository [can be found online](https://rust-lang.org/policies/code-of-conduct/).
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "clippy"
|
||||
version = "0.1.92"
|
||||
version = "0.1.93"
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
readme = "README.md"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
[book]
|
||||
authors = ["The Rust Clippy Developers"]
|
||||
language = "en"
|
||||
multilingual = false
|
||||
src = "src"
|
||||
title = "Clippy Documentation"
|
||||
|
||||
[rust]
|
||||
|
|
|
|||
|
|
@ -21,14 +21,13 @@ use clippy_utils::sym;
|
|||
impl<'tcx> LateLintPass<'tcx> for OurFancyMethodLint {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
// Check our expr is calling a method with pattern matching
|
||||
if let hir::ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind
|
||||
if let hir::ExprKind::MethodCall(path, _, _, _) = &expr.kind
|
||||
// Check if the name of this method is `our_fancy_method`
|
||||
&& path.ident.name == sym::our_fancy_method
|
||||
// We can check the type of the self argument whenever necessary.
|
||||
// (It's necessary if we want to check that method is specifically belonging to a specific trait,
|
||||
// for example, a `map` method could belong to user-defined trait instead of to `Iterator`)
|
||||
// Check if the method belongs to the `sym::OurFancyTrait` trait.
|
||||
// (for example, a `map` method could belong to user-defined trait instead of to `Iterator`)
|
||||
// See the next section for more information.
|
||||
&& cx.ty_based_def(self_arg).opt_parent(cx).is_diag_item(cx, sym::OurFancyTrait)
|
||||
&& cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::OurFancyTrait)
|
||||
{
|
||||
println!("`expr` is a method call for `our_fancy_method`");
|
||||
}
|
||||
|
|
@ -45,6 +44,12 @@ New symbols such as `our_fancy_method` need to be added to the `clippy_utils::sy
|
|||
This module extends the list of symbols already provided by the compiler crates
|
||||
in `rustc_span::sym`.
|
||||
|
||||
If a trait defines only one method (such as the `std::ops::Deref` trait, which only has the `deref()` method),
|
||||
one might be tempted to omit the method name check. This would work, but is not always advisable because:
|
||||
- If a new method (possibly with a default implementation) were to be added to the trait, there would be a risk of
|
||||
matching the wrong method.
|
||||
- Comparing symbols is very cheap and might prevent a more expensive lookup.
|
||||
|
||||
## Checking if a `impl` block implements a method
|
||||
|
||||
While sometimes we want to check whether a method is being called or not, other
|
||||
|
|
|
|||
|
|
@ -859,6 +859,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
|
|||
* [`io_other_error`](https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error)
|
||||
* [`iter_kv_map`](https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map)
|
||||
* [`legacy_numeric_constants`](https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants)
|
||||
* [`len_zero`](https://rust-lang.github.io/rust-clippy/master/index.html#len_zero)
|
||||
* [`lines_filter_map_ok`](https://rust-lang.github.io/rust-clippy/master/index.html#lines_filter_map_ok)
|
||||
* [`manual_abs_diff`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_abs_diff)
|
||||
* [`manual_bits`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "clippy_config"
|
||||
version = "0.1.92"
|
||||
version = "0.1.93"
|
||||
edition = "2024"
|
||||
publish = false
|
||||
|
||||
|
|
|
|||
|
|
@ -748,6 +748,7 @@ define_Conf! {
|
|||
io_other_error,
|
||||
iter_kv_map,
|
||||
legacy_numeric_constants,
|
||||
len_zero,
|
||||
lines_filter_map_ok,
|
||||
manual_abs_diff,
|
||||
manual_bits,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use crate::update_lints::{DeprecatedLint, Lint, find_lint_decls, generate_lint_files, read_deprecated_lints};
|
||||
use crate::parse::{DeprecatedLint, Lint, ParseCx};
|
||||
use crate::update_lints::generate_lint_files;
|
||||
use crate::utils::{UpdateMode, Version};
|
||||
use std::ffi::OsStr;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
|
@ -13,21 +14,20 @@ use std::{fs, io};
|
|||
/// # Panics
|
||||
///
|
||||
/// If a file path could not read from or written to
|
||||
pub fn deprecate(clippy_version: Version, name: &str, reason: &str) {
|
||||
if let Some((prefix, _)) = name.split_once("::") {
|
||||
panic!("`{name}` should not contain the `{prefix}` prefix");
|
||||
}
|
||||
|
||||
let mut lints = find_lint_decls();
|
||||
let (mut deprecated_lints, renamed_lints) = read_deprecated_lints();
|
||||
pub fn deprecate<'cx>(cx: ParseCx<'cx>, clippy_version: Version, name: &'cx str, reason: &'cx str) {
|
||||
let mut lints = cx.find_lint_decls();
|
||||
let (mut deprecated_lints, renamed_lints) = cx.read_deprecated_lints();
|
||||
|
||||
let Some(lint) = lints.iter().find(|l| l.name == name) else {
|
||||
eprintln!("error: failed to find lint `{name}`");
|
||||
return;
|
||||
};
|
||||
|
||||
let prefixed_name = String::from_iter(["clippy::", name]);
|
||||
match deprecated_lints.binary_search_by(|x| x.name.cmp(&prefixed_name)) {
|
||||
let prefixed_name = cx.str_buf.with(|buf| {
|
||||
buf.extend(["clippy::", name]);
|
||||
cx.arena.alloc_str(buf)
|
||||
});
|
||||
match deprecated_lints.binary_search_by(|x| x.name.cmp(prefixed_name)) {
|
||||
Ok(_) => {
|
||||
println!("`{name}` is already deprecated");
|
||||
return;
|
||||
|
|
@ -36,8 +36,8 @@ pub fn deprecate(clippy_version: Version, name: &str, reason: &str) {
|
|||
idx,
|
||||
DeprecatedLint {
|
||||
name: prefixed_name,
|
||||
reason: reason.into(),
|
||||
version: clippy_version.rust_display().to_string(),
|
||||
reason,
|
||||
version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()),
|
||||
},
|
||||
),
|
||||
}
|
||||
|
|
@ -61,8 +61,8 @@ pub fn deprecate(clippy_version: Version, name: &str, reason: &str) {
|
|||
}
|
||||
}
|
||||
|
||||
fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io::Result<bool> {
|
||||
fn remove_lint(name: &str, lints: &mut Vec<Lint>) {
|
||||
fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint<'_>>) -> io::Result<bool> {
|
||||
fn remove_lint(name: &str, lints: &mut Vec<Lint<'_>>) {
|
||||
lints.iter().position(|l| l.name == name).map(|pos| lints.remove(pos));
|
||||
}
|
||||
|
||||
|
|
@ -135,14 +135,14 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io
|
|||
);
|
||||
|
||||
assert!(
|
||||
content[lint.declaration_range.clone()].contains(&name.to_uppercase()),
|
||||
content[lint.declaration_range].contains(&name.to_uppercase()),
|
||||
"error: `{}` does not contain lint `{}`'s declaration",
|
||||
path.display(),
|
||||
lint.name
|
||||
);
|
||||
|
||||
// Remove lint declaration (declare_clippy_lint!)
|
||||
content.replace_range(lint.declaration_range.clone(), "");
|
||||
content.replace_range(lint.declaration_range, "");
|
||||
|
||||
// Remove the module declaration (mod xyz;)
|
||||
let mod_decl = format!("\nmod {name};");
|
||||
|
|
|
|||
|
|
@ -268,7 +268,7 @@ fn run_rustfmt(update_mode: UpdateMode) {
|
|||
.expect("invalid rustfmt path");
|
||||
rustfmt_path.truncate(rustfmt_path.trim_end().len());
|
||||
|
||||
let args: Vec<_> = walk_dir_no_dot_or_target()
|
||||
let args: Vec<_> = walk_dir_no_dot_or_target(".")
|
||||
.filter_map(|e| {
|
||||
let e = expect_action(e, ErrAction::Read, ".");
|
||||
e.path()
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
#![feature(
|
||||
rustc_private,
|
||||
exit_status_error,
|
||||
if_let_guard,
|
||||
new_range,
|
||||
new_range_api,
|
||||
os_str_slice,
|
||||
os_string_truncate,
|
||||
pattern,
|
||||
rustc_private,
|
||||
slice_split_once
|
||||
)]
|
||||
#![warn(
|
||||
|
|
@ -15,6 +18,7 @@
|
|||
)]
|
||||
#![allow(clippy::missing_panics_doc)]
|
||||
|
||||
extern crate rustc_arena;
|
||||
#[expect(unused_extern_crates, reason = "required to link to rustc crates")]
|
||||
extern crate rustc_driver;
|
||||
extern crate rustc_lexer;
|
||||
|
|
@ -32,5 +36,8 @@ pub mod setup;
|
|||
pub mod sync;
|
||||
pub mod update_lints;
|
||||
|
||||
mod parse;
|
||||
mod utils;
|
||||
pub use utils::{ClippyInfo, UpdateMode};
|
||||
|
||||
pub use self::parse::{ParseCx, new_parse_cx};
|
||||
pub use self::utils::{ClippyInfo, UpdateMode};
|
||||
|
|
|
|||
|
|
@ -4,10 +4,9 @@
|
|||
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
use clippy_dev::{
|
||||
ClippyInfo, UpdateMode, deprecate_lint, dogfood, fmt, lint, new_lint, release, rename_lint, serve, setup, sync,
|
||||
update_lints,
|
||||
ClippyInfo, UpdateMode, deprecate_lint, dogfood, fmt, lint, new_lint, new_parse_cx, release, rename_lint, serve,
|
||||
setup, sync, update_lints,
|
||||
};
|
||||
use std::convert::Infallible;
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
|
|
@ -28,7 +27,7 @@ fn main() {
|
|||
allow_no_vcs,
|
||||
} => dogfood::dogfood(fix, allow_dirty, allow_staged, allow_no_vcs),
|
||||
DevCommand::Fmt { check } => fmt::run(UpdateMode::from_check(check)),
|
||||
DevCommand::UpdateLints { check } => update_lints::update(UpdateMode::from_check(check)),
|
||||
DevCommand::UpdateLints { check } => new_parse_cx(|cx| update_lints::update(cx, UpdateMode::from_check(check))),
|
||||
DevCommand::NewLint {
|
||||
pass,
|
||||
name,
|
||||
|
|
@ -36,7 +35,7 @@ fn main() {
|
|||
r#type,
|
||||
msrv,
|
||||
} => match new_lint::create(clippy.version, pass, &name, &category, r#type.as_deref(), msrv) {
|
||||
Ok(()) => update_lints::update(UpdateMode::Change),
|
||||
Ok(()) => new_parse_cx(|cx| update_lints::update(cx, UpdateMode::Change)),
|
||||
Err(e) => eprintln!("Unable to create lint: {e}"),
|
||||
},
|
||||
DevCommand::Setup(SetupCommand { subcommand }) => match subcommand {
|
||||
|
|
@ -79,13 +78,18 @@ fn main() {
|
|||
old_name,
|
||||
new_name,
|
||||
uplift,
|
||||
} => rename_lint::rename(
|
||||
clippy.version,
|
||||
&old_name,
|
||||
new_name.as_ref().unwrap_or(&old_name),
|
||||
uplift,
|
||||
),
|
||||
DevCommand::Deprecate { name, reason } => deprecate_lint::deprecate(clippy.version, &name, &reason),
|
||||
} => new_parse_cx(|cx| {
|
||||
rename_lint::rename(
|
||||
cx,
|
||||
clippy.version,
|
||||
&old_name,
|
||||
new_name.as_ref().unwrap_or(&old_name),
|
||||
uplift,
|
||||
);
|
||||
}),
|
||||
DevCommand::Deprecate { name, reason } => {
|
||||
new_parse_cx(|cx| deprecate_lint::deprecate(cx, clippy.version, &name, &reason));
|
||||
},
|
||||
DevCommand::Sync(SyncCommand { subcommand }) => match subcommand {
|
||||
SyncSubcommand::UpdateNightly => sync::update_nightly(),
|
||||
},
|
||||
|
|
@ -95,6 +99,20 @@ fn main() {
|
|||
}
|
||||
}
|
||||
|
||||
fn lint_name(name: &str) -> Result<String, String> {
|
||||
let name = name.replace('-', "_");
|
||||
if let Some((pre, _)) = name.split_once("::") {
|
||||
Err(format!("lint name should not contain the `{pre}` prefix"))
|
||||
} else if name
|
||||
.bytes()
|
||||
.any(|x| !matches!(x, b'_' | b'0'..=b'9' | b'a'..=b'z' | b'A'..=b'Z'))
|
||||
{
|
||||
Err("lint name contains invalid characters".to_owned())
|
||||
} else {
|
||||
Ok(name)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "dev", about)]
|
||||
struct Dev {
|
||||
|
|
@ -150,7 +168,7 @@ enum DevCommand {
|
|||
#[arg(
|
||||
short,
|
||||
long,
|
||||
value_parser = |name: &str| Ok::<_, Infallible>(name.replace('-', "_")),
|
||||
value_parser = lint_name,
|
||||
)]
|
||||
/// Name of the new lint in snake case, ex: `fn_too_long`
|
||||
name: String,
|
||||
|
|
@ -223,8 +241,12 @@ enum DevCommand {
|
|||
/// Rename a lint
|
||||
RenameLint {
|
||||
/// The name of the lint to rename
|
||||
#[arg(value_parser = lint_name)]
|
||||
old_name: String,
|
||||
#[arg(required_unless_present = "uplift")]
|
||||
#[arg(
|
||||
required_unless_present = "uplift",
|
||||
value_parser = lint_name,
|
||||
)]
|
||||
/// The new name of the lint
|
||||
new_name: Option<String>,
|
||||
#[arg(long)]
|
||||
|
|
@ -234,6 +256,7 @@ enum DevCommand {
|
|||
/// Deprecate the given lint
|
||||
Deprecate {
|
||||
/// The name of the lint to deprecate
|
||||
#[arg(value_parser = lint_name)]
|
||||
name: String,
|
||||
#[arg(long, short)]
|
||||
/// The reason for deprecation
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use crate::utils::{RustSearcher, Token, Version};
|
||||
use crate::parse::cursor::{self, Capture, Cursor};
|
||||
use crate::utils::Version;
|
||||
use clap::ValueEnum;
|
||||
use indoc::{formatdoc, writedoc};
|
||||
use std::fmt::{self, Write as _};
|
||||
|
|
@ -516,22 +517,22 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str>
|
|||
// Find both the last lint declaration (declare_clippy_lint!) and the lint pass impl
|
||||
fn parse_mod_file(path: &Path, contents: &str) -> (&'static str, usize) {
|
||||
#[allow(clippy::enum_glob_use)]
|
||||
use Token::*;
|
||||
use cursor::Pat::*;
|
||||
|
||||
let mut context = None;
|
||||
let mut decl_end = None;
|
||||
let mut searcher = RustSearcher::new(contents);
|
||||
while let Some(name) = searcher.find_capture_token(CaptureIdent) {
|
||||
match name {
|
||||
let mut cursor = Cursor::new(contents);
|
||||
let mut captures = [Capture::EMPTY];
|
||||
while let Some(name) = cursor.find_any_ident() {
|
||||
match cursor.get_text(name) {
|
||||
"declare_clippy_lint" => {
|
||||
if searcher.match_tokens(&[Bang, OpenBrace], &mut []) && searcher.find_token(CloseBrace) {
|
||||
decl_end = Some(searcher.pos());
|
||||
if cursor.match_all(&[Bang, OpenBrace], &mut []) && cursor.find_pat(CloseBrace) {
|
||||
decl_end = Some(cursor.pos());
|
||||
}
|
||||
},
|
||||
"impl" => {
|
||||
let mut capture = "";
|
||||
if searcher.match_tokens(&[Lt, Lifetime, Gt, CaptureIdent], &mut [&mut capture]) {
|
||||
match capture {
|
||||
if cursor.match_all(&[Lt, Lifetime, Gt, CaptureIdent], &mut captures) {
|
||||
match cursor.get_text(captures[0]) {
|
||||
"LateLintPass" => context = Some("LateContext"),
|
||||
"EarlyLintPass" => context = Some("EarlyContext"),
|
||||
_ => {},
|
||||
|
|
|
|||
285
src/tools/clippy/clippy_dev/src/parse.rs
Normal file
285
src/tools/clippy/clippy_dev/src/parse.rs
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
pub mod cursor;
|
||||
|
||||
use self::cursor::{Capture, Cursor};
|
||||
use crate::utils::{ErrAction, File, Scoped, expect_action, walk_dir_no_dot_or_target};
|
||||
use core::fmt::{Display, Write as _};
|
||||
use core::range::Range;
|
||||
use rustc_arena::DroplessArena;
|
||||
use std::fs;
|
||||
use std::path::{self, Path, PathBuf};
|
||||
use std::str::pattern::Pattern;
|
||||
|
||||
pub struct ParseCxImpl<'cx> {
|
||||
pub arena: &'cx DroplessArena,
|
||||
pub str_buf: StrBuf,
|
||||
}
|
||||
pub type ParseCx<'cx> = &'cx mut ParseCxImpl<'cx>;
|
||||
|
||||
/// Calls the given function inside a newly created parsing context.
|
||||
pub fn new_parse_cx<'env, T>(f: impl for<'cx> FnOnce(&'cx mut Scoped<'cx, 'env, ParseCxImpl<'cx>>) -> T) -> T {
|
||||
let arena = DroplessArena::default();
|
||||
f(&mut Scoped::new(ParseCxImpl {
|
||||
arena: &arena,
|
||||
str_buf: StrBuf::with_capacity(128),
|
||||
}))
|
||||
}
|
||||
|
||||
/// A string used as a temporary buffer used to avoid allocating for short lived strings.
|
||||
pub struct StrBuf(String);
|
||||
impl StrBuf {
|
||||
/// Creates a new buffer with the specified initial capacity.
|
||||
pub fn with_capacity(cap: usize) -> Self {
|
||||
Self(String::with_capacity(cap))
|
||||
}
|
||||
|
||||
/// Allocates the result of formatting the given value onto the arena.
|
||||
pub fn alloc_display<'cx>(&mut self, arena: &'cx DroplessArena, value: impl Display) -> &'cx str {
|
||||
self.0.clear();
|
||||
write!(self.0, "{value}").expect("`Display` impl returned an error");
|
||||
arena.alloc_str(&self.0)
|
||||
}
|
||||
|
||||
/// Allocates the string onto the arena with all ascii characters converted to
|
||||
/// lowercase.
|
||||
pub fn alloc_ascii_lower<'cx>(&mut self, arena: &'cx DroplessArena, s: &str) -> &'cx str {
|
||||
self.0.clear();
|
||||
self.0.push_str(s);
|
||||
self.0.make_ascii_lowercase();
|
||||
arena.alloc_str(&self.0)
|
||||
}
|
||||
|
||||
/// Allocates the result of replacing all instances the pattern with the given string
|
||||
/// onto the arena.
|
||||
pub fn alloc_replaced<'cx>(
|
||||
&mut self,
|
||||
arena: &'cx DroplessArena,
|
||||
s: &str,
|
||||
pat: impl Pattern,
|
||||
replacement: &str,
|
||||
) -> &'cx str {
|
||||
let mut parts = s.split(pat);
|
||||
let Some(first) = parts.next() else {
|
||||
return "";
|
||||
};
|
||||
self.0.clear();
|
||||
self.0.push_str(first);
|
||||
for part in parts {
|
||||
self.0.push_str(replacement);
|
||||
self.0.push_str(part);
|
||||
}
|
||||
if self.0.is_empty() {
|
||||
""
|
||||
} else {
|
||||
arena.alloc_str(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs an operation with the freshly cleared buffer.
|
||||
pub fn with<T>(&mut self, f: impl FnOnce(&mut String) -> T) -> T {
|
||||
self.0.clear();
|
||||
f(&mut self.0)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Lint<'cx> {
|
||||
pub name: &'cx str,
|
||||
pub group: &'cx str,
|
||||
pub module: &'cx str,
|
||||
pub path: PathBuf,
|
||||
pub declaration_range: Range<usize>,
|
||||
}
|
||||
|
||||
pub struct DeprecatedLint<'cx> {
|
||||
pub name: &'cx str,
|
||||
pub reason: &'cx str,
|
||||
pub version: &'cx str,
|
||||
}
|
||||
|
||||
pub struct RenamedLint<'cx> {
|
||||
pub old_name: &'cx str,
|
||||
pub new_name: &'cx str,
|
||||
pub version: &'cx str,
|
||||
}
|
||||
|
||||
impl<'cx> ParseCxImpl<'cx> {
|
||||
/// Finds all lint declarations (`declare_clippy_lint!`)
|
||||
#[must_use]
|
||||
pub fn find_lint_decls(&mut self) -> Vec<Lint<'cx>> {
|
||||
let mut lints = Vec::with_capacity(1000);
|
||||
let mut contents = String::new();
|
||||
for e in expect_action(fs::read_dir("."), ErrAction::Read, ".") {
|
||||
let e = expect_action(e, ErrAction::Read, ".");
|
||||
|
||||
// Skip if this isn't a lint crate's directory.
|
||||
let mut crate_path = if expect_action(e.file_type(), ErrAction::Read, ".").is_dir()
|
||||
&& let Ok(crate_path) = e.file_name().into_string()
|
||||
&& crate_path.starts_with("clippy_lints")
|
||||
&& crate_path != "clippy_lints_internal"
|
||||
{
|
||||
crate_path
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
crate_path.push(path::MAIN_SEPARATOR);
|
||||
crate_path.push_str("src");
|
||||
for e in walk_dir_no_dot_or_target(&crate_path) {
|
||||
let e = expect_action(e, ErrAction::Read, &crate_path);
|
||||
if let Some(path) = e.path().to_str()
|
||||
&& let Some(path) = path.strip_suffix(".rs")
|
||||
&& let Some(path) = path.get(crate_path.len() + 1..)
|
||||
{
|
||||
let module = if path == "lib" {
|
||||
""
|
||||
} else {
|
||||
let path = path
|
||||
.strip_suffix("mod")
|
||||
.and_then(|x| x.strip_suffix(path::MAIN_SEPARATOR))
|
||||
.unwrap_or(path);
|
||||
self.str_buf
|
||||
.alloc_replaced(self.arena, path, path::MAIN_SEPARATOR, "::")
|
||||
};
|
||||
self.parse_clippy_lint_decls(
|
||||
e.path(),
|
||||
File::open_read_to_cleared_string(e.path(), &mut contents),
|
||||
module,
|
||||
&mut lints,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
lints.sort_by(|lhs, rhs| lhs.name.cmp(rhs.name));
|
||||
lints
|
||||
}
|
||||
|
||||
/// Parse a source file looking for `declare_clippy_lint` macro invocations.
|
||||
fn parse_clippy_lint_decls(&mut self, path: &Path, contents: &str, module: &'cx str, lints: &mut Vec<Lint<'cx>>) {
|
||||
#[allow(clippy::enum_glob_use)]
|
||||
use cursor::Pat::*;
|
||||
#[rustfmt::skip]
|
||||
static DECL_TOKENS: &[cursor::Pat<'_>] = &[
|
||||
// !{ /// docs
|
||||
Bang, OpenBrace, AnyComment,
|
||||
// #[clippy::version = "version"]
|
||||
Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, LitStr, CloseBracket,
|
||||
// pub NAME, GROUP,
|
||||
Ident("pub"), CaptureIdent, Comma, AnyComment, CaptureIdent, Comma,
|
||||
];
|
||||
|
||||
let mut cursor = Cursor::new(contents);
|
||||
let mut captures = [Capture::EMPTY; 2];
|
||||
while let Some(start) = cursor.find_ident("declare_clippy_lint") {
|
||||
if cursor.match_all(DECL_TOKENS, &mut captures) && cursor.find_pat(CloseBrace) {
|
||||
lints.push(Lint {
|
||||
name: self.str_buf.alloc_ascii_lower(self.arena, cursor.get_text(captures[0])),
|
||||
group: self.arena.alloc_str(cursor.get_text(captures[1])),
|
||||
module,
|
||||
path: path.into(),
|
||||
declaration_range: start as usize..cursor.pos() as usize,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn read_deprecated_lints(&mut self) -> (Vec<DeprecatedLint<'cx>>, Vec<RenamedLint<'cx>>) {
|
||||
#[allow(clippy::enum_glob_use)]
|
||||
use cursor::Pat::*;
|
||||
#[rustfmt::skip]
|
||||
static DECL_TOKENS: &[cursor::Pat<'_>] = &[
|
||||
// #[clippy::version = "version"]
|
||||
Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, CaptureLitStr, CloseBracket,
|
||||
// ("first", "second"),
|
||||
OpenParen, CaptureLitStr, Comma, CaptureLitStr, CloseParen, Comma,
|
||||
];
|
||||
#[rustfmt::skip]
|
||||
static DEPRECATED_TOKENS: &[cursor::Pat<'_>] = &[
|
||||
// !{ DEPRECATED(DEPRECATED_VERSION) = [
|
||||
Bang, OpenBrace, Ident("DEPRECATED"), OpenParen, Ident("DEPRECATED_VERSION"), CloseParen, Eq, OpenBracket,
|
||||
];
|
||||
#[rustfmt::skip]
|
||||
static RENAMED_TOKENS: &[cursor::Pat<'_>] = &[
|
||||
// !{ RENAMED(RENAMED_VERSION) = [
|
||||
Bang, OpenBrace, Ident("RENAMED"), OpenParen, Ident("RENAMED_VERSION"), CloseParen, Eq, OpenBracket,
|
||||
];
|
||||
|
||||
let path = "clippy_lints/src/deprecated_lints.rs";
|
||||
let mut deprecated = Vec::with_capacity(30);
|
||||
let mut renamed = Vec::with_capacity(80);
|
||||
let mut contents = String::new();
|
||||
File::open_read_to_cleared_string(path, &mut contents);
|
||||
|
||||
let mut cursor = Cursor::new(&contents);
|
||||
let mut captures = [Capture::EMPTY; 3];
|
||||
|
||||
// First instance is the macro definition.
|
||||
assert!(
|
||||
cursor.find_ident("declare_with_version").is_some(),
|
||||
"error reading deprecated lints"
|
||||
);
|
||||
|
||||
if cursor.find_ident("declare_with_version").is_some() && cursor.match_all(DEPRECATED_TOKENS, &mut []) {
|
||||
while cursor.match_all(DECL_TOKENS, &mut captures) {
|
||||
deprecated.push(DeprecatedLint {
|
||||
name: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[1])),
|
||||
reason: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])),
|
||||
version: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
panic!("error reading deprecated lints");
|
||||
}
|
||||
|
||||
if cursor.find_ident("declare_with_version").is_some() && cursor.match_all(RENAMED_TOKENS, &mut []) {
|
||||
while cursor.match_all(DECL_TOKENS, &mut captures) {
|
||||
renamed.push(RenamedLint {
|
||||
old_name: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[1])),
|
||||
new_name: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[2])),
|
||||
version: self.parse_str_single_line(path.as_ref(), cursor.get_text(captures[0])),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
panic!("error reading renamed lints");
|
||||
}
|
||||
|
||||
deprecated.sort_by(|lhs, rhs| lhs.name.cmp(rhs.name));
|
||||
renamed.sort_by(|lhs, rhs| lhs.old_name.cmp(rhs.old_name));
|
||||
(deprecated, renamed)
|
||||
}
|
||||
|
||||
/// Removes the line splices and surrounding quotes from a string literal
|
||||
fn parse_str_lit(&mut self, s: &str) -> &'cx str {
|
||||
let (s, is_raw) = if let Some(s) = s.strip_prefix("r") {
|
||||
(s.trim_matches('#'), true)
|
||||
} else {
|
||||
(s, false)
|
||||
};
|
||||
let s = s
|
||||
.strip_prefix('"')
|
||||
.and_then(|s| s.strip_suffix('"'))
|
||||
.unwrap_or_else(|| panic!("expected quoted string, found `{s}`"));
|
||||
|
||||
if is_raw {
|
||||
if s.is_empty() { "" } else { self.arena.alloc_str(s) }
|
||||
} else {
|
||||
self.str_buf.with(|buf| {
|
||||
rustc_literal_escaper::unescape_str(s, &mut |_, ch| {
|
||||
if let Ok(ch) = ch {
|
||||
buf.push(ch);
|
||||
}
|
||||
});
|
||||
if buf.is_empty() { "" } else { self.arena.alloc_str(buf) }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_str_single_line(&mut self, path: &Path, s: &str) -> &'cx str {
|
||||
let value = self.parse_str_lit(s);
|
||||
assert!(
|
||||
!value.contains('\n'),
|
||||
"error parsing `{}`: `{s}` should be a single line string",
|
||||
path.display(),
|
||||
);
|
||||
value
|
||||
}
|
||||
}
|
||||
263
src/tools/clippy/clippy_dev/src/parse/cursor.rs
Normal file
263
src/tools/clippy/clippy_dev/src/parse/cursor.rs
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
use core::slice;
|
||||
use rustc_lexer::{self as lex, LiteralKind, Token, TokenKind};
|
||||
|
||||
/// A token pattern used for searching and matching by the [`Cursor`].
|
||||
///
|
||||
/// In the event that a pattern is a multi-token sequence, earlier tokens will be consumed
|
||||
/// even if the pattern ultimately isn't matched. e.g. With the sequence `:*` matching
|
||||
/// `DoubleColon` will consume the first `:` and then fail to match, leaving the cursor at
|
||||
/// the `*`.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Pat<'a> {
|
||||
/// Matches any number of comments and doc comments.
|
||||
AnyComment,
|
||||
Ident(&'a str),
|
||||
CaptureIdent,
|
||||
LitStr,
|
||||
CaptureLitStr,
|
||||
Bang,
|
||||
CloseBrace,
|
||||
CloseBracket,
|
||||
CloseParen,
|
||||
Comma,
|
||||
DoubleColon,
|
||||
Eq,
|
||||
Lifetime,
|
||||
Lt,
|
||||
Gt,
|
||||
OpenBrace,
|
||||
OpenBracket,
|
||||
OpenParen,
|
||||
Pound,
|
||||
Semi,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Capture {
|
||||
pub pos: u32,
|
||||
pub len: u32,
|
||||
}
|
||||
impl Capture {
|
||||
pub const EMPTY: Self = Self { pos: 0, len: 0 };
|
||||
}
|
||||
|
||||
/// A unidirectional cursor over a token stream that is lexed on demand.
|
||||
pub struct Cursor<'txt> {
|
||||
next_token: Token,
|
||||
pos: u32,
|
||||
inner: lex::Cursor<'txt>,
|
||||
text: &'txt str,
|
||||
}
|
||||
impl<'txt> Cursor<'txt> {
|
||||
#[must_use]
|
||||
pub fn new(text: &'txt str) -> Self {
|
||||
let mut inner = lex::Cursor::new(text, lex::FrontmatterAllowed::Yes);
|
||||
Self {
|
||||
next_token: inner.advance_token(),
|
||||
pos: 0,
|
||||
inner,
|
||||
text,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the text of the captured token assuming it came from this cursor.
|
||||
#[must_use]
|
||||
pub fn get_text(&self, capture: Capture) -> &'txt str {
|
||||
&self.text[capture.pos as usize..(capture.pos + capture.len) as usize]
|
||||
}
|
||||
|
||||
/// Gets the text that makes up the next token in the stream, or the empty string if
|
||||
/// stream is exhausted.
|
||||
#[must_use]
|
||||
pub fn peek_text(&self) -> &'txt str {
|
||||
&self.text[self.pos as usize..(self.pos + self.next_token.len) as usize]
|
||||
}
|
||||
|
||||
/// Gets the length of the next token in bytes, or zero if the stream is exhausted.
|
||||
#[must_use]
|
||||
pub fn peek_len(&self) -> u32 {
|
||||
self.next_token.len
|
||||
}
|
||||
|
||||
/// Gets the next token in the stream, or [`TokenKind::Eof`] if the stream is
|
||||
/// exhausted.
|
||||
#[must_use]
|
||||
pub fn peek(&self) -> TokenKind {
|
||||
self.next_token.kind
|
||||
}
|
||||
|
||||
/// Gets the offset of the next token in the source string, or the string's length if
|
||||
/// the stream is exhausted.
|
||||
#[must_use]
|
||||
pub fn pos(&self) -> u32 {
|
||||
self.pos
|
||||
}
|
||||
|
||||
/// Gets whether the cursor has exhausted its input.
|
||||
#[must_use]
|
||||
pub fn at_end(&self) -> bool {
|
||||
self.next_token.kind == TokenKind::Eof
|
||||
}
|
||||
|
||||
/// Advances the cursor to the next token. If the stream is exhausted this will set
|
||||
/// the next token to [`TokenKind::Eof`].
|
||||
pub fn step(&mut self) {
|
||||
// `next_token.len` is zero for the eof marker.
|
||||
self.pos += self.next_token.len;
|
||||
self.next_token = self.inner.advance_token();
|
||||
}
|
||||
|
||||
/// Consumes tokens until the given pattern is either fully matched of fails to match.
|
||||
/// Returns whether the pattern was fully matched.
|
||||
///
|
||||
/// For each capture made by the pattern one item will be taken from the capture
|
||||
/// sequence with the result placed inside.
|
||||
fn match_impl(&mut self, pat: Pat<'_>, captures: &mut slice::IterMut<'_, Capture>) -> bool {
|
||||
loop {
|
||||
match (pat, self.next_token.kind) {
|
||||
#[rustfmt::skip] // rustfmt bug: https://github.com/rust-lang/rustfmt/issues/6697
|
||||
(_, TokenKind::Whitespace)
|
||||
| (
|
||||
Pat::AnyComment,
|
||||
TokenKind::BlockComment { terminated: true, .. } | TokenKind::LineComment { .. },
|
||||
) => self.step(),
|
||||
(Pat::AnyComment, _) => return true,
|
||||
(Pat::Bang, TokenKind::Bang)
|
||||
| (Pat::CloseBrace, TokenKind::CloseBrace)
|
||||
| (Pat::CloseBracket, TokenKind::CloseBracket)
|
||||
| (Pat::CloseParen, TokenKind::CloseParen)
|
||||
| (Pat::Comma, TokenKind::Comma)
|
||||
| (Pat::Eq, TokenKind::Eq)
|
||||
| (Pat::Lifetime, TokenKind::Lifetime { .. })
|
||||
| (Pat::Lt, TokenKind::Lt)
|
||||
| (Pat::Gt, TokenKind::Gt)
|
||||
| (Pat::OpenBrace, TokenKind::OpenBrace)
|
||||
| (Pat::OpenBracket, TokenKind::OpenBracket)
|
||||
| (Pat::OpenParen, TokenKind::OpenParen)
|
||||
| (Pat::Pound, TokenKind::Pound)
|
||||
| (Pat::Semi, TokenKind::Semi)
|
||||
| (
|
||||
Pat::LitStr,
|
||||
TokenKind::Literal {
|
||||
kind: LiteralKind::Str { terminated: true } | LiteralKind::RawStr { .. },
|
||||
..
|
||||
},
|
||||
) => {
|
||||
self.step();
|
||||
return true;
|
||||
},
|
||||
(Pat::Ident(x), TokenKind::Ident) if x == self.peek_text() => {
|
||||
self.step();
|
||||
return true;
|
||||
},
|
||||
(Pat::DoubleColon, TokenKind::Colon) => {
|
||||
self.step();
|
||||
if !self.at_end() && matches!(self.next_token.kind, TokenKind::Colon) {
|
||||
self.step();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
#[rustfmt::skip]
|
||||
(
|
||||
Pat::CaptureLitStr,
|
||||
TokenKind::Literal {
|
||||
kind:
|
||||
LiteralKind::Str { terminated: true }
|
||||
| LiteralKind::RawStr { n_hashes: Some(_) },
|
||||
..
|
||||
},
|
||||
)
|
||||
| (Pat::CaptureIdent, TokenKind::Ident) => {
|
||||
*captures.next().unwrap() = Capture { pos: self.pos, len: self.next_token.len };
|
||||
self.step();
|
||||
return true;
|
||||
},
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes all tokens until the specified identifier is found and returns its
|
||||
/// position. Returns `None` if the identifier could not be found.
|
||||
///
|
||||
/// The cursor will be positioned immediately after the identifier, or at the end if
|
||||
/// it is not.
|
||||
pub fn find_ident(&mut self, ident: &str) -> Option<u32> {
|
||||
loop {
|
||||
match self.next_token.kind {
|
||||
TokenKind::Ident if self.peek_text() == ident => {
|
||||
let pos = self.pos;
|
||||
self.step();
|
||||
return Some(pos);
|
||||
},
|
||||
TokenKind::Eof => return None,
|
||||
_ => self.step(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Consumes all tokens until the next identifier is found and captures it. Returns
|
||||
/// `None` if no identifier could be found.
|
||||
///
|
||||
/// The cursor will be positioned immediately after the identifier, or at the end if
|
||||
/// it is not.
|
||||
pub fn find_any_ident(&mut self) -> Option<Capture> {
|
||||
loop {
|
||||
match self.next_token.kind {
|
||||
TokenKind::Ident => {
|
||||
let res = Capture {
|
||||
pos: self.pos,
|
||||
len: self.next_token.len,
|
||||
};
|
||||
self.step();
|
||||
return Some(res);
|
||||
},
|
||||
TokenKind::Eof => return None,
|
||||
_ => self.step(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Continually attempt to match the pattern on subsequent tokens until a match is
|
||||
/// found. Returns whether the pattern was successfully matched.
|
||||
///
|
||||
/// Not generally suitable for multi-token patterns or patterns that can match
|
||||
/// nothing.
|
||||
#[must_use]
|
||||
pub fn find_pat(&mut self, pat: Pat<'_>) -> bool {
|
||||
let mut capture = [].iter_mut();
|
||||
while !self.match_impl(pat, &mut capture) {
|
||||
self.step();
|
||||
if self.at_end() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/// Attempts to match a sequence of patterns at the current position. Returns whether
|
||||
/// all patterns were successfully matched.
|
||||
///
|
||||
/// Captures will be written to the given slice in the order they're matched. If a
|
||||
/// capture is matched, but there are no more capture slots this will panic. If the
|
||||
/// match is completed without filling all the capture slots they will be left
|
||||
/// unmodified.
|
||||
///
|
||||
/// If the match fails the cursor will be positioned at the first failing token.
|
||||
#[must_use]
|
||||
pub fn match_all(&mut self, pats: &[Pat<'_>], captures: &mut [Capture]) -> bool {
|
||||
let mut captures = captures.iter_mut();
|
||||
pats.iter().all(|&p| self.match_impl(p, &mut captures))
|
||||
}
|
||||
|
||||
/// Attempts to match a single pattern at the current position. Returns whether the
|
||||
/// pattern was successfully matched.
|
||||
///
|
||||
/// If the pattern attempts to capture anything this will panic. If the match fails
|
||||
/// the cursor will be positioned at the first failing token.
|
||||
#[must_use]
|
||||
pub fn match_pat(&mut self, pat: Pat<'_>) -> bool {
|
||||
self.match_impl(pat, &mut [].iter_mut())
|
||||
}
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@ pub fn bump_version(mut version: Version) {
|
|||
dst.push_str(&src[..package.version_range.start]);
|
||||
write!(dst, "\"{}\"", version.toml_display()).unwrap();
|
||||
dst.push_str(&src[package.version_range.end..]);
|
||||
UpdateStatus::from_changed(src.get(package.version_range.clone()) != dst.get(package.version_range))
|
||||
UpdateStatus::from_changed(src.get(package.version_range) != dst.get(package.version_range))
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
use crate::update_lints::{RenamedLint, find_lint_decls, generate_lint_files, read_deprecated_lints};
|
||||
use crate::parse::cursor::{self, Capture, Cursor};
|
||||
use crate::parse::{ParseCx, RenamedLint};
|
||||
use crate::update_lints::generate_lint_files;
|
||||
use crate::utils::{
|
||||
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,
|
||||
ErrAction, FileUpdater, 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;
|
||||
|
|
@ -24,36 +26,35 @@ use std::path::Path;
|
|||
/// * If `old_name` doesn't name an existing lint.
|
||||
/// * If `old_name` names a deprecated or renamed lint.
|
||||
#[expect(clippy::too_many_lines)]
|
||||
pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: bool) {
|
||||
if let Some((prefix, _)) = old_name.split_once("::") {
|
||||
panic!("`{old_name}` should not contain the `{prefix}` prefix");
|
||||
}
|
||||
if let Some((prefix, _)) = new_name.split_once("::") {
|
||||
panic!("`{new_name}` should not contain the `{prefix}` prefix");
|
||||
}
|
||||
|
||||
pub fn rename<'cx>(cx: ParseCx<'cx>, clippy_version: Version, old_name: &'cx str, new_name: &'cx str, uplift: bool) {
|
||||
let mut updater = FileUpdater::default();
|
||||
let mut lints = find_lint_decls();
|
||||
let (deprecated_lints, mut renamed_lints) = read_deprecated_lints();
|
||||
let mut lints = cx.find_lint_decls();
|
||||
let (deprecated_lints, mut renamed_lints) = cx.read_deprecated_lints();
|
||||
|
||||
let Ok(lint_idx) = lints.binary_search_by(|x| x.name.as_str().cmp(old_name)) else {
|
||||
let Ok(lint_idx) = lints.binary_search_by(|x| x.name.cmp(old_name)) else {
|
||||
panic!("could not find lint `{old_name}`");
|
||||
};
|
||||
let lint = &lints[lint_idx];
|
||||
|
||||
let old_name_prefixed = String::from_iter(["clippy::", old_name]);
|
||||
let old_name_prefixed = cx.str_buf.with(|buf| {
|
||||
buf.extend(["clippy::", old_name]);
|
||||
cx.arena.alloc_str(buf)
|
||||
});
|
||||
let new_name_prefixed = if uplift {
|
||||
new_name.to_owned()
|
||||
new_name
|
||||
} else {
|
||||
String::from_iter(["clippy::", new_name])
|
||||
cx.str_buf.with(|buf| {
|
||||
buf.extend(["clippy::", new_name]);
|
||||
cx.arena.alloc_str(buf)
|
||||
})
|
||||
};
|
||||
|
||||
for lint in &mut renamed_lints {
|
||||
if lint.new_name == old_name_prefixed {
|
||||
lint.new_name.clone_from(&new_name_prefixed);
|
||||
lint.new_name = new_name_prefixed;
|
||||
}
|
||||
}
|
||||
match renamed_lints.binary_search_by(|x| x.old_name.cmp(&old_name_prefixed)) {
|
||||
match renamed_lints.binary_search_by(|x| x.old_name.cmp(old_name_prefixed)) {
|
||||
Ok(_) => {
|
||||
println!("`{old_name}` already has a rename registered");
|
||||
return;
|
||||
|
|
@ -63,12 +64,8 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b
|
|||
idx,
|
||||
RenamedLint {
|
||||
old_name: old_name_prefixed,
|
||||
new_name: if uplift {
|
||||
new_name.to_owned()
|
||||
} else {
|
||||
String::from_iter(["clippy::", new_name])
|
||||
},
|
||||
version: clippy_version.rust_display().to_string(),
|
||||
new_name: new_name_prefixed,
|
||||
version: cx.str_buf.alloc_display(cx.arena, clippy_version.rust_display()),
|
||||
},
|
||||
);
|
||||
},
|
||||
|
|
@ -104,7 +101,7 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b
|
|||
}
|
||||
delete_test_files(old_name, change_prefixed_tests);
|
||||
lints.remove(lint_idx);
|
||||
} else if lints.binary_search_by(|x| x.name.as_str().cmp(new_name)).is_err() {
|
||||
} else if lints.binary_search_by(|x| x.name.cmp(new_name)).is_err() {
|
||||
let lint = &mut lints[lint_idx];
|
||||
if lint.module.ends_with(old_name)
|
||||
&& lint
|
||||
|
|
@ -118,13 +115,15 @@ pub fn rename(clippy_version: Version, old_name: &str, new_name: &str, uplift: b
|
|||
mod_edit = ModEdit::Rename;
|
||||
}
|
||||
|
||||
let mod_len = lint.module.len();
|
||||
lint.module.truncate(mod_len - old_name.len());
|
||||
lint.module.push_str(new_name);
|
||||
lint.module = cx.str_buf.with(|buf| {
|
||||
buf.push_str(&lint.module[..lint.module.len() - old_name.len()]);
|
||||
buf.push_str(new_name);
|
||||
cx.arena.alloc_str(buf)
|
||||
});
|
||||
}
|
||||
rename_test_files(old_name, new_name, change_prefixed_tests);
|
||||
new_name.clone_into(&mut lints[lint_idx].name);
|
||||
lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name));
|
||||
lints[lint_idx].name = new_name;
|
||||
lints.sort_by(|lhs, rhs| lhs.name.cmp(rhs.name));
|
||||
} else {
|
||||
println!("Renamed `clippy::{old_name}` to `clippy::{new_name}`");
|
||||
println!("Since `{new_name}` already exists the existing code has not been changed");
|
||||
|
|
@ -132,7 +131,7 @@ 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 e in walk_dir_no_dot_or_target() {
|
||||
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);
|
||||
|
|
@ -285,47 +284,46 @@ fn file_update_fn<'a, 'b>(
|
|||
move |_, src, dst| {
|
||||
let mut copy_pos = 0u32;
|
||||
let mut changed = false;
|
||||
let mut searcher = RustSearcher::new(src);
|
||||
let mut capture = "";
|
||||
let mut cursor = Cursor::new(src);
|
||||
let mut captures = [Capture::EMPTY];
|
||||
loop {
|
||||
match searcher.peek() {
|
||||
match cursor.peek() {
|
||||
TokenKind::Eof => break,
|
||||
TokenKind::Ident => {
|
||||
let match_start = searcher.pos();
|
||||
let text = searcher.peek_text();
|
||||
searcher.step();
|
||||
let match_start = cursor.pos();
|
||||
let text = cursor.peek_text();
|
||||
cursor.step();
|
||||
match text {
|
||||
// clippy::line_name or clippy::lint-name
|
||||
"clippy" => {
|
||||
if searcher.match_tokens(&[Token::DoubleColon, Token::CaptureIdent], &mut [&mut capture])
|
||||
&& capture == old_name
|
||||
if cursor.match_all(&[cursor::Pat::DoubleColon, cursor::Pat::CaptureIdent], &mut captures)
|
||||
&& cursor.get_text(captures[0]) == old_name
|
||||
{
|
||||
dst.push_str(&src[copy_pos as usize..searcher.pos() as usize - capture.len()]);
|
||||
dst.push_str(&src[copy_pos as usize..captures[0].pos as usize]);
|
||||
dst.push_str(new_name);
|
||||
copy_pos = searcher.pos();
|
||||
copy_pos = cursor.pos();
|
||||
changed = true;
|
||||
}
|
||||
},
|
||||
// mod lint_name
|
||||
"mod" => {
|
||||
if !matches!(mod_edit, ModEdit::None)
|
||||
&& searcher.match_tokens(&[Token::CaptureIdent], &mut [&mut capture])
|
||||
&& capture == old_name
|
||||
&& let Some(pos) = cursor.find_ident(old_name)
|
||||
{
|
||||
match mod_edit {
|
||||
ModEdit::Rename => {
|
||||
dst.push_str(&src[copy_pos as usize..searcher.pos() as usize - capture.len()]);
|
||||
dst.push_str(&src[copy_pos as usize..pos as usize]);
|
||||
dst.push_str(new_name);
|
||||
copy_pos = searcher.pos();
|
||||
copy_pos = cursor.pos();
|
||||
changed = true;
|
||||
},
|
||||
ModEdit::Delete if searcher.match_tokens(&[Token::Semi], &mut []) => {
|
||||
ModEdit::Delete if cursor.match_pat(cursor::Pat::Semi) => {
|
||||
let mut start = &src[copy_pos as usize..match_start as usize];
|
||||
if start.ends_with("\n\n") {
|
||||
start = &start[..start.len() - 1];
|
||||
}
|
||||
dst.push_str(start);
|
||||
copy_pos = searcher.pos();
|
||||
copy_pos = cursor.pos();
|
||||
if src[copy_pos as usize..].starts_with("\n\n") {
|
||||
copy_pos += 1;
|
||||
}
|
||||
|
|
@ -337,8 +335,8 @@ fn file_update_fn<'a, 'b>(
|
|||
},
|
||||
// lint_name::
|
||||
name if matches!(mod_edit, ModEdit::Rename) && name == old_name => {
|
||||
let name_end = searcher.pos();
|
||||
if searcher.match_tokens(&[Token::DoubleColon], &mut []) {
|
||||
let name_end = cursor.pos();
|
||||
if cursor.match_pat(cursor::Pat::DoubleColon) {
|
||||
dst.push_str(&src[copy_pos as usize..match_start as usize]);
|
||||
dst.push_str(new_name);
|
||||
copy_pos = name_end;
|
||||
|
|
@ -356,36 +354,36 @@ fn file_update_fn<'a, 'b>(
|
|||
};
|
||||
dst.push_str(&src[copy_pos as usize..match_start as usize]);
|
||||
dst.push_str(replacement);
|
||||
copy_pos = searcher.pos();
|
||||
copy_pos = cursor.pos();
|
||||
changed = true;
|
||||
},
|
||||
}
|
||||
},
|
||||
// //~ lint_name
|
||||
TokenKind::LineComment { doc_style: None } => {
|
||||
let text = searcher.peek_text();
|
||||
let text = cursor.peek_text();
|
||||
if text.starts_with("//~")
|
||||
&& let Some(text) = text.strip_suffix(old_name)
|
||||
&& !text.ends_with(|c| matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '_'))
|
||||
{
|
||||
dst.push_str(&src[copy_pos as usize..searcher.pos() as usize + text.len()]);
|
||||
dst.push_str(&src[copy_pos as usize..cursor.pos() as usize + text.len()]);
|
||||
dst.push_str(new_name);
|
||||
copy_pos = searcher.pos() + searcher.peek_len();
|
||||
copy_pos = cursor.pos() + cursor.peek_len();
|
||||
changed = true;
|
||||
}
|
||||
searcher.step();
|
||||
cursor.step();
|
||||
},
|
||||
// ::lint_name
|
||||
TokenKind::Colon
|
||||
if searcher.match_tokens(&[Token::DoubleColon, Token::CaptureIdent], &mut [&mut capture])
|
||||
&& capture == old_name =>
|
||||
if cursor.match_all(&[cursor::Pat::DoubleColon, cursor::Pat::CaptureIdent], &mut captures)
|
||||
&& cursor.get_text(captures[0]) == old_name =>
|
||||
{
|
||||
dst.push_str(&src[copy_pos as usize..searcher.pos() as usize - capture.len()]);
|
||||
dst.push_str(&src[copy_pos as usize..captures[0].pos as usize]);
|
||||
dst.push_str(new_name);
|
||||
copy_pos = searcher.pos();
|
||||
copy_pos = cursor.pos();
|
||||
changed = true;
|
||||
},
|
||||
_ => searcher.step(),
|
||||
_ => cursor.step(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
use crate::utils::{
|
||||
ErrAction, File, FileUpdater, RustSearcher, Token, UpdateMode, UpdateStatus, expect_action, update_text_region_fn,
|
||||
};
|
||||
use crate::parse::cursor::Cursor;
|
||||
use crate::parse::{DeprecatedLint, Lint, ParseCx, RenamedLint};
|
||||
use crate::utils::{FileUpdater, UpdateMode, UpdateStatus, update_text_region_fn};
|
||||
use itertools::Itertools;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::Write;
|
||||
use std::fs;
|
||||
use std::ops::Range;
|
||||
use std::path::{self, Path, PathBuf};
|
||||
use walkdir::{DirEntry, WalkDir};
|
||||
use std::path::{self, Path};
|
||||
|
||||
const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\
|
||||
// Use that command to update this file and do not edit by hand.\n\
|
||||
|
|
@ -24,18 +21,18 @@ const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.ht
|
|||
/// # Panics
|
||||
///
|
||||
/// Panics if a file path could not read from or then written to
|
||||
pub fn update(update_mode: UpdateMode) {
|
||||
let lints = find_lint_decls();
|
||||
let (deprecated, renamed) = read_deprecated_lints();
|
||||
pub fn update(cx: ParseCx<'_>, update_mode: UpdateMode) {
|
||||
let lints = cx.find_lint_decls();
|
||||
let (deprecated, renamed) = cx.read_deprecated_lints();
|
||||
generate_lint_files(update_mode, &lints, &deprecated, &renamed);
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_lines)]
|
||||
pub fn generate_lint_files(
|
||||
update_mode: UpdateMode,
|
||||
lints: &[Lint],
|
||||
deprecated: &[DeprecatedLint],
|
||||
renamed: &[RenamedLint],
|
||||
lints: &[Lint<'_>],
|
||||
deprecated: &[DeprecatedLint<'_>],
|
||||
renamed: &[RenamedLint<'_>],
|
||||
) {
|
||||
let mut updater = FileUpdater::default();
|
||||
updater.update_file_checked(
|
||||
|
|
@ -64,7 +61,7 @@ pub fn generate_lint_files(
|
|||
|dst| {
|
||||
for lint in lints
|
||||
.iter()
|
||||
.map(|l| &*l.name)
|
||||
.map(|l| l.name)
|
||||
.chain(deprecated.iter().filter_map(|l| l.name.strip_prefix("clippy::")))
|
||||
.chain(renamed.iter().filter_map(|l| l.old_name.strip_prefix("clippy::")))
|
||||
.sorted()
|
||||
|
|
@ -79,13 +76,13 @@ pub fn generate_lint_files(
|
|||
update_mode,
|
||||
"clippy_lints/src/deprecated_lints.rs",
|
||||
&mut |_, src, dst| {
|
||||
let mut searcher = RustSearcher::new(src);
|
||||
let mut cursor = Cursor::new(src);
|
||||
assert!(
|
||||
searcher.find_token(Token::Ident("declare_with_version"))
|
||||
&& searcher.find_token(Token::Ident("declare_with_version")),
|
||||
cursor.find_ident("declare_with_version").is_some()
|
||||
&& cursor.find_ident("declare_with_version").is_some(),
|
||||
"error reading deprecated lints"
|
||||
);
|
||||
dst.push_str(&src[..searcher.pos() as usize]);
|
||||
dst.push_str(&src[..cursor.pos() as usize]);
|
||||
dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n");
|
||||
for lint in deprecated {
|
||||
write!(
|
||||
|
|
@ -135,13 +132,13 @@ pub fn generate_lint_files(
|
|||
dst.push_str(GENERATED_FILE_COMMENT);
|
||||
dst.push_str("#![allow(clippy::duplicated_attributes)]\n");
|
||||
for lint in renamed {
|
||||
if seen_lints.insert(&lint.new_name) {
|
||||
if seen_lints.insert(lint.new_name) {
|
||||
writeln!(dst, "#![allow({})]", lint.new_name).unwrap();
|
||||
}
|
||||
}
|
||||
seen_lints.clear();
|
||||
for lint in renamed {
|
||||
if seen_lints.insert(&lint.old_name) {
|
||||
if seen_lints.insert(lint.old_name) {
|
||||
writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap();
|
||||
}
|
||||
}
|
||||
|
|
@ -167,7 +164,7 @@ pub fn generate_lint_files(
|
|||
for lint_mod in lints
|
||||
.iter()
|
||||
.filter(|l| !l.module.is_empty())
|
||||
.map(|l| l.module.split_once("::").map_or(&*l.module, |x| x.0))
|
||||
.map(|l| l.module.split_once("::").map_or(l.module, |x| x.0))
|
||||
.sorted()
|
||||
.dedup()
|
||||
{
|
||||
|
|
@ -200,260 +197,3 @@ pub fn generate_lint_files(
|
|||
fn round_to_fifty(count: usize) -> usize {
|
||||
count / 50 * 50
|
||||
}
|
||||
|
||||
/// Lint data parsed from the Clippy source code.
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
pub struct Lint {
|
||||
pub name: String,
|
||||
pub group: String,
|
||||
pub module: String,
|
||||
pub path: PathBuf,
|
||||
pub declaration_range: Range<usize>,
|
||||
}
|
||||
|
||||
pub struct DeprecatedLint {
|
||||
pub name: String,
|
||||
pub reason: String,
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
pub struct RenamedLint {
|
||||
pub old_name: String,
|
||||
pub new_name: String,
|
||||
pub version: String,
|
||||
}
|
||||
|
||||
/// Finds all lint declarations (`declare_clippy_lint!`)
|
||||
#[must_use]
|
||||
pub fn find_lint_decls() -> Vec<Lint> {
|
||||
let mut lints = Vec::with_capacity(1000);
|
||||
let mut contents = String::new();
|
||||
for e in expect_action(fs::read_dir("."), ErrAction::Read, ".") {
|
||||
let e = expect_action(e, ErrAction::Read, ".");
|
||||
if !expect_action(e.file_type(), ErrAction::Read, ".").is_dir() {
|
||||
continue;
|
||||
}
|
||||
let Ok(mut name) = e.file_name().into_string() else {
|
||||
continue;
|
||||
};
|
||||
if name.starts_with("clippy_lints") && name != "clippy_lints_internal" {
|
||||
name.push_str("/src");
|
||||
for (file, module) in read_src_with_module(name.as_ref()) {
|
||||
parse_clippy_lint_decls(
|
||||
file.path(),
|
||||
File::open_read_to_cleared_string(file.path(), &mut contents),
|
||||
&module,
|
||||
&mut lints,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name));
|
||||
lints
|
||||
}
|
||||
|
||||
/// 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 = 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(src_root.as_os_str().len() + 1..)
|
||||
{
|
||||
if path == b"lib" {
|
||||
Some((e, String::new()))
|
||||
} else {
|
||||
let path = if let Some(path) = path.strip_suffix(b"mod")
|
||||
&& let Some(path) = path.strip_suffix(b"/").or_else(|| path.strip_suffix(b"\\"))
|
||||
{
|
||||
path
|
||||
} else {
|
||||
path
|
||||
};
|
||||
if let Ok(path) = str::from_utf8(path) {
|
||||
let path = path.replace(['/', '\\'], "::");
|
||||
Some((e, path))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse a source file looking for `declare_clippy_lint` macro invocations.
|
||||
fn parse_clippy_lint_decls(path: &Path, contents: &str, module: &str, lints: &mut Vec<Lint>) {
|
||||
#[allow(clippy::enum_glob_use)]
|
||||
use Token::*;
|
||||
#[rustfmt::skip]
|
||||
static DECL_TOKENS: &[Token<'_>] = &[
|
||||
// !{ /// docs
|
||||
Bang, OpenBrace, AnyComment,
|
||||
// #[clippy::version = "version"]
|
||||
Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, LitStr, CloseBracket,
|
||||
// pub NAME, GROUP,
|
||||
Ident("pub"), CaptureIdent, Comma, AnyComment, CaptureIdent, Comma,
|
||||
];
|
||||
|
||||
let mut searcher = RustSearcher::new(contents);
|
||||
while searcher.find_token(Ident("declare_clippy_lint")) {
|
||||
let start = searcher.pos() as usize - "declare_clippy_lint".len();
|
||||
let (mut name, mut group) = ("", "");
|
||||
if searcher.match_tokens(DECL_TOKENS, &mut [&mut name, &mut group]) && searcher.find_token(CloseBrace) {
|
||||
lints.push(Lint {
|
||||
name: name.to_lowercase(),
|
||||
group: group.into(),
|
||||
module: module.into(),
|
||||
path: path.into(),
|
||||
declaration_range: start..searcher.pos() as usize,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn read_deprecated_lints() -> (Vec<DeprecatedLint>, Vec<RenamedLint>) {
|
||||
#[allow(clippy::enum_glob_use)]
|
||||
use Token::*;
|
||||
#[rustfmt::skip]
|
||||
static DECL_TOKENS: &[Token<'_>] = &[
|
||||
// #[clippy::version = "version"]
|
||||
Pound, OpenBracket, Ident("clippy"), DoubleColon, Ident("version"), Eq, CaptureLitStr, CloseBracket,
|
||||
// ("first", "second"),
|
||||
OpenParen, CaptureLitStr, Comma, CaptureLitStr, CloseParen, Comma,
|
||||
];
|
||||
#[rustfmt::skip]
|
||||
static DEPRECATED_TOKENS: &[Token<'_>] = &[
|
||||
// !{ DEPRECATED(DEPRECATED_VERSION) = [
|
||||
Bang, OpenBrace, Ident("DEPRECATED"), OpenParen, Ident("DEPRECATED_VERSION"), CloseParen, Eq, OpenBracket,
|
||||
];
|
||||
#[rustfmt::skip]
|
||||
static RENAMED_TOKENS: &[Token<'_>] = &[
|
||||
// !{ RENAMED(RENAMED_VERSION) = [
|
||||
Bang, OpenBrace, Ident("RENAMED"), OpenParen, Ident("RENAMED_VERSION"), CloseParen, Eq, OpenBracket,
|
||||
];
|
||||
|
||||
let path = "clippy_lints/src/deprecated_lints.rs";
|
||||
let mut deprecated = Vec::with_capacity(30);
|
||||
let mut renamed = Vec::with_capacity(80);
|
||||
let mut contents = String::new();
|
||||
File::open_read_to_cleared_string(path, &mut contents);
|
||||
|
||||
let mut searcher = RustSearcher::new(&contents);
|
||||
|
||||
// First instance is the macro definition.
|
||||
assert!(
|
||||
searcher.find_token(Ident("declare_with_version")),
|
||||
"error reading deprecated lints"
|
||||
);
|
||||
|
||||
if searcher.find_token(Ident("declare_with_version")) && searcher.match_tokens(DEPRECATED_TOKENS, &mut []) {
|
||||
let mut version = "";
|
||||
let mut name = "";
|
||||
let mut reason = "";
|
||||
while searcher.match_tokens(DECL_TOKENS, &mut [&mut version, &mut name, &mut reason]) {
|
||||
deprecated.push(DeprecatedLint {
|
||||
name: parse_str_single_line(path.as_ref(), name),
|
||||
reason: parse_str_single_line(path.as_ref(), reason),
|
||||
version: parse_str_single_line(path.as_ref(), version),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
panic!("error reading deprecated lints");
|
||||
}
|
||||
|
||||
if searcher.find_token(Ident("declare_with_version")) && searcher.match_tokens(RENAMED_TOKENS, &mut []) {
|
||||
let mut version = "";
|
||||
let mut old_name = "";
|
||||
let mut new_name = "";
|
||||
while searcher.match_tokens(DECL_TOKENS, &mut [&mut version, &mut old_name, &mut new_name]) {
|
||||
renamed.push(RenamedLint {
|
||||
old_name: parse_str_single_line(path.as_ref(), old_name),
|
||||
new_name: parse_str_single_line(path.as_ref(), new_name),
|
||||
version: parse_str_single_line(path.as_ref(), version),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
panic!("error reading renamed lints");
|
||||
}
|
||||
|
||||
deprecated.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name));
|
||||
renamed.sort_by(|lhs, rhs| lhs.old_name.cmp(&rhs.old_name));
|
||||
(deprecated, renamed)
|
||||
}
|
||||
|
||||
/// Removes the line splices and surrounding quotes from a string literal
|
||||
fn parse_str_lit(s: &str) -> String {
|
||||
let s = s.strip_prefix("r").unwrap_or(s).trim_matches('#');
|
||||
let s = s
|
||||
.strip_prefix('"')
|
||||
.and_then(|s| s.strip_suffix('"'))
|
||||
.unwrap_or_else(|| panic!("expected quoted string, found `{s}`"));
|
||||
let mut res = String::with_capacity(s.len());
|
||||
rustc_literal_escaper::unescape_str(s, &mut |_, ch| {
|
||||
if let Ok(ch) = ch {
|
||||
res.push(ch);
|
||||
}
|
||||
});
|
||||
res
|
||||
}
|
||||
|
||||
fn parse_str_single_line(path: &Path, s: &str) -> String {
|
||||
let value = parse_str_lit(s);
|
||||
assert!(
|
||||
!value.contains('\n'),
|
||||
"error parsing `{}`: `{s}` should be a single line string",
|
||||
path.display(),
|
||||
);
|
||||
value
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_clippy_lint_decls() {
|
||||
static CONTENTS: &str = r#"
|
||||
declare_clippy_lint! {
|
||||
#[clippy::version = "Hello Clippy!"]
|
||||
pub PTR_ARG,
|
||||
style,
|
||||
"really long \
|
||||
text"
|
||||
}
|
||||
|
||||
declare_clippy_lint!{
|
||||
#[clippy::version = "Test version"]
|
||||
pub DOC_MARKDOWN,
|
||||
pedantic,
|
||||
"single line"
|
||||
}
|
||||
"#;
|
||||
let mut result = Vec::new();
|
||||
parse_clippy_lint_decls("".as_ref(), CONTENTS, "module_name", &mut result);
|
||||
for r in &mut result {
|
||||
r.declaration_range = Range::default();
|
||||
}
|
||||
|
||||
let expected = vec![
|
||||
Lint {
|
||||
name: "ptr_arg".into(),
|
||||
group: "style".into(),
|
||||
module: "module_name".into(),
|
||||
path: PathBuf::new(),
|
||||
declaration_range: Range::default(),
|
||||
},
|
||||
Lint {
|
||||
name: "doc_markdown".into(),
|
||||
group: "pedantic".into(),
|
||||
module: "module_name".into(),
|
||||
path: PathBuf::new(),
|
||||
declaration_range: Range::default(),
|
||||
},
|
||||
];
|
||||
assert_eq!(expected, result);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
use core::fmt::{self, Display};
|
||||
use core::marker::PhantomData;
|
||||
use core::num::NonZero;
|
||||
use core::ops::Range;
|
||||
use core::slice;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use core::range::Range;
|
||||
use core::str::FromStr;
|
||||
use rustc_lexer::{self as lexer, FrontmatterAllowed};
|
||||
use std::ffi::OsStr;
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io::{self, Read as _, Seek as _, SeekFrom, Write};
|
||||
|
|
@ -12,6 +12,24 @@ use std::process::{self, Command, Stdio};
|
|||
use std::{env, thread};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
pub struct Scoped<'inner, 'outer: 'inner, T>(T, PhantomData<&'inner mut T>, PhantomData<&'outer mut ()>);
|
||||
impl<T> Scoped<'_, '_, T> {
|
||||
pub fn new(value: T) -> Self {
|
||||
Self(value, PhantomData, PhantomData)
|
||||
}
|
||||
}
|
||||
impl<T> Deref for Scoped<'_, '_, T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
impl<T> DerefMut for Scoped<'_, '_, T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum ErrAction {
|
||||
Open,
|
||||
|
|
@ -410,179 +428,6 @@ pub fn update_text_region_fn(
|
|||
move |path, src, dst| update_text_region(path, start, end, src, dst, &mut insert)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Token<'a> {
|
||||
/// Matches any number of comments / doc comments.
|
||||
AnyComment,
|
||||
Ident(&'a str),
|
||||
CaptureIdent,
|
||||
LitStr,
|
||||
CaptureLitStr,
|
||||
Bang,
|
||||
CloseBrace,
|
||||
CloseBracket,
|
||||
CloseParen,
|
||||
/// This will consume the first colon even if the second doesn't exist.
|
||||
DoubleColon,
|
||||
Comma,
|
||||
Eq,
|
||||
Lifetime,
|
||||
Lt,
|
||||
Gt,
|
||||
OpenBrace,
|
||||
OpenBracket,
|
||||
OpenParen,
|
||||
Pound,
|
||||
Semi,
|
||||
}
|
||||
|
||||
pub struct RustSearcher<'txt> {
|
||||
text: &'txt str,
|
||||
cursor: lexer::Cursor<'txt>,
|
||||
pos: u32,
|
||||
next_token: lexer::Token,
|
||||
}
|
||||
impl<'txt> RustSearcher<'txt> {
|
||||
#[must_use]
|
||||
#[expect(clippy::inconsistent_struct_constructor)]
|
||||
pub fn new(text: &'txt str) -> Self {
|
||||
let mut cursor = lexer::Cursor::new(text, FrontmatterAllowed::Yes);
|
||||
Self {
|
||||
text,
|
||||
pos: 0,
|
||||
next_token: cursor.advance_token(),
|
||||
cursor,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn peek_text(&self) -> &'txt str {
|
||||
&self.text[self.pos as usize..(self.pos + self.next_token.len) as usize]
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn peek_len(&self) -> u32 {
|
||||
self.next_token.len
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn peek(&self) -> lexer::TokenKind {
|
||||
self.next_token.kind
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn pos(&self) -> u32 {
|
||||
self.pos
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn at_end(&self) -> bool {
|
||||
self.next_token.kind == lexer::TokenKind::Eof
|
||||
}
|
||||
|
||||
pub fn step(&mut self) {
|
||||
// `next_len` is zero for the sentinel value and the eof marker.
|
||||
self.pos += self.next_token.len;
|
||||
self.next_token = self.cursor.advance_token();
|
||||
}
|
||||
|
||||
/// Consumes the next token if it matches the requested value and captures the value if
|
||||
/// requested. Returns true if a token was matched.
|
||||
fn read_token(&mut self, token: Token<'_>, captures: &mut slice::IterMut<'_, &mut &'txt str>) -> bool {
|
||||
loop {
|
||||
match (token, self.next_token.kind) {
|
||||
(_, lexer::TokenKind::Whitespace)
|
||||
| (
|
||||
Token::AnyComment,
|
||||
lexer::TokenKind::BlockComment { terminated: true, .. } | lexer::TokenKind::LineComment { .. },
|
||||
) => self.step(),
|
||||
(Token::AnyComment, _) => return true,
|
||||
(Token::Bang, lexer::TokenKind::Bang)
|
||||
| (Token::CloseBrace, lexer::TokenKind::CloseBrace)
|
||||
| (Token::CloseBracket, lexer::TokenKind::CloseBracket)
|
||||
| (Token::CloseParen, lexer::TokenKind::CloseParen)
|
||||
| (Token::Comma, lexer::TokenKind::Comma)
|
||||
| (Token::Eq, lexer::TokenKind::Eq)
|
||||
| (Token::Lifetime, lexer::TokenKind::Lifetime { .. })
|
||||
| (Token::Lt, lexer::TokenKind::Lt)
|
||||
| (Token::Gt, lexer::TokenKind::Gt)
|
||||
| (Token::OpenBrace, lexer::TokenKind::OpenBrace)
|
||||
| (Token::OpenBracket, lexer::TokenKind::OpenBracket)
|
||||
| (Token::OpenParen, lexer::TokenKind::OpenParen)
|
||||
| (Token::Pound, lexer::TokenKind::Pound)
|
||||
| (Token::Semi, lexer::TokenKind::Semi)
|
||||
| (
|
||||
Token::LitStr,
|
||||
lexer::TokenKind::Literal {
|
||||
kind: lexer::LiteralKind::Str { terminated: true } | lexer::LiteralKind::RawStr { .. },
|
||||
..
|
||||
},
|
||||
) => {
|
||||
self.step();
|
||||
return true;
|
||||
},
|
||||
(Token::Ident(x), lexer::TokenKind::Ident) if x == self.peek_text() => {
|
||||
self.step();
|
||||
return true;
|
||||
},
|
||||
(Token::DoubleColon, lexer::TokenKind::Colon) => {
|
||||
self.step();
|
||||
if !self.at_end() && matches!(self.next_token.kind, lexer::TokenKind::Colon) {
|
||||
self.step();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
(
|
||||
Token::CaptureLitStr,
|
||||
lexer::TokenKind::Literal {
|
||||
kind: lexer::LiteralKind::Str { terminated: true } | lexer::LiteralKind::RawStr { .. },
|
||||
..
|
||||
},
|
||||
)
|
||||
| (Token::CaptureIdent, lexer::TokenKind::Ident) => {
|
||||
**captures.next().unwrap() = self.peek_text();
|
||||
self.step();
|
||||
return true;
|
||||
},
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn find_token(&mut self, token: Token<'_>) -> bool {
|
||||
let mut capture = [].iter_mut();
|
||||
while !self.read_token(token, &mut capture) {
|
||||
self.step();
|
||||
if self.at_end() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn find_capture_token(&mut self, token: Token<'_>) -> Option<&'txt str> {
|
||||
let mut res = "";
|
||||
let mut capture = &mut res;
|
||||
let mut capture = slice::from_mut(&mut capture).iter_mut();
|
||||
while !self.read_token(token, &mut capture) {
|
||||
self.step();
|
||||
if self.at_end() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(res)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn match_tokens(&mut self, tokens: &[Token<'_>], captures: &mut [&mut &'txt str]) -> bool {
|
||||
let mut captures = captures.iter_mut();
|
||||
tokens.iter().all(|&t| self.read_token(t, &mut captures))
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn try_rename_file(old_name: &Path, new_name: &Path) -> bool {
|
||||
match OpenOptions::new().create_new(true).write(true).open(new_name) {
|
||||
|
|
@ -748,8 +593,8 @@ pub fn delete_dir_if_exists(path: &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| {
|
||||
pub fn walk_dir_no_dot_or_target(p: impl AsRef<Path>) -> impl Iterator<Item = ::walkdir::Result<::walkdir::DirEntry>> {
|
||||
WalkDir::new(p).into_iter().filter_entry(|e| {
|
||||
e.path()
|
||||
.file_name()
|
||||
.is_none_or(|x| x != "target" && x.as_encoded_bytes().first().copied() != Some(b'.'))
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "clippy_lints"
|
||||
version = "0.1.92"
|
||||
version = "0.1.93"
|
||||
description = "A bunch of helpful lints to avoid common pitfalls in Rust"
|
||||
repository = "https://github.com/rust-lang/rust-clippy"
|
||||
readme = "README.md"
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
|
|||
crate::duplicate_mod::DUPLICATE_MOD_INFO,
|
||||
crate::else_if_without_else::ELSE_IF_WITHOUT_ELSE_INFO,
|
||||
crate::empty_drop::EMPTY_DROP_INFO,
|
||||
crate::empty_enum::EMPTY_ENUM_INFO,
|
||||
crate::empty_enums::EMPTY_ENUMS_INFO,
|
||||
crate::empty_line_after::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO,
|
||||
crate::empty_line_after::EMPTY_LINE_AFTER_OUTER_ATTR_INFO,
|
||||
crate::empty_with_brackets::EMPTY_ENUM_VARIANTS_WITH_BRACKETS_INFO,
|
||||
|
|
@ -227,8 +227,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
|
|||
crate::init_numbered_fields::INIT_NUMBERED_FIELDS_INFO,
|
||||
crate::inline_fn_without_body::INLINE_FN_WITHOUT_BODY_INFO,
|
||||
crate::int_plus_one::INT_PLUS_ONE_INFO,
|
||||
crate::integer_division_remainder_used::INTEGER_DIVISION_REMAINDER_USED_INFO,
|
||||
crate::invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS_INFO,
|
||||
crate::item_name_repetitions::ENUM_VARIANT_NAMES_INFO,
|
||||
crate::item_name_repetitions::MODULE_INCEPTION_INFO,
|
||||
crate::item_name_repetitions::MODULE_NAME_REPETITIONS_INFO,
|
||||
|
|
@ -258,7 +256,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
|
|||
crate::lifetimes::ELIDABLE_LIFETIME_NAMES_INFO,
|
||||
crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO,
|
||||
crate::lifetimes::NEEDLESS_LIFETIMES_INFO,
|
||||
crate::lines_filter_map_ok::LINES_FILTER_MAP_OK_INFO,
|
||||
crate::literal_representation::DECIMAL_LITERAL_REPRESENTATION_INFO,
|
||||
crate::literal_representation::INCONSISTENT_DIGIT_GROUPING_INFO,
|
||||
crate::literal_representation::LARGE_DIGIT_GROUPS_INFO,
|
||||
|
|
@ -298,7 +295,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
|
|||
crate::manual_async_fn::MANUAL_ASYNC_FN_INFO,
|
||||
crate::manual_bits::MANUAL_BITS_INFO,
|
||||
crate::manual_clamp::MANUAL_CLAMP_INFO,
|
||||
crate::manual_div_ceil::MANUAL_DIV_CEIL_INFO,
|
||||
crate::manual_float_methods::MANUAL_IS_FINITE_INFO,
|
||||
crate::manual_float_methods::MANUAL_IS_INFINITE_INFO,
|
||||
crate::manual_hash_one::MANUAL_HASH_ONE_INFO,
|
||||
|
|
@ -404,6 +400,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
|
|||
crate::methods::ITER_SKIP_ZERO_INFO,
|
||||
crate::methods::ITER_WITH_DRAIN_INFO,
|
||||
crate::methods::JOIN_ABSOLUTE_PATHS_INFO,
|
||||
crate::methods::LINES_FILTER_MAP_OK_INFO,
|
||||
crate::methods::MANUAL_CONTAINS_INFO,
|
||||
crate::methods::MANUAL_C_STR_LITERALS_INFO,
|
||||
crate::methods::MANUAL_FILTER_MAP_INFO,
|
||||
|
|
@ -545,7 +542,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
|
|||
crate::needless_continue::NEEDLESS_CONTINUE_INFO,
|
||||
crate::needless_else::NEEDLESS_ELSE_INFO,
|
||||
crate::needless_for_each::NEEDLESS_FOR_EACH_INFO,
|
||||
crate::needless_if::NEEDLESS_IF_INFO,
|
||||
crate::needless_ifs::NEEDLESS_IFS_INFO,
|
||||
crate::needless_late_init::NEEDLESS_LATE_INIT_INFO,
|
||||
crate::needless_maybe_sized::NEEDLESS_MAYBE_SIZED_INFO,
|
||||
crate::needless_parens_on_range_literals::NEEDLESS_PARENS_ON_RANGE_LITERALS_INFO,
|
||||
|
|
@ -592,6 +589,9 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
|
|||
crate::operators::IMPOSSIBLE_COMPARISONS_INFO,
|
||||
crate::operators::INEFFECTIVE_BIT_MASK_INFO,
|
||||
crate::operators::INTEGER_DIVISION_INFO,
|
||||
crate::operators::INTEGER_DIVISION_REMAINDER_USED_INFO,
|
||||
crate::operators::INVALID_UPCAST_COMPARISONS_INFO,
|
||||
crate::operators::MANUAL_DIV_CEIL_INFO,
|
||||
crate::operators::MANUAL_IS_MULTIPLE_OF_INFO,
|
||||
crate::operators::MANUAL_MIDPOINT_INFO,
|
||||
crate::operators::MISREFACTORED_ASSIGN_OP_INFO,
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue