Skip to content
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