Skip to content

[stable] Prepare the 1.87.0 release #140859

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
May 9, 2025
Merged
289 changes: 289 additions & 0 deletions RELEASES.md

Large diffs are not rendered by default.

15 changes: 6 additions & 9 deletions compiler/rustc_attr_parsing/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use rustc_ast_pretty::pprust;
use rustc_errors::DiagCtxtHandle;
use rustc_hir::{self as hir, AttrPath};
use rustc_span::symbol::{Ident, kw, sym};
use rustc_span::{ErrorGuaranteed, Span, Symbol};
use rustc_span::{Span, Symbol};

pub struct SegmentIterator<'a> {
offset: usize,
Expand Down Expand Up @@ -176,7 +176,7 @@ impl<'a> ArgParser<'a> {
pub enum MetaItemOrLitParser<'a> {
MetaItemParser(MetaItemParser<'a>),
Lit(MetaItemLit),
Err(Span, ErrorGuaranteed),
Err(Span),
}

impl<'a> MetaItemOrLitParser<'a> {
Expand All @@ -186,7 +186,7 @@ impl<'a> MetaItemOrLitParser<'a> {
generic_meta_item_parser.span()
}
MetaItemOrLitParser::Lit(meta_item_lit) => meta_item_lit.span,
MetaItemOrLitParser::Err(span, _) => *span,
MetaItemOrLitParser::Err(span) => *span,
}
}

Expand Down Expand Up @@ -495,12 +495,9 @@ impl<'a> MetaItemListParserContext<'a> {
// where the macro didn't expand to a literal. An error is already given
// for this at this point, and then we do continue. This makes this path
// reachable...
let e = self.dcx.span_delayed_bug(
*span,
"expr in place where literal is expected (builtin attr parsing)",
);

return Some(MetaItemOrLitParser::Err(*span, e));
// NOTE: For backward compatibility we can't emit any error / delayed bug here (yet).
// See <https://github.com/rust-lang/rust/issues/140612>
return Some(MetaItemOrLitParser::Err(*span));
} else {
self.next_path()?
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {

let principal_trait = regular_traits.into_iter().next();

let mut needed_associated_types = vec![];
// A stable ordering of associated types from the principal trait and all its
// supertraits. We use this to ensure that different substitutions of a trait
// don't result in `dyn Trait` types with different projections lists, which
// can be unsound: <https://github.com/rust-lang/rust/pull/136458>.
// We achieve a stable ordering by walking over the unsubstituted principal
// trait ref.
let mut ordered_associated_types = vec![];

if let Some((principal_trait, ref spans)) = principal_trait {
let principal_trait = principal_trait.map_bound(|trait_pred| {
assert_eq!(trait_pred.polarity, ty::PredicatePolarity::Positive);
Expand All @@ -171,16 +178,13 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// FIXME(negative_bounds): Handle this correctly...
let trait_ref =
tcx.anonymize_bound_vars(bound_predicate.rebind(pred.trait_ref));
needed_associated_types.extend(
ordered_associated_types.extend(
tcx.associated_items(pred.trait_ref.def_id)
.in_definition_order()
// We only care about associated types.
.filter(|item| item.kind == ty::AssocKind::Type)
// No RPITITs -- they're not dyn-compatible for now.
.filter(|item| !item.is_impl_trait_in_trait())
// If the associated type has a `where Self: Sized` bound,
// we do not need to constrain the associated type.
.filter(|item| !tcx.generics_require_sized_self(item.def_id))
.map(|item| (item.def_id, trait_ref)),
);
}
Expand Down Expand Up @@ -252,14 +256,26 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
}
}

// We compute the list of projection bounds taking the ordered associated types,
// and check if there was an entry in the collected `projection_bounds`. Those
// are computed by first taking the user-written associated types, then elaborating
// the principal trait ref, and only using those if there was no user-written.
// See note below about how we handle missing associated types with `Self: Sized`,
// which are not required to be provided, but are still used if they are provided.
let mut missing_assoc_types = FxIndexSet::default();
let projection_bounds: Vec<_> = needed_associated_types
let projection_bounds: Vec<_> = ordered_associated_types
.into_iter()
.filter_map(|key| {
if let Some(assoc) = projection_bounds.get(&key) {
Some(*assoc)
} else {
missing_assoc_types.insert(key);
// If the associated type has a `where Self: Sized` bound, then
// we do not need to provide the associated type. This results in
// a `dyn Trait` type that has a different number of projection
// bounds, which may lead to type mismatches.
if !tcx.generics_require_sized_self(key.0) {
missing_assoc_types.insert(key);
}
None
}
})
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_middle/src/ty/sty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,10 @@ impl<'tcx> Ty<'tcx> {
repr: DynKind,
) -> Ty<'tcx> {
if cfg!(debug_assertions) {
let projection_count = obj.projection_bounds().count();
let projection_count = obj
.projection_bounds()
.filter(|item| !tcx.generics_require_sized_self(item.item_def_id()))
.count();
let expected_count: usize = obj
.principal_def_id()
.into_iter()
Expand Down
2 changes: 1 addition & 1 deletion src/ci/channel
Original file line number Diff line number Diff line change
@@ -1 +1 @@
beta
stable
33 changes: 18 additions & 15 deletions src/tools/clippy/clippy_lints/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,13 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
return;
};

if then_search.is_key_used_and_no_copy || else_search.is_key_used_and_no_copy {
span_lint(cx, MAP_ENTRY, expr.span, lint_msg);
return;
}

if then_search.edits.is_empty() && else_search.edits.is_empty() {
// No insertions
return;
} else if then_search.is_key_used_and_no_copy || else_search.is_key_used_and_no_copy {
// If there are other uses of the key, and the key is not copy,
// we cannot perform a fix automatically, but continue to emit a lint.
None
} else if then_search.edits.is_empty() || else_search.edits.is_empty() {
// if .. { insert } else { .. } or if .. { .. } else { insert }
let ((then_str, entry_kind), else_str) = match (else_search.edits.is_empty(), contains_expr.negated) {
Expand All @@ -123,10 +122,10 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
snippet_with_applicability(cx, then_expr.span, "{ .. }", &mut app),
),
};
format!(
Some(format!(
"if let {}::{entry_kind} = {map_str}.entry({key_str}) {then_str} else {else_str}",
map_ty.entry_path(),
)
))
} else {
// if .. { insert } else { insert }
let ((then_str, then_entry), (else_str, else_entry)) = if contains_expr.negated {
Expand All @@ -142,13 +141,13 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
};
let indent_str = snippet_indent(cx, expr.span);
let indent_str = indent_str.as_deref().unwrap_or("");
format!(
Some(format!(
"match {map_str}.entry({key_str}) {{\n{indent_str} {entry}::{then_entry} => {}\n\
{indent_str} {entry}::{else_entry} => {}\n{indent_str}}}",
reindent_multiline(&then_str, true, Some(4 + indent_str.len())),
reindent_multiline(&else_str, true, Some(4 + indent_str.len())),
entry = map_ty.entry_path(),
)
))
}
} else {
if then_search.edits.is_empty() {
Expand All @@ -163,17 +162,17 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
} else {
then_search.snippet_occupied(cx, then_expr.span, &mut app)
};
format!(
Some(format!(
"if let {}::{entry_kind} = {map_str}.entry({key_str}) {body_str}",
map_ty.entry_path(),
)
))
} else if let Some(insertion) = then_search.as_single_insertion() {
let value_str = snippet_with_context(cx, insertion.value.span, then_expr.span.ctxt(), "..", &mut app).0;
if contains_expr.negated {
if insertion.value.can_have_side_effects() {
format!("{map_str}.entry({key_str}).or_insert_with(|| {value_str});")
Some(format!("{map_str}.entry({key_str}).or_insert_with(|| {value_str});"))
} else {
format!("{map_str}.entry({key_str}).or_insert({value_str});")
Some(format!("{map_str}.entry({key_str}).or_insert({value_str});"))
}
} else {
// TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here.
Expand All @@ -183,7 +182,7 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
} else {
let block_str = then_search.snippet_closure(cx, then_expr.span, &mut app);
if contains_expr.negated {
format!("{map_str}.entry({key_str}).or_insert_with(|| {block_str});")
Some(format!("{map_str}.entry({key_str}).or_insert_with(|| {block_str});"))
} else {
// TODO: suggest using `if let Some(v) = map.get_mut(k) { .. }` here.
// This would need to be a different lint.
Expand All @@ -192,7 +191,11 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
}
};

span_lint_and_sugg(cx, MAP_ENTRY, expr.span, lint_msg, "try", sugg, app);
if let Some(sugg) = sugg {
span_lint_and_sugg(cx, MAP_ENTRY, expr.span, lint_msg, "try", sugg, app);
} else {
span_lint(cx, MAP_ENTRY, expr.span, lint_msg);
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ fn is_variant_or_wildcard(cx: &LateContext<'_>, pat: &Pat<'_>, can_be_wild: bool
/// contains `Err(IDENT)`, `None` otherwise.
fn is_ok_or_err<'hir>(cx: &LateContext<'_>, pat: &Pat<'hir>) -> Option<(bool, &'hir Ident)> {
if let PatKind::TupleStruct(qpath, [arg], _) = &pat.kind
&& let PatKind::Binding(BindingMode::NONE, _, ident, _) = &arg.kind
&& let PatKind::Binding(BindingMode::NONE, _, ident, None) = &arg.kind
&& let res = cx.qpath_res(qpath, pat.hir_id)
&& let Res::Def(DefKind::Ctor(..), id) = res
&& let id @ Some(_) = cx.tcx.opt_parent(id)
Expand Down
35 changes: 22 additions & 13 deletions src/tools/clippy/clippy_lints/src/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -786,9 +786,9 @@ fn check_ptr_eq<'tcx>(
}

// Remove one level of usize conversion if any
let (left, right) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) {
(Some(lhs), Some(rhs)) => (lhs, rhs),
_ => (left, right),
let (left, right, usize_peeled) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) {
(Some(lhs), Some(rhs)) => (lhs, rhs, true),
_ => (left, right, false),
};

// This lint concerns raw pointers
Expand All @@ -797,10 +797,16 @@ fn check_ptr_eq<'tcx>(
return;
}

let (left_var, right_var) = (peel_raw_casts(cx, left, left_ty), peel_raw_casts(cx, right, right_ty));
let ((left_var, left_casts_peeled), (right_var, right_casts_peeled)) =
(peel_raw_casts(cx, left, left_ty), peel_raw_casts(cx, right, right_ty));

if let Some(left_snip) = left_var.span.get_source_text(cx)
&& let Some(right_snip) = right_var.span.get_source_text(cx)
if !(usize_peeled || left_casts_peeled || right_casts_peeled) {
return;
}

let mut app = Applicability::MachineApplicable;
let left_snip = Sugg::hir_with_context(cx, left_var, expr.span.ctxt(), "_", &mut app);
let right_snip = Sugg::hir_with_context(cx, right_var, expr.span.ctxt(), "_", &mut app);
{
let Some(top_crate) = std_or_core(cx) else { return };
let invert = if op == BinOpKind::Eq { "" } else { "!" };
Expand All @@ -811,15 +817,16 @@ fn check_ptr_eq<'tcx>(
format!("use `{top_crate}::ptr::eq` when comparing raw pointers"),
"try",
format!("{invert}{top_crate}::ptr::eq({left_snip}, {right_snip})"),
Applicability::MachineApplicable,
app,
);
}
}

// If the given expression is a cast to a usize, return the lhs of the cast
// E.g., `foo as *const _ as usize` returns `foo as *const _`.
fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
if cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize
if !cast_expr.span.from_expansion()
&& cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize
&& let ExprKind::Cast(expr, _) = cast_expr.kind
{
Some(expr)
Expand All @@ -828,16 +835,18 @@ fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>
}
}

// Peel raw casts if the remaining expression can be coerced to it
fn peel_raw_casts<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expr_ty: Ty<'tcx>) -> &'tcx Expr<'tcx> {
if let ExprKind::Cast(inner, _) = expr.kind
// Peel raw casts if the remaining expression can be coerced to it, and whether casts have been
// peeled or not.
fn peel_raw_casts<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expr_ty: Ty<'tcx>) -> (&'tcx Expr<'tcx>, bool) {
if !expr.span.from_expansion()
&& let ExprKind::Cast(inner, _) = expr.kind
&& let ty::RawPtr(target_ty, _) = expr_ty.kind()
&& let inner_ty = cx.typeck_results().expr_ty(inner)
&& let ty::RawPtr(inner_target_ty, _) | ty::Ref(_, inner_target_ty, _) = inner_ty.kind()
&& target_ty == inner_target_ty
{
peel_raw_casts(cx, inner, inner_ty)
(peel_raw_casts(cx, inner, inner_ty).0, true)
} else {
expr
(expr, false)
}
}
22 changes: 22 additions & 0 deletions src/tools/clippy/tests/ui/entry.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -226,4 +226,26 @@ fn issue11976() {
}
}

mod issue14449 {
use std::collections::BTreeMap;

pub struct Meow {
map: BTreeMap<String, String>,
}

impl Meow {
fn pet(&self, _key: &str, _v: u32) -> u32 {
42
}
}

pub fn f(meow: &Meow, x: String) {
if meow.map.contains_key(&x) {
let _ = meow.pet(&x, 1);
} else {
let _ = meow.pet(&x, 0);
}
}
}

fn main() {}
22 changes: 22 additions & 0 deletions src/tools/clippy/tests/ui/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,4 +232,26 @@ fn issue11976() {
}
}

mod issue14449 {
use std::collections::BTreeMap;

pub struct Meow {
map: BTreeMap<String, String>,
}

impl Meow {
fn pet(&self, _key: &str, _v: u32) -> u32 {
42
}
}

pub fn f(meow: &Meow, x: String) {
if meow.map.contains_key(&x) {
let _ = meow.pet(&x, 1);
} else {
let _ = meow.pet(&x, 0);
}
}
}

fn main() {}
5 changes: 5 additions & 0 deletions src/tools/clippy/tests/ui/manual_ok_err.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ fn no_lint() {
Ok(3) => None,
Ok(v) => Some(v),
};

let _ = match funcall() {
Ok(v @ 1..) => Some(v),
_ => None,
};
}

const fn cf(x: Result<u32, &'static str>) -> Option<u32> {
Expand Down
5 changes: 5 additions & 0 deletions src/tools/clippy/tests/ui/manual_ok_err.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ fn no_lint() {
Ok(3) => None,
Ok(v) => Some(v),
};

let _ = match funcall() {
Ok(v @ 1..) => Some(v),
_ => None,
};
}

const fn cf(x: Result<u32, &'static str>) -> Option<u32> {
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/tests/ui/manual_ok_err.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ LL | | };
| |_____^ help: replace with: `(-S).ok()`

error: manual implementation of `ok`
--> tests/ui/manual_ok_err.rs:132:12
--> tests/ui/manual_ok_err.rs:137:12
|
LL | } else if let Ok(n) = "1".parse::<u8>() {
| ____________^
Expand Down
Loading
Loading