diff --git a/Cargo.lock b/Cargo.lock index 979198cece80f..04bb96be369d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1859,7 +1859,6 @@ checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", "hashbrown 0.15.2", - "rustc-rayon", "serde", ] @@ -3240,11 +3239,12 @@ dependencies = [ [[package]] name = "rustc-rayon" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb81aadc8837ca6ecebe0fe1353f15df83b3b3cc2cf7a8afd571bc22aa121710" +checksum = "2cd9fb077db982d7ceb42a90471e5a69a990b58f71e06f0d8340bb2cf35eb751" dependencies = [ "either", + "indexmap", "rustc-rayon-core", ] diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 3459d39131ac7..7caf7c4c35687 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -954,8 +954,14 @@ fn walk_coroutine_kind(vis: &mut T, coroutine_kind: &mut Coroutin fn walk_fn(vis: &mut T, kind: FnKind<'_>) { match kind { - FnKind::Fn(_ctxt, _ident, FnSig { header, decl, span }, _visibility, generics, body) => { + FnKind::Fn( + _ctxt, + _ident, + _vis, + Fn { defaultness, generics, body, sig: FnSig { header, decl, span } }, + ) => { // Identifier and visibility are visited as a part of the item. + visit_defaultness(vis, defaultness); vis.visit_fn_header(header); vis.visit_generics(generics); vis.visit_fn_decl(decl); @@ -1205,13 +1211,8 @@ impl WalkItemKind for ItemKind { ItemKind::Const(item) => { visit_const_item(item, vis); } - ItemKind::Fn(box Fn { defaultness, generics, sig, body }) => { - visit_defaultness(vis, defaultness); - vis.visit_fn( - FnKind::Fn(FnCtxt::Free, ident, sig, visibility, generics, body), - span, - id, - ); + ItemKind::Fn(func) => { + vis.visit_fn(FnKind::Fn(FnCtxt::Free, ident, visibility, &mut *func), span, id); } ItemKind::Mod(safety, mod_kind) => { visit_safety(vis, safety); @@ -1329,10 +1330,9 @@ impl WalkItemKind for AssocItemKind { AssocItemKind::Const(item) => { visit_const_item(item, visitor); } - AssocItemKind::Fn(box Fn { defaultness, generics, sig, body }) => { - visit_defaultness(visitor, defaultness); + AssocItemKind::Fn(func) => { visitor.visit_fn( - FnKind::Fn(FnCtxt::Assoc(ctxt), ident, sig, visibility, generics, body), + FnKind::Fn(FnCtxt::Assoc(ctxt), ident, visibility, &mut *func), span, id, ); @@ -1476,10 +1476,9 @@ impl WalkItemKind for ForeignItemKind { visitor.visit_ty(ty); visit_opt(expr, |expr| visitor.visit_expr(expr)); } - ForeignItemKind::Fn(box Fn { defaultness, generics, sig, body }) => { - visit_defaultness(visitor, defaultness); + ForeignItemKind::Fn(func) => { visitor.visit_fn( - FnKind::Fn(FnCtxt::Foreign, ident, sig, visibility, generics, body), + FnKind::Fn(FnCtxt::Foreign, ident, visibility, &mut *func), span, id, ); @@ -1965,14 +1964,7 @@ impl DummyAstNode for crate::ast_traits::AstNo #[derive(Debug)] pub enum FnKind<'a> { /// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`. - Fn( - FnCtxt, - &'a mut Ident, - &'a mut FnSig, - &'a mut Visibility, - &'a mut Generics, - &'a mut Option>, - ), + Fn(FnCtxt, &'a mut Ident, &'a mut Visibility, &'a mut Fn), /// E.g., `|x, y| body`. Closure( diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 1d6d7330757de..232fd546de9a3 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -65,7 +65,7 @@ impl BoundKind { #[derive(Copy, Clone, Debug)] pub enum FnKind<'a> { /// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`. - Fn(FnCtxt, &'a Ident, &'a FnSig, &'a Visibility, &'a Generics, &'a Option>), + Fn(FnCtxt, &'a Ident, &'a Visibility, &'a Fn), /// E.g., `|x, y| body`. Closure(&'a ClosureBinder, &'a Option, &'a FnDecl, &'a Expr), @@ -74,7 +74,7 @@ pub enum FnKind<'a> { impl<'a> FnKind<'a> { pub fn header(&self) -> Option<&'a FnHeader> { match *self { - FnKind::Fn(_, _, sig, _, _, _) => Some(&sig.header), + FnKind::Fn(_, _, _, Fn { sig, .. }) => Some(&sig.header), FnKind::Closure(..) => None, } } @@ -88,7 +88,7 @@ impl<'a> FnKind<'a> { pub fn decl(&self) -> &'a FnDecl { match self { - FnKind::Fn(_, _, sig, _, _, _) => &sig.decl, + FnKind::Fn(_, _, _, Fn { sig, .. }) => &sig.decl, FnKind::Closure(_, _, decl, _) => decl, } } @@ -374,8 +374,8 @@ impl WalkItemKind for ItemKind { try_visit!(visitor.visit_ty(ty)); visit_opt!(visitor, visit_expr, expr); } - ItemKind::Fn(box Fn { defaultness: _, generics, sig, body }) => { - let kind = FnKind::Fn(FnCtxt::Free, ident, sig, vis, generics, body); + ItemKind::Fn(func) => { + let kind = FnKind::Fn(FnCtxt::Free, ident, vis, &*func); try_visit!(visitor.visit_fn(kind, span, id)); } ItemKind::Mod(_unsafety, mod_kind) => match mod_kind { @@ -715,8 +715,8 @@ impl WalkItemKind for ForeignItemKind { try_visit!(visitor.visit_ty(ty)); visit_opt!(visitor, visit_expr, expr); } - ForeignItemKind::Fn(box Fn { defaultness: _, generics, sig, body }) => { - let kind = FnKind::Fn(FnCtxt::Foreign, ident, sig, vis, generics, body); + ForeignItemKind::Fn(func) => { + let kind = FnKind::Fn(FnCtxt::Foreign, ident, vis, &*func); try_visit!(visitor.visit_fn(kind, span, id)); } ForeignItemKind::TyAlias(box TyAlias { @@ -858,7 +858,12 @@ pub fn walk_fn_decl<'a, V: Visitor<'a>>( pub fn walk_fn<'a, V: Visitor<'a>>(visitor: &mut V, kind: FnKind<'a>) -> V::Result { match kind { - FnKind::Fn(_ctxt, _ident, FnSig { header, decl, span: _ }, _vis, generics, body) => { + FnKind::Fn( + _ctxt, + _ident, + _vis, + Fn { defaultness: _, sig: FnSig { header, decl, span: _ }, generics, body }, + ) => { // Identifier and visibility are visited as a part of the item. try_visit!(visitor.visit_fn_header(header)); try_visit!(visitor.visit_generics(generics)); @@ -892,8 +897,8 @@ impl WalkItemKind for AssocItemKind { try_visit!(visitor.visit_ty(ty)); visit_opt!(visitor, visit_expr, expr); } - AssocItemKind::Fn(box Fn { defaultness: _, generics, sig, body }) => { - let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), ident, sig, vis, generics, body); + AssocItemKind::Fn(func) => { + let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), ident, vis, &*func); try_visit!(visitor.visit_fn(kind, span, id)); } AssocItemKind::Type(box TyAlias { diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index f31e2c65c792a..b932915dc298c 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -2125,7 +2125,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.arena.alloc(self.expr_call_mut(span, e, args)) } - fn expr_call_lang_item_fn_mut( + pub(super) fn expr_call_lang_item_fn_mut( &mut self, span: Span, lang_item: hir::LangItem, @@ -2135,7 +2135,7 @@ impl<'hir> LoweringContext<'_, 'hir> { self.expr_call_mut(span, path, args) } - fn expr_call_lang_item_fn( + pub(super) fn expr_call_lang_item_fn( &mut self, span: Span, lang_item: hir::LangItem, diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 236ca7ba70357..ea1f4a6559ac8 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -917,7 +917,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { walk_list!(self, visit_attribute, &item.attrs); return; // Avoid visiting again. } - ItemKind::Fn(box Fn { defaultness, sig, generics, body }) => { + ItemKind::Fn(func @ box Fn { defaultness, generics: _, sig, body }) => { self.check_defaultness(item.span, *defaultness); let is_intrinsic = @@ -947,7 +947,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.visit_vis(&item.vis); self.visit_ident(&item.ident); - let kind = FnKind::Fn(FnCtxt::Free, &item.ident, sig, &item.vis, generics, body); + let kind = FnKind::Fn(FnCtxt::Free, &item.ident, &item.vis, &*func); self.visit_fn(kind, item.span, item.id); walk_list!(self, visit_attribute, &item.attrs); return; // Avoid visiting again. @@ -1348,19 +1348,20 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } if let FnKind::Fn( - _, - _, - FnSig { header: FnHeader { ext: Extern::Implicit(extern_span), .. }, .. }, _, _, _, + Fn { + sig: FnSig { header: FnHeader { ext: Extern::Implicit(extern_span), .. }, .. }, + .. + }, ) = fk { self.maybe_lint_missing_abi(*extern_span, id); } // Functions without bodies cannot have patterns. - if let FnKind::Fn(ctxt, _, sig, _, _, None) = fk { + if let FnKind::Fn(ctxt, _, _, Fn { body: None, sig, .. }) = fk { Self::check_decl_no_pat(&sig.decl, |span, ident, mut_ident| { if mut_ident && matches!(ctxt, FnCtxt::Assoc(_)) { if let Some(ident) = ident { @@ -1394,7 +1395,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { .is_some(); let disallowed = (!tilde_const_allowed).then(|| match fk { - FnKind::Fn(_, ident, _, _, _, _) => TildeConstReason::Function { ident: ident.span }, + FnKind::Fn(_, ident, _, _) => TildeConstReason::Function { ident: ident.span }, FnKind::Closure(..) => TildeConstReason::Closure, }); self.with_tilde_const(disallowed, |this| visit::walk_fn(this, fk)); @@ -1470,15 +1471,14 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.outer_trait_or_trait_impl.as_ref().and_then(TraitOrTraitImpl::constness).is_some(); match &item.kind { - AssocItemKind::Fn(box Fn { sig, generics, body, .. }) + AssocItemKind::Fn(func) if parent_is_const || ctxt == AssocCtxt::Trait - || matches!(sig.header.constness, Const::Yes(_)) => + || matches!(func.sig.header.constness, Const::Yes(_)) => { self.visit_vis(&item.vis); self.visit_ident(&item.ident); - let kind = - FnKind::Fn(FnCtxt::Assoc(ctxt), &item.ident, sig, &item.vis, generics, body); + let kind = FnKind::Fn(FnCtxt::Assoc(ctxt), &item.ident, &item.vis, &*func); walk_list!(self, visit_attribute, &item.attrs); self.visit_fn(kind, item.span, item.id); } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 897c275d850c2..4cfcaa95233da 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -34,8 +34,8 @@ impl<'a> State<'a> { self.maybe_print_comment(span.lo()); self.print_outer_attributes(attrs); match kind { - ast::ForeignItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { - self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs); + ast::ForeignItemKind::Fn(func) => { + self.print_fn_full(ident, vis, attrs, &*func); } ast::ForeignItemKind::Static(box ast::StaticItem { ty, mutability, expr, safety }) => { self.print_item_const( @@ -199,16 +199,8 @@ impl<'a> State<'a> { *defaultness, ); } - ast::ItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { - self.print_fn_full( - sig, - item.ident, - generics, - &item.vis, - *defaultness, - body.as_deref(), - &item.attrs, - ); + ast::ItemKind::Fn(func) => { + self.print_fn_full(item.ident, &item.vis, &item.attrs, &*func); } ast::ItemKind::Mod(safety, mod_kind) => { self.head(Self::to_string(|s| { @@ -542,8 +534,8 @@ impl<'a> State<'a> { self.maybe_print_comment(span.lo()); self.print_outer_attributes(attrs); match kind { - ast::AssocItemKind::Fn(box ast::Fn { defaultness, sig, generics, body }) => { - self.print_fn_full(sig, ident, generics, vis, *defaultness, body.as_deref(), attrs); + ast::AssocItemKind::Fn(func) => { + self.print_fn_full(ident, vis, attrs, &*func); } ast::AssocItemKind::Const(box ast::ConstItem { defaultness, generics, ty, expr }) => { self.print_item_const( @@ -653,19 +645,17 @@ impl<'a> State<'a> { fn print_fn_full( &mut self, - sig: &ast::FnSig, name: Ident, - generics: &ast::Generics, vis: &ast::Visibility, - defaultness: ast::Defaultness, - body: Option<&ast::Block>, attrs: &[ast::Attribute], + func: &ast::Fn, ) { + let ast::Fn { defaultness, generics, sig, body } = func; if body.is_some() { self.head(""); } self.print_visibility(vis); - self.print_defaultness(defaultness); + self.print_defaultness(*defaultness); self.print_fn(&sig.decl, sig.header, Some(name), generics); if let Some(body) = body { self.nbsp(); diff --git a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs index 90d12ea832857..475897d8f3e31 100644 --- a/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/bound_region_errors.rs @@ -310,7 +310,7 @@ impl<'tcx> TypeOpInfo<'tcx> for AscribeUserTypeQuery<'tcx> { let (infcx, key, _) = mbcx.infcx.tcx.infer_ctxt().build_with_canonical(cause.span, &self.canonical_query); let ocx = ObligationCtxt::new(&infcx); - type_op_ascribe_user_type_with_span(&ocx, key, Some(cause.span)).ok()?; + type_op_ascribe_user_type_with_span(&ocx, key, cause.span).ok()?; let diag = try_extract_error_from_fulfill_cx( &ocx, mbcx.mir_def_id(), diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index a6ca038282d96..e841a5e4c948a 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -1140,10 +1140,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { let amp_mut_sugg = match *local_decl.local_info() { LocalInfo::User(mir::BindingForm::ImplicitSelf(_)) => { - let suggestion = suggest_ampmut_self(self.infcx.tcx, decl_span); - let additional = - local_trait.map(|span| (span, suggest_ampmut_self(self.infcx.tcx, span))); - Some(AmpMutSugg { has_sugg: true, span: decl_span, suggestion, additional }) + let (span, suggestion) = suggest_ampmut_self(self.infcx.tcx, decl_span); + let additional = local_trait.map(|span| suggest_ampmut_self(self.infcx.tcx, span)); + Some(AmpMutSugg { has_sugg: true, span, suggestion, additional }) } LocalInfo::User(mir::BindingForm::Var(mir::VarBindingForm { @@ -1202,10 +1201,11 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { opt_ty_info: None, .. })) => { - let sugg = suggest_ampmut_self(self.infcx.tcx, decl_span); + let (span, sugg) = + suggest_ampmut_self(self.infcx.tcx, decl_span); Some(AmpMutSugg { has_sugg: true, - span: decl_span, + span, suggestion: sugg, additional: None, }) @@ -1461,17 +1461,12 @@ fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option(tcx: TyCtxt<'tcx>, span: Span) -> String { +fn suggest_ampmut_self(tcx: TyCtxt<'_>, span: Span) -> (Span, String) { match tcx.sess.source_map().span_to_snippet(span) { - Ok(snippet) => { - let lt_pos = snippet.find('\''); - if let Some(lt_pos) = lt_pos { - format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4]) - } else { - "&mut self".to_string() - } + Ok(snippet) if snippet.ends_with("self") => { + (span.with_hi(span.hi() - BytePos(4)).shrink_to_hi(), "mut ".to_string()) } - _ => "&mut self".to_string(), + _ => (span, "&mut self".to_string()), } } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 91dc76f597ad7..decfab502bb59 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1284,15 +1284,18 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { ); } - &Rvalue::RawPtr(mutability, place) => { - let access_kind = match mutability { - Mutability::Mut => ( + &Rvalue::RawPtr(kind, place) => { + let access_kind = match kind { + RawPtrKind::Mut => ( Deep, Write(WriteKind::MutableBorrow(BorrowKind::Mut { kind: MutBorrowKind::Default, })), ), - Mutability::Not => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))), + RawPtrKind::Const => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))), + RawPtrKind::FakeForPtrMetadata => { + (Shallow(Some(ArtificialField::ArrayLength)), Read(ReadKind::Copy)) + } }; self.access_place( diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index 40e801d03885c..f71e6f3e6f3a6 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -1,14 +1,18 @@ use std::io; +use rustc_data_structures::fx::FxHashSet; +use rustc_index::IndexVec; use rustc_middle::mir::pretty::{ PassWhere, PrettyPrintMirOptions, create_dump_file, dump_enabled, dump_mir_to_writer, }; use rustc_middle::mir::{Body, ClosureRegionRequirements}; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{RegionVid, TyCtxt}; use rustc_session::config::MirIncludeSpans; use crate::borrow_set::BorrowSet; +use crate::constraints::OutlivesConstraint; use crate::polonius::{LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet}; +use crate::type_check::Locations; use crate::{BorrowckInferCtxt, RegionInferenceContext}; /// `-Zdump-mir=polonius` dumps MIR annotated with NLL and polonius specific information. @@ -50,6 +54,8 @@ pub(crate) fn dump_polonius_mir<'tcx>( /// - the NLL MIR /// - the list of polonius localized constraints /// - a mermaid graph of the CFG +/// - a mermaid graph of the NLL regions and the constraints between them +/// - a mermaid graph of the NLL SCCs and the constraints between them fn emit_polonius_dump<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, @@ -68,7 +74,7 @@ fn emit_polonius_dump<'tcx>( // Section 1: the NLL + Polonius MIR. writeln!(out, "
")?; writeln!(out, "Raw MIR dump")?; - writeln!(out, "
")?;
+    writeln!(out, "
")?;
     emit_html_mir(
         tcx,
         body,
@@ -78,15 +84,31 @@ fn emit_polonius_dump<'tcx>(
         closure_region_requirements,
         out,
     )?;
-    writeln!(out, "
")?; + writeln!(out, "
")?; writeln!(out, "
")?; // Section 2: mermaid visualization of the CFG. writeln!(out, "
")?; writeln!(out, "Control-flow graph")?; - writeln!(out, "
")?;
+    writeln!(out, "
")?;
     emit_mermaid_cfg(body, out)?;
-    writeln!(out, "
")?; + writeln!(out, "
")?; + writeln!(out, "
")?; + + // Section 3: mermaid visualization of the NLL region graph. + writeln!(out, "
")?; + writeln!(out, "NLL regions")?; + writeln!(out, "
")?;
+    emit_mermaid_nll_regions(regioncx, out)?;
+    writeln!(out, "
")?; + writeln!(out, "
")?; + + // Section 4: mermaid visualization of the NLL SCC graph. + writeln!(out, "
")?; + writeln!(out, "NLL SCCs")?; + writeln!(out, "
")?;
+    emit_mermaid_nll_sccs(regioncx, out)?;
+    writeln!(out, "
")?; writeln!(out, "
")?; // Finalize the dump with the HTML epilogue. @@ -261,3 +283,112 @@ fn emit_mermaid_cfg(body: &Body<'_>, out: &mut dyn io::Write) -> io::Result<()> Ok(()) } + +/// Emits a region's label: index, universe, external name. +fn render_region( + region: RegionVid, + regioncx: &RegionInferenceContext<'_>, + out: &mut dyn io::Write, +) -> io::Result<()> { + let def = regioncx.region_definition(region); + let universe = def.universe; + + write!(out, "'{}", region.as_usize())?; + if !universe.is_root() { + write!(out, "/{universe:?}")?; + } + if let Some(name) = def.external_name.and_then(|e| e.get_name()) { + write!(out, " ({name})")?; + } + Ok(()) +} + +/// Emits a mermaid flowchart of the NLL regions and the outlives constraints between them, similar +/// to the graphviz version. +fn emit_mermaid_nll_regions<'tcx>( + regioncx: &RegionInferenceContext<'tcx>, + out: &mut dyn io::Write, +) -> io::Result<()> { + // The mermaid chart type: a top-down flowchart. + writeln!(out, "flowchart TD")?; + + // Emit the region nodes. + for region in regioncx.var_infos.indices() { + write!(out, "{}[\"", region.as_usize())?; + render_region(region, regioncx, out)?; + writeln!(out, "\"]")?; + } + + // Get a set of edges to check for the reverse edge being present. + let edges: FxHashSet<_> = regioncx.outlives_constraints().map(|c| (c.sup, c.sub)).collect(); + + // Order (and deduplicate) edges for traversal, to display them in a generally increasing order. + let constraint_key = |c: &OutlivesConstraint<'_>| { + let min = c.sup.min(c.sub); + let max = c.sup.max(c.sub); + (min, max) + }; + let mut ordered_edges: Vec<_> = regioncx.outlives_constraints().collect(); + ordered_edges.sort_by_key(|c| constraint_key(c)); + ordered_edges.dedup_by_key(|c| constraint_key(c)); + + for outlives in ordered_edges { + // Source node. + write!(out, "{} ", outlives.sup.as_usize())?; + + // The kind of arrow: bidirectional if the opposite edge exists in the set. + if edges.contains(&(outlives.sub, outlives.sup)) { + write!(out, "<")?; + } + write!(out, "-- ")?; + + // Edge label from its `Locations`. + match outlives.locations { + Locations::All(_) => write!(out, "All")?, + Locations::Single(location) => write!(out, "{:?}", location)?, + } + + // Target node. + writeln!(out, " --> {}", outlives.sub.as_usize())?; + } + Ok(()) +} + +/// Emits a mermaid flowchart of the NLL SCCs and the outlives constraints between them, similar +/// to the graphviz version. +fn emit_mermaid_nll_sccs<'tcx>( + regioncx: &RegionInferenceContext<'tcx>, + out: &mut dyn io::Write, +) -> io::Result<()> { + // The mermaid chart type: a top-down flowchart. + writeln!(out, "flowchart TD")?; + + // Gather and emit the SCC nodes. + let mut nodes_per_scc: IndexVec<_, _> = + regioncx.constraint_sccs().all_sccs().map(|_| Vec::new()).collect(); + for region in regioncx.var_infos.indices() { + let scc = regioncx.constraint_sccs().scc(region); + nodes_per_scc[scc].push(region); + } + for (scc, regions) in nodes_per_scc.iter_enumerated() { + // The node label: the regions contained in the SCC. + write!(out, "{scc}[\"SCC({scc}) = {{", scc = scc.as_usize())?; + for (idx, ®ion) in regions.iter().enumerate() { + render_region(region, regioncx, out)?; + if idx < regions.len() - 1 { + write!(out, ",")?; + } + } + writeln!(out, "}}\"]")?; + } + + // Emit the edges between SCCs. + let edges = regioncx.constraint_sccs().all_sccs().flat_map(|source| { + regioncx.constraint_sccs().successors(source).iter().map(move |&target| (source, target)) + }); + for (source, target) in edges { + writeln!(out, "{} --> {}", source.as_usize(), target.as_usize())?; + } + + Ok(()) +} diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index cbcfab1dc3e11..f79bcf5af5564 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -3,11 +3,7 @@ use std::ops::ControlFlow; use rustc_data_structures::graph::dominators::Dominators; use rustc_middle::bug; use rustc_middle::mir::visit::Visitor; -use rustc_middle::mir::{ - self, BasicBlock, Body, BorrowKind, FakeBorrowKind, InlineAsmOperand, Location, Mutability, - NonDivergingIntrinsic, Operand, Place, Rvalue, Statement, StatementKind, Terminator, - TerminatorKind, -}; +use rustc_middle::mir::*; use rustc_middle::ty::TyCtxt; use tracing::debug; @@ -60,7 +56,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> { StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => { self.consume_operand(location, op); } - StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(mir::CopyNonOverlapping { + StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping { src, dst, count, @@ -273,15 +269,18 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> { self.access_place(location, place, access_kind, LocalMutationIsAllowed::No); } - &Rvalue::RawPtr(mutability, place) => { - let access_kind = match mutability { - Mutability::Mut => ( + &Rvalue::RawPtr(kind, place) => { + let access_kind = match kind { + RawPtrKind::Mut => ( Deep, Write(WriteKind::MutableBorrow(BorrowKind::Mut { - kind: mir::MutBorrowKind::Default, + kind: MutBorrowKind::Default, })), ), - Mutability::Not => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))), + RawPtrKind::Const => (Deep, Read(ReadKind::Borrow(BorrowKind::Shared))), + RawPtrKind::FakeForPtrMetadata => { + (Shallow(Some(ArtificialField::ArrayLength)), Read(ReadKind::Copy)) + } }; self.access_place(location, place, access_kind, LocalMutationIsAllowed::No); diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index e0196d55f20a2..a2ef5588f48f8 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -349,8 +349,8 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { let tcx = self.tcx(); let maybe_uneval = match constant.const_ { Const::Ty(_, ct) => match ct.kind() { - ty::ConstKind::Unevaluated(_) => { - bug!("should not encounter unevaluated Const::Ty here, got {:?}", ct) + ty::ConstKind::Unevaluated(uv) => { + Some(UnevaluatedConst { def: uv.def, args: uv.args, promoted: None }) } _ => None, }, diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 5062cf55bb9ad..eb5b345e49ecd 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -651,7 +651,7 @@ fn expand_preparsed_asm( .map(|span| template_span.from_inner(InnerSpan::new(span.start, span.end))); for piece in unverified_pieces { match piece { - parse::Piece::String(s) => { + parse::Piece::Lit(s) => { template.push(ast::InlineAsmTemplatePiece::String(s.to_string().into())) } parse::Piece::NextArgument(arg) => { diff --git a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs index 49706db0e0b3f..82417a86dd9ee 100644 --- a/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs +++ b/compiler/rustc_builtin_macros/src/deriving/coerce_pointee.rs @@ -157,7 +157,7 @@ pub(crate) fn expand_deriving_coerce_pointee( { cx.dcx().emit_err(RequiresMaybeSized { span: pointee_ty_ident.span, - name: pointee_ty_ident.name.to_ident_string(), + name: pointee_ty_ident, }); return; } @@ -471,5 +471,5 @@ struct TooManyPointees { struct RequiresMaybeSized { #[primary_span] span: Span, - name: String, + name: Ident, } diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 5202fe26c401e..a0ab6375a666b 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -406,7 +406,7 @@ fn make_format_args( for piece in &pieces { match *piece { - parse::Piece::String(s) => { + parse::Piece::Lit(s) => { unfinished_literal.push_str(s); } parse::Piece::NextArgument(box parse::Argument { position, position_span, format }) => { diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 2c99597922e8f..fdcd9caf4ac87 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -126,7 +126,7 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { &mut self, name: &str, params: Vec, - returns: Vec, + mut returns: Vec, args: &[Value], ) -> Cow<'_, [Value]> { // Pass i128 arguments by-ref on Windows. @@ -150,15 +150,19 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { (params, args.into()) }; - // Return i128 using a return area pointer on Windows and s390x. - let adjust_ret_param = - if self.tcx.sess.target.is_like_windows || self.tcx.sess.target.arch == "s390x" { - returns.len() == 1 && returns[0].value_type == types::I128 - } else { - false - }; + let ret_single_i128 = returns.len() == 1 && returns[0].value_type == types::I128; + if ret_single_i128 && self.tcx.sess.target.is_like_windows { + // Return i128 using the vector ABI on Windows + returns[0].value_type = types::I64X2; + + let ret = self.lib_call_unadjusted(name, params, returns, &args)[0]; - if adjust_ret_param { + // FIXME(bytecodealliance/wasmtime#6104) use bitcast instead of store to get from i64x2 to i128 + let ret_ptr = self.create_stack_slot(16, 16); + ret_ptr.store(self, ret, MemFlags::trusted()); + Cow::Owned(vec![ret_ptr.load(self, types::I128, MemFlags::trusted())]) + } else if ret_single_i128 && self.tcx.sess.target.arch == "s390x" { + // Return i128 using a return area pointer on s390x. let mut params = params; let mut args = args.to_vec(); diff --git a/compiler/rustc_codegen_cranelift/src/cast.rs b/compiler/rustc_codegen_cranelift/src/cast.rs index 0b5cb1547fc69..4463631c524be 100644 --- a/compiler/rustc_codegen_cranelift/src/cast.rs +++ b/compiler/rustc_codegen_cranelift/src/cast.rs @@ -96,25 +96,9 @@ pub(crate) fn clif_int_or_float_cast( }, ); - if fx.tcx.sess.target.is_like_windows { - let ret = fx.lib_call( - &name, - vec![AbiParam::new(from_ty)], - vec![AbiParam::new(types::I64X2)], - &[from], - )[0]; - // FIXME(bytecodealliance/wasmtime#6104) use bitcast instead of store to get from i64x2 to i128 - let ret_ptr = fx.create_stack_slot(16, 16); - ret_ptr.store(fx, ret, MemFlags::trusted()); - ret_ptr.load(fx, types::I128, MemFlags::trusted()) - } else { - fx.lib_call( - &name, - vec![AbiParam::new(from_ty)], - vec![AbiParam::new(types::I128)], - &[from], - )[0] - } + fx.lib_call(&name, vec![AbiParam::new(from_ty)], vec![AbiParam::new(types::I128)], &[ + from, + ])[0] } else if to_ty == types::I8 || to_ty == types::I16 { // FIXME implement fcvt_to_*int_sat.i8/i16 let val = if to_signed { diff --git a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs index dcfd7ddabbc42..df5a79086fa3e 100644 --- a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs +++ b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs @@ -33,28 +33,14 @@ pub(crate) fn maybe_codegen<'tcx>( (BinOp::Rem, true) => "__modti3", _ => unreachable!(), }; - if fx.tcx.sess.target.is_like_windows { - let args = [lhs.load_scalar(fx), rhs.load_scalar(fx)]; - let ret = fx.lib_call( - name, - vec![AbiParam::new(types::I128), AbiParam::new(types::I128)], - vec![AbiParam::new(types::I64X2)], - &args, - )[0]; - // FIXME(bytecodealliance/wasmtime#6104) use bitcast instead of store to get from i64x2 to i128 - let ret_place = CPlace::new_stack_slot(fx, lhs.layout()); - ret_place.to_ptr().store(fx, ret, MemFlags::trusted()); - Some(ret_place.to_cvalue(fx)) - } else { - let args = [lhs.load_scalar(fx), rhs.load_scalar(fx)]; - let ret_val = fx.lib_call( - name, - vec![AbiParam::new(types::I128), AbiParam::new(types::I128)], - vec![AbiParam::new(types::I128)], - &args, - )[0]; - Some(CValue::by_val(ret_val, lhs.layout())) - } + let args = [lhs.load_scalar(fx), rhs.load_scalar(fx)]; + let ret_val = fx.lib_call( + name, + vec![AbiParam::new(types::I128), AbiParam::new(types::I128)], + vec![AbiParam::new(types::I128)], + &args, + )[0]; + Some(CValue::by_val(ret_val, lhs.layout())) } BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne | BinOp::Cmp => None, BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => None, diff --git a/compiler/rustc_codegen_cranelift/src/driver/aot.rs b/compiler/rustc_codegen_cranelift/src/driver/aot.rs index 7d5592daac1c3..27adf6318e227 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/aot.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/aot.rs @@ -676,7 +676,7 @@ pub(crate) fn run_aot( .to_owned(); let cgus = if tcx.sess.opts.output_types.should_codegen() { - tcx.collect_and_partition_mono_items(()).1 + tcx.collect_and_partition_mono_items(()).codegen_units } else { // If only `--emit metadata` is used, we shouldn't perform any codegen. // Also `tcx.collect_and_partition_mono_items` may panic in that case. diff --git a/compiler/rustc_codegen_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 560aff43d6538..4e8c8aaaf5c8a 100644 --- a/compiler/rustc_codegen_gcc/src/gcc_util.rs +++ b/compiler/rustc_codegen_gcc/src/gcc_util.rs @@ -1,10 +1,8 @@ -use std::iter::FromIterator; - #[cfg(feature = "master")] use gccjit::Context; use rustc_codegen_ssa::codegen_attrs::check_tied_features; use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::unord::UnordSet; use rustc_session::Session; use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES; @@ -45,12 +43,6 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec Vec { /// Prepare sets of definitions that are relevant to deciding whether something /// is an "unused function" for coverage purposes. fn prepare_usage_sets<'tcx>(tcx: TyCtxt<'tcx>) -> UsageSets<'tcx> { - let (all_mono_items, cgus) = tcx.collect_and_partition_mono_items(()); + let MonoItemPartitions { all_mono_items, codegen_units } = + tcx.collect_and_partition_mono_items(()); // Obtain a MIR body for each function participating in codegen, via an // arbitrary instance. let mut def_ids_seen = FxHashSet::default(); - let def_and_mir_for_all_mono_fns = cgus + let def_and_mir_for_all_mono_fns = codegen_units .iter() .flat_map(|cgu| cgu.items().keys()) .filter_map(|item| match item { diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs index c3d7c217861fb..53611c746a722 100644 --- a/compiler/rustc_codegen_llvm/src/llvm_util.rs +++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs @@ -319,7 +319,6 @@ pub fn target_features_cfg(sess: &Session, allow_unstable: bool) -> Vec sess.target .rust_target_features() .iter() - .filter(|(_, gate, _)| gate.in_cfg()) .filter(|(feature, _, _)| { // skip checking special features, as LLVM may not understand them if RUSTC_SPECIAL_FEATURES.contains(feature) { @@ -388,9 +387,13 @@ pub fn target_features_cfg(sess: &Session, allow_unstable: bool) -> Vec sess.target .rust_target_features() .iter() - .filter(|(_, gate, _)| gate.in_cfg()) .filter_map(|(feature, gate, _)| { - if sess.is_nightly_build() || allow_unstable || gate.requires_nightly().is_none() { + // The `allow_unstable` set is used by rustc internally to determined which target + // features are truly available, so we want to return even perma-unstable "forbidden" + // features. + if allow_unstable + || (gate.in_cfg() && (sess.is_nightly_build() || gate.requires_nightly().is_none())) + { Some(*feature) } else { None @@ -670,12 +673,6 @@ pub(crate) fn global_llvm_features( // Will only be filled when `diagnostics` is set! let mut featsmap = FxHashMap::default(); - // Ensure that all ABI-required features are enabled, and the ABI-forbidden ones - // are disabled. - let abi_feature_constraints = sess.target.abi_required_features(); - let abi_incompatible_set = - FxHashSet::from_iter(abi_feature_constraints.incompatible.iter().copied()); - // Compute implied features let mut all_rust_features = vec![]; for feature in sess.opts.cg.target_feature.split(',') { @@ -746,52 +743,11 @@ pub(crate) fn global_llvm_features( } } - // Ensure that the features we enable/disable are compatible with the ABI. - if enable { - if abi_incompatible_set.contains(feature) { - sess.dcx().emit_warn(ForbiddenCTargetFeature { - feature, - enabled: "enabled", - reason: "this feature is incompatible with the target ABI", - }); - } - } else { - // FIXME: we have to request implied features here since - // negative features do not handle implied features above. - for &required in abi_feature_constraints.required.iter() { - let implied = - sess.target.implied_target_features(std::iter::once(required)); - if implied.contains(feature) { - sess.dcx().emit_warn(ForbiddenCTargetFeature { - feature, - enabled: "disabled", - reason: "this feature is required by the target ABI", - }); - } - } - } - // FIXME(nagisa): figure out how to not allocate a full hashset here. featsmap.insert(feature, enable); } } - // To be sure the ABI-relevant features are all in the right state, we explicitly - // (un)set them here. This means if the target spec sets those features wrong, - // we will silently correct them rather than silently producing wrong code. - // (The target sanity check tries to catch this, but we can't know which features are - // enabled in LLVM by default so we can't be fully sure about that check.) - // We add these at the beginning of the list so that `-Ctarget-features` can - // still override it... that's unsound, but more compatible with past behavior. - all_rust_features.splice( - 0..0, - abi_feature_constraints - .required - .iter() - .map(|&f| (true, f)) - .chain(abi_feature_constraints.incompatible.iter().map(|&f| (false, f))), - ); - // Translate this into LLVM features. let feats = all_rust_features .iter() diff --git a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs index ab65319e3d3e4..27331ce4ca66f 100644 --- a/compiler/rustc_codegen_ssa/src/assert_module_sources.rs +++ b/compiler/rustc_codegen_ssa/src/assert_module_sources.rs @@ -46,8 +46,12 @@ pub fn assert_module_sources(tcx: TyCtxt<'_>, set_reuse: &dyn Fn(&mut CguReuseTr return; } - let available_cgus = - tcx.collect_and_partition_mono_items(()).1.iter().map(|cgu| cgu.name()).collect(); + let available_cgus = tcx + .collect_and_partition_mono_items(()) + .codegen_units + .iter() + .map(|cgu| cgu.name()) + .collect(); let mut ams = AssertModuleSource { tcx, diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 60ab291935256..f8f7bb2dbc69b 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -293,7 +293,7 @@ fn exported_symbols_provider_local( // external linkage is enough for monomorphization to be linked to. let need_visibility = tcx.sess.target.dynamic_linking && !tcx.sess.target.only_cdylib; - let (_, cgus) = tcx.collect_and_partition_mono_items(()); + let cgus = tcx.collect_and_partition_mono_items(()).codegen_units; // The symbols created in this loop are sorted below it #[allow(rustc::potential_query_instability)] diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 014bdeb46ad50..e438bd70c510e 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -619,7 +619,7 @@ pub fn codegen_crate( // Run the monomorphization collector and partition the collected items into // codegen units. - let codegen_units = tcx.collect_and_partition_mono_items(()).1; + let codegen_units = tcx.collect_and_partition_mono_items(()).codegen_units; // Force all codegen_unit queries so they are already either red or green // when compile_codegen_unit accesses them. We are not able to re-execute @@ -1051,7 +1051,7 @@ pub(crate) fn provide(providers: &mut Providers) { config::OptLevel::SizeMin => config::OptLevel::Default, }; - let (defids, _) = tcx.collect_and_partition_mono_items(cratenum); + let defids = tcx.collect_and_partition_mono_items(cratenum).all_mono_items; let any_for_speed = defids.items().any(|id| { let CodegenFnAttrs { optimize, .. } = tcx.codegen_fn_attrs(*id); diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index e775d219c7b42..d7fc5e8e673fd 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -612,9 +612,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::CopyForDeref(place) => { self.codegen_operand(bx, &mir::Operand::Copy(place)) } - mir::Rvalue::RawPtr(mutability, place) => { - let mk_ptr = - move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| Ty::new_ptr(tcx, ty, mutability); + mir::Rvalue::RawPtr(kind, place) => { + let mk_ptr = move |tcx: TyCtxt<'tcx>, ty: Ty<'tcx>| { + Ty::new_ptr(tcx, ty, kind.to_mutbl_lossy()) + }; self.codegen_place_to_pointer(bx, place, mk_ptr) } diff --git a/compiler/rustc_const_eval/messages.ftl b/compiler/rustc_const_eval/messages.ftl index 485c8696342ab..d600d223bffd3 100644 --- a/compiler/rustc_const_eval/messages.ftl +++ b/compiler/rustc_const_eval/messages.ftl @@ -403,7 +403,7 @@ const_eval_uninhabited_enum_variant_read = const_eval_uninhabited_enum_variant_written = writing discriminant of an uninhabited enum variant -const_eval_unmarked_const_fn_exposed = `{$def_path}` cannot be (indirectly) exposed to stable +const_eval_unmarked_const_item_exposed = `{$def_path}` cannot be (indirectly) exposed to stable .help = either mark the callee as `#[rustc_const_stable_indirect]`, or the caller as `#[rustc_const_unstable]` const_eval_unmarked_intrinsic_exposed = intrinsic `{$def_path}` cannot be (indirectly) exposed to stable .help = mark the caller as `#[rustc_const_unstable]`, or mark the intrinsic `#[rustc_intrinsic_const_stable_indirect]` (but this requires team approval) @@ -414,6 +414,7 @@ const_eval_unreachable_unwind = const_eval_unsized_local = unsized locals are not supported const_eval_unstable_const_fn = `{$def_path}` is not yet stable as a const fn +const_eval_unstable_const_trait = `{$def_path}` is not yet stable as a const trait const_eval_unstable_in_stable_exposed = const function that might be (indirectly) exposed to stable cannot use `#[feature({$gate})]` .is_function_call = mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features diff --git a/compiler/rustc_const_eval/src/check_consts/check.rs b/compiler/rustc_const_eval/src/check_consts/check.rs index ed34996a7a7d7..16ead1b978543 100644 --- a/compiler/rustc_const_eval/src/check_consts/check.rs +++ b/compiler/rustc_const_eval/src/check_consts/check.rs @@ -8,6 +8,7 @@ use std::ops::Deref; use rustc_attr_parsing::{ConstStability, StabilityLevel}; use rustc_errors::{Diag, ErrorGuaranteed}; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem}; use rustc_index::bit_set::DenseBitSet; @@ -29,7 +30,7 @@ use super::ops::{self, NonConstOp, Status}; use super::qualifs::{self, HasMutInterior, NeedsDrop, NeedsNonConstDrop}; use super::resolver::FlowSensitiveAnalysis; use super::{ConstCx, Qualif}; -use crate::check_consts::is_safe_to_expose_on_stable_const_fn; +use crate::check_consts::is_fn_or_trait_safe_to_expose_on_stable; use crate::errors; type QualifResults<'mir, 'tcx, Q> = @@ -470,6 +471,88 @@ impl<'mir, 'tcx> Checker<'mir, 'tcx> { self.tcx.crate_level_attribute_injection_span(self.tcx.local_def_id_to_hir_id(id)) }) } + + /// Check the const stability of the given item (fn or trait). + fn check_callee_stability(&mut self, def_id: DefId) { + match self.tcx.lookup_const_stability(def_id) { + Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => { + // All good. + } + None => { + // This doesn't need a separate const-stability check -- const-stability equals + // regular stability, and regular stability is checked separately. + // However, we *do* have to worry about *recursive* const stability. + if self.enforce_recursive_const_stability() + && !is_fn_or_trait_safe_to_expose_on_stable(self.tcx, def_id) + { + self.dcx().emit_err(errors::UnmarkedConstItemExposed { + span: self.span, + def_path: self.tcx.def_path_str(def_id), + }); + } + } + Some(ConstStability { + level: StabilityLevel::Unstable { implied_by: implied_feature, issue, .. }, + feature, + .. + }) => { + // An unstable const fn/trait with a feature gate. + let callee_safe_to_expose_on_stable = + is_fn_or_trait_safe_to_expose_on_stable(self.tcx, def_id); + + // We only honor `span.allows_unstable` aka `#[allow_internal_unstable]` if + // the callee is safe to expose, to avoid bypassing recursive stability. + // This is not ideal since it means the user sees an error, not the macro + // author, but that's also the case if one forgets to set + // `#[allow_internal_unstable]` in the first place. Note that this cannot be + // integrated in the check below since we want to enforce + // `callee_safe_to_expose_on_stable` even if + // `!self.enforce_recursive_const_stability()`. + if (self.span.allows_unstable(feature) + || implied_feature.is_some_and(|f| self.span.allows_unstable(f))) + && callee_safe_to_expose_on_stable + { + return; + } + + // We can't use `check_op` to check whether the feature is enabled because + // the logic is a bit different than elsewhere: local functions don't need + // the feature gate, and there might be an "implied" gate that also suffices + // to allow this. + let feature_enabled = def_id.is_local() + || self.tcx.features().enabled(feature) + || implied_feature.is_some_and(|f| self.tcx.features().enabled(f)) + || { + // When we're compiling the compiler itself we may pull in + // crates from crates.io, but those crates may depend on other + // crates also pulled in from crates.io. We want to ideally be + // able to compile everything without requiring upstream + // modifications, so in the case that this looks like a + // `rustc_private` crate (e.g., a compiler crate) and we also have + // the `-Z force-unstable-if-unmarked` flag present (we're + // compiling a compiler crate), then let this missing feature + // annotation slide. + // This matches what we do in `eval_stability_allow_unstable` for + // regular stability. + feature == sym::rustc_private + && issue == NonZero::new(27812) + && self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked + }; + // Even if the feature is enabled, we still need check_op to double-check + // this if the callee is not safe to expose on stable. + if !feature_enabled || !callee_safe_to_expose_on_stable { + self.check_op(ops::CallUnstable { + def_id, + feature, + feature_enabled, + safe_to_expose_on_stable: callee_safe_to_expose_on_stable, + suggestion_span: self.crate_inject_span(), + is_function_call: self.tcx.def_kind(def_id) != DefKind::Trait, + }); + } + } + } + } } impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { @@ -518,7 +601,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } Rvalue::Ref(_, BorrowKind::Mut { .. }, place) - | Rvalue::RawPtr(Mutability::Mut, place) => { + | Rvalue::RawPtr(RawPtrKind::Mut, place) => { // Inside mutable statics, we allow arbitrary mutable references. // We've allowed `static mut FOO = &mut [elements];` for a long time (the exact // reasons why are lost to history), and there is no reason to restrict that to @@ -536,7 +619,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } Rvalue::Ref(_, BorrowKind::Shared | BorrowKind::Fake(_), place) - | Rvalue::RawPtr(Mutability::Not, place) => { + | Rvalue::RawPtr(RawPtrKind::Const, place) => { let borrowed_place_has_mut_interior = qualifs::in_place::( self.ccx, &mut |local| self.qualifs.has_mut_interior(self.ccx, local, location), @@ -548,6 +631,12 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } } + Rvalue::RawPtr(RawPtrKind::FakeForPtrMetadata, place) => { + // These are only inserted for slice length, so the place must already be indirect. + // This implies we do not have to worry about whether the borrow escapes. + assert!(place.is_indirect(), "fake borrows are always indirect"); + } + Rvalue::Cast( CastKind::PointerCoercion( PointerCoercion::MutToConstPointer @@ -586,12 +675,23 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { ) => {} Rvalue::ShallowInitBox(_, _) => {} - Rvalue::UnaryOp(_, operand) => { + Rvalue::UnaryOp(op, operand) => { let ty = operand.ty(self.body, self.tcx); - if is_int_bool_float_or_char(ty) { - // Int, bool, float, and char operations are fine. - } else { - span_bug!(self.span, "non-primitive type in `Rvalue::UnaryOp`: {:?}", ty); + match op { + UnOp::Not | UnOp::Neg => { + if is_int_bool_float_or_char(ty) { + // Int, bool, float, and char operations are fine. + } else { + span_bug!( + self.span, + "non-primitive type in `Rvalue::UnaryOp{op:?}`: {ty:?}", + ); + } + } + UnOp::PtrMetadata => { + // Getting the metadata from a pointer is always const. + // We already validated the type is valid in the validator. + } } } @@ -716,8 +816,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { span: *fn_span, call_source, }); - // FIXME(const_trait_impl): do a more fine-grained check whether this - // particular trait can be const-stably called. + self.check_callee_stability(trait_did); } else { // Not even a const trait. self.check_op(ops::FnCallNonConst { @@ -793,7 +892,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { // fallback body is safe to expose on stable. let is_const_stable = intrinsic.const_stable || (!intrinsic.must_be_overridden - && is_safe_to_expose_on_stable_const_fn(tcx, callee)); + && is_fn_or_trait_safe_to_expose_on_stable(tcx, callee)); match tcx.lookup_const_stability(callee) { None => { // This doesn't need a separate const-stability check -- const-stability equals @@ -842,83 +941,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'_, 'tcx> { } // Finally, stability for regular function calls -- this is the big one. - match tcx.lookup_const_stability(callee) { - Some(ConstStability { level: StabilityLevel::Stable { .. }, .. }) => { - // All good. - } - None => { - // This doesn't need a separate const-stability check -- const-stability equals - // regular stability, and regular stability is checked separately. - // However, we *do* have to worry about *recursive* const stability. - if self.enforce_recursive_const_stability() - && !is_safe_to_expose_on_stable_const_fn(tcx, callee) - { - self.dcx().emit_err(errors::UnmarkedConstFnExposed { - span: self.span, - def_path: self.tcx.def_path_str(callee), - }); - } - } - Some(ConstStability { - level: StabilityLevel::Unstable { implied_by: implied_feature, issue, .. }, - feature, - .. - }) => { - // An unstable const fn with a feature gate. - let callee_safe_to_expose_on_stable = - is_safe_to_expose_on_stable_const_fn(tcx, callee); - - // We only honor `span.allows_unstable` aka `#[allow_internal_unstable]` if - // the callee is safe to expose, to avoid bypassing recursive stability. - // This is not ideal since it means the user sees an error, not the macro - // author, but that's also the case if one forgets to set - // `#[allow_internal_unstable]` in the first place. Note that this cannot be - // integrated in the check below since we want to enforce - // `callee_safe_to_expose_on_stable` even if - // `!self.enforce_recursive_const_stability()`. - if (self.span.allows_unstable(feature) - || implied_feature.is_some_and(|f| self.span.allows_unstable(f))) - && callee_safe_to_expose_on_stable - { - return; - } - - // We can't use `check_op` to check whether the feature is enabled because - // the logic is a bit different than elsewhere: local functions don't need - // the feature gate, and there might be an "implied" gate that also suffices - // to allow this. - let feature_enabled = callee.is_local() - || tcx.features().enabled(feature) - || implied_feature.is_some_and(|f| tcx.features().enabled(f)) - || { - // When we're compiling the compiler itself we may pull in - // crates from crates.io, but those crates may depend on other - // crates also pulled in from crates.io. We want to ideally be - // able to compile everything without requiring upstream - // modifications, so in the case that this looks like a - // `rustc_private` crate (e.g., a compiler crate) and we also have - // the `-Z force-unstable-if-unmarked` flag present (we're - // compiling a compiler crate), then let this missing feature - // annotation slide. - // This matches what we do in `eval_stability_allow_unstable` for - // regular stability. - feature == sym::rustc_private - && issue == NonZero::new(27812) - && tcx.sess.opts.unstable_opts.force_unstable_if_unmarked - }; - // Even if the feature is enabled, we still need check_op to double-check - // this if the callee is not safe to expose on stable. - if !feature_enabled || !callee_safe_to_expose_on_stable { - self.check_op(ops::FnCallUnstable { - def_id: callee, - feature, - feature_enabled, - safe_to_expose_on_stable: callee_safe_to_expose_on_stable, - suggestion_span: self.crate_inject_span(), - }); - } - } - } + self.check_callee_stability(callee); } // Forbid all `Drop` terminators unless the place being dropped is a local with no diff --git a/compiler/rustc_const_eval/src/check_consts/mod.rs b/compiler/rustc_const_eval/src/check_consts/mod.rs index ab68691f1b97a..bfa0a0319c344 100644 --- a/compiler/rustc_const_eval/src/check_consts/mod.rs +++ b/compiler/rustc_const_eval/src/check_consts/mod.rs @@ -56,7 +56,7 @@ impl<'mir, 'tcx> ConstCx<'mir, 'tcx> { self.const_kind == Some(hir::ConstContext::ConstFn) && (self.tcx.features().staged_api() || self.tcx.sess.opts.unstable_opts.force_unstable_if_unmarked) - && is_safe_to_expose_on_stable_const_fn(self.tcx, self.def_id().to_def_id()) + && is_fn_or_trait_safe_to_expose_on_stable(self.tcx, self.def_id().to_def_id()) } fn is_async(&self) -> bool { @@ -84,28 +84,14 @@ pub fn rustc_allow_const_fn_unstable( attr::rustc_allow_const_fn_unstable(tcx.sess, attrs).any(|name| name == feature_gate) } -/// Returns `true` if the given `const fn` is "safe to expose on stable". -/// -/// Panics if the given `DefId` does not refer to a `const fn`. +/// Returns `true` if the given `def_id` (trait or function) is "safe to expose on stable". /// /// This is relevant within a `staged_api` crate. Unlike with normal features, the use of unstable /// const features *recursively* taints the functions that use them. This is to avoid accidentally /// exposing e.g. the implementation of an unstable const intrinsic on stable. So we partition the /// world into two functions: those that are safe to expose on stable (and hence may not use /// unstable features, not even recursively), and those that are not. -pub fn is_safe_to_expose_on_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - // A default body in a `#[const_trait]` is not const-stable because const trait fns currently - // cannot be const-stable. These functions can't be called from anything stable, so we shouldn't - // restrict them to only call const-stable functions. - if tcx.is_const_default_method(def_id) { - // FIXME(const_trait_impl): we have to eventually allow some of these if these things can ever be stable. - // They should probably behave like regular `const fn` for that... - return false; - } - - // Const-stability is only relevant for `const fn`. - assert!(tcx.is_const_fn(def_id)); - +pub fn is_fn_or_trait_safe_to_expose_on_stable(tcx: TyCtxt<'_>, def_id: DefId) -> bool { match tcx.lookup_const_stability(def_id) { None => { // In a `staged_api` crate, we do enforce recursive const stability for all unmarked diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 3c83a7b92cdc6..7756e51c4c5f2 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -377,11 +377,11 @@ fn build_error_for_const_call<'tcx>( err } -/// A call to an `#[unstable]` const fn or `#[rustc_const_unstable]` function. +/// A call to an `#[unstable]` const fn, `#[rustc_const_unstable]` function or trait. /// -/// Contains the name of the feature that would allow the use of this function. +/// Contains the name of the feature that would allow the use of this function/trait. #[derive(Debug)] -pub(crate) struct FnCallUnstable { +pub(crate) struct CallUnstable { pub def_id: DefId, pub feature: Symbol, /// If this is true, then the feature is enabled, but we need to still check if it is safe to @@ -389,24 +389,33 @@ pub(crate) struct FnCallUnstable { pub feature_enabled: bool, pub safe_to_expose_on_stable: bool, pub suggestion_span: Option, + /// true if `def_id` is the function we are calling, false if `def_id` is an unstable trait. + pub is_function_call: bool, } -impl<'tcx> NonConstOp<'tcx> for FnCallUnstable { +impl<'tcx> NonConstOp<'tcx> for CallUnstable { fn status_in_item(&self, _ccx: &ConstCx<'_, 'tcx>) -> Status { Status::Unstable { gate: self.feature, gate_already_checked: self.feature_enabled, safe_to_expose_on_stable: self.safe_to_expose_on_stable, - is_function_call: true, + is_function_call: self.is_function_call, } } fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { assert!(!self.feature_enabled); - let mut err = ccx.dcx().create_err(errors::UnstableConstFn { - span, - def_path: ccx.tcx.def_path_str(self.def_id), - }); + let mut err = if self.is_function_call { + ccx.dcx().create_err(errors::UnstableConstFn { + span, + def_path: ccx.tcx.def_path_str(self.def_id), + }) + } else { + ccx.dcx().create_err(errors::UnstableConstTrait { + span, + def_path: ccx.tcx.def_path_str(self.def_id), + }) + }; // FIXME: make this translatable let msg = format!("add `#![feature({})]` to the crate attributes to enable", self.feature); #[allow(rustc::untranslatable_diagnostic)] diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 1ee9214c4b2a0..a2635885098e3 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -121,6 +121,14 @@ pub(crate) struct UnstableConstFn { pub def_path: String, } +#[derive(Diagnostic)] +#[diag(const_eval_unstable_const_trait)] +pub(crate) struct UnstableConstTrait { + #[primary_span] + pub span: Span, + pub def_path: String, +} + #[derive(Diagnostic)] #[diag(const_eval_unstable_intrinsic)] pub(crate) struct UnstableIntrinsic { @@ -139,9 +147,9 @@ pub(crate) struct UnstableIntrinsic { } #[derive(Diagnostic)] -#[diag(const_eval_unmarked_const_fn_exposed)] +#[diag(const_eval_unmarked_const_item_exposed)] #[help] -pub(crate) struct UnmarkedConstFnExposed { +pub(crate) struct UnmarkedConstItemExposed { #[primary_span] pub span: Span, pub def_path: String, diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 2772c94d52b0b..d736f14f5a389 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -830,9 +830,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { /// [`InterpCx::get_alloc_info`] if all you need to check is whether the kind is /// [`AllocKind::Dead`] because it doesn't have to look up the type and layout of statics. pub fn is_alloc_live(&self, id: AllocId) -> bool { - self.tcx.try_get_global_alloc(id).is_some() - || self.memory.alloc_map.contains_key_ref(&id) + self.memory.alloc_map.contains_key_ref(&id) || self.memory.extra_fn_ptr_map.contains_key(&id) + // We check `tcx` last as that has to acquire a lock in `many-seeds` mode. + // This also matches the order in `get_alloc_info`. + || self.tcx.try_get_global_alloc(id).is_some() } /// Obtain the size and alignment of an allocation, even if that allocation has diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index b61865be66788..d9c0ff5acd116 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -237,7 +237,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { self.write_immediate(*val, &dest)?; } - RawPtr(_, place) => { + RawPtr(kind, place) => { // Figure out whether this is an addr_of of an already raw place. let place_base_raw = if place.is_indirect_first_projection() { let ty = self.frame().body.local_decls[place.local].ty; @@ -250,8 +250,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { let src = self.eval_place(place)?; let place = self.force_allocation(&src)?; let mut val = ImmTy::from_immediate(place.to_ref(self), dest.layout); - if !place_base_raw { - // If this was not already raw, it needs retagging. + if !place_base_raw && !kind.is_fake() { + // If this was not already raw, it needs retagging -- except for "fake" + // raw borrows whose defining property is that they do not get retagged. val = M::retag_ptr_value(self, mir::RetagKind::Raw, &val)?; } self.write_immediate(*val, &dest)?; diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index 889a8299c18f8..8e5af33d8b67f 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -10,11 +10,11 @@ bitflags = "2.4.1" either = "1.0" elsa = "=1.7.1" ena = "0.14.3" -indexmap = { version = "2.4.0", features = ["rustc-rayon"] } +indexmap = "2.4.0" jobserver_crate = { version = "0.1.28", package = "jobserver" } measureme = "11" rustc-hash = "2.0.0" -rustc-rayon = "0.5.0" +rustc-rayon = { version = "0.5.1", features = ["indexmap"] } rustc-stable-hash = { version = "0.1.0", features = ["nightly"] } rustc_arena = { path = "../rustc_arena" } rustc_graphviz = { path = "../rustc_graphviz" } diff --git a/compiler/rustc_data_structures/src/graph/mod.rs b/compiler/rustc_data_structures/src/graph/mod.rs index 92035e8bc480f..4a1e5db6768db 100644 --- a/compiler/rustc_data_structures/src/graph/mod.rs +++ b/compiler/rustc_data_structures/src/graph/mod.rs @@ -14,7 +14,23 @@ mod tests; pub trait DirectedGraph { type Node: Idx; + /// Returns the total number of nodes in this graph. + /// + /// Several graph algorithm implementations assume that every node ID is + /// strictly less than the number of nodes, i.e. nodes are densely numbered. + /// That assumption allows them to use `num_nodes` to allocate per-node + /// data structures, indexed by node. fn num_nodes(&self) -> usize; + + /// Iterates over all nodes of a graph in ascending numeric order. + /// + /// Assumes that nodes are densely numbered, i.e. every index in + /// `0..num_nodes` is a valid node. + fn iter_nodes( + &self, + ) -> impl Iterator + DoubleEndedIterator + ExactSizeIterator { + (0..self.num_nodes()).map(::new) + } } pub trait NumEdges: DirectedGraph { diff --git a/compiler/rustc_data_structures/src/graph/scc/mod.rs b/compiler/rustc_data_structures/src/graph/scc/mod.rs index 06fedef00fc3f..93f6192b10b03 100644 --- a/compiler/rustc_data_structures/src/graph/scc/mod.rs +++ b/compiler/rustc_data_structures/src/graph/scc/mod.rs @@ -333,8 +333,8 @@ where to_annotation, }; - let scc_indices = (0..num_nodes) - .map(G::Node::new) + let scc_indices = graph + .iter_nodes() .map(|node| match this.start_walk_from(node) { WalkReturn::Complete { scc_index, .. } => scc_index, WalkReturn::Cycle { min_depth, .. } => { diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 20be21446098b..c9d38a0f93253 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -160,7 +160,7 @@ pub trait Callbacks { fn after_crate_root_parsing( &mut self, _compiler: &interface::Compiler, - _queries: &ast::Crate, + _krate: &mut ast::Crate, ) -> Compilation { Compilation::Continue } @@ -311,7 +311,7 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) // Parse the crate root source code (doesn't parse submodules yet) // Everything else is parsed during macro expansion. - let krate = passes::parse(sess); + let mut krate = passes::parse(sess); // If pretty printing is requested: Figure out the representation, print it and exit if let Some(pp_mode) = sess.opts.pretty { @@ -328,7 +328,7 @@ pub fn run_compiler(at_args: &[String], callbacks: &mut (dyn Callbacks + Send)) return early_exit(); } - if callbacks.after_crate_root_parsing(compiler, &krate) == Compilation::Stop { + if callbacks.after_crate_root_parsing(compiler, &mut krate) == Compilation::Stop { return early_exit(); } diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index dbc5c634c455c..d2ab98bae8919 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -2362,7 +2362,7 @@ fn try_report_async_mismatch<'tcx>( // the right span is a bit difficult. return Err(tcx.sess.dcx().emit_err(MethodShouldReturnFuture { span: tcx.def_span(impl_m.def_id), - method_name: trait_m.name, + method_name: tcx.item_ident(impl_m.def_id), trait_item_span: tcx.hir().span_if_local(trait_m.def_id), })); } diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 92b18c80fd82d..cc0b7fdd8dd1f 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -197,7 +197,7 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) { fn report_forbidden_specialization(tcx: TyCtxt<'_>, impl_item: DefId, parent_impl: DefId) { let span = tcx.def_span(impl_item); - let ident = tcx.item_name(impl_item); + let ident = tcx.item_ident(impl_item); let err = match tcx.span_of_impl(parent_impl) { Ok(sp) => errors::ImplNotMarkedDefault::Ok { span, ident, ok_label: sp }, @@ -297,7 +297,7 @@ fn default_body_is_unstable( reason: Option, issue: Option>, ) { - let missing_item_name = tcx.associated_item(item_did).name; + let missing_item_name = tcx.item_ident(item_did); let (mut some_note, mut none_note, mut reason_str) = (false, false, String::new()); match reason { Some(r) => { @@ -457,7 +457,7 @@ fn fn_sig_suggestion<'tcx>( let asyncness = if tcx.asyncness(assoc.def_id).is_async() { output = if let ty::Alias(_, alias_ty) = *output.kind() { - tcx.explicit_item_super_predicates(alias_ty.def_id) + tcx.explicit_item_self_bounds(alias_ty.def_id) .iter_instantiated_copied(tcx, alias_ty.args) .find_map(|(bound, _)| { bound.as_projection_clause()?.no_bound_vars()?.term.as_type() diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index b43a808ccdc10..27a7c2ea530ff 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -292,7 +292,7 @@ fn visit_implementation_of_dispatch_from_dyn(checker: &Checker<'_>) -> Result<() res = Err(tcx.dcx().emit_err(errors::DispatchFromDynZST { span, - name: field.name, + name: field.ident(tcx), ty: ty_a, })); diff --git a/compiler/rustc_hir_analysis/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs index d17ee86ba667c..dbf7a7378f5ab 100644 --- a/compiler/rustc_hir_analysis/src/coherence/orphan.rs +++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs @@ -465,8 +465,8 @@ fn emit_orphan_check_error<'tcx>( traits::OrphanCheckErr::UncoveredTyParams(UncoveredTyParams { uncovered, local_ty }) => { let mut reported = None; for param_def_id in uncovered { - let span = tcx.def_ident_span(param_def_id).unwrap(); - let name = tcx.item_name(param_def_id); + let name = tcx.item_ident(param_def_id); + let span = name.span; reported.get_or_insert(match local_ty { Some(local_type) => tcx.dcx().emit_err(errors::TyParamFirstLocal { @@ -492,7 +492,7 @@ fn lint_uncovered_ty_params<'tcx>( for param_def_id in uncovered { let span = tcx.def_ident_span(param_def_id).unwrap(); - let name = tcx.item_name(param_def_id); + let name = tcx.item_ident(param_def_id); match local_ty { Some(local_type) => tcx.emit_node_span_lint( diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index c517d25fcbfc0..cad7b2a1e57b9 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -65,9 +65,9 @@ pub fn provide(providers: &mut Providers) { type_alias_is_lazy: type_of::type_alias_is_lazy, item_bounds: item_bounds::item_bounds, explicit_item_bounds: item_bounds::explicit_item_bounds, - item_super_predicates: item_bounds::item_super_predicates, - explicit_item_super_predicates: item_bounds::explicit_item_super_predicates, - item_non_self_assumptions: item_bounds::item_non_self_assumptions, + item_self_bounds: item_bounds::item_self_bounds, + explicit_item_self_bounds: item_bounds::explicit_item_self_bounds, + item_non_self_bounds: item_bounds::item_non_self_bounds, impl_super_outlives: item_bounds::impl_super_outlives, generics_of: generics_of::generics_of, predicates_of: predicates_of::predicates_of, @@ -328,9 +328,9 @@ impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> { self.tcx.ensure().generics_of(def_id); self.tcx.ensure().predicates_of(def_id); self.tcx.ensure().explicit_item_bounds(def_id); - self.tcx.ensure().explicit_item_super_predicates(def_id); + self.tcx.ensure().explicit_item_self_bounds(def_id); self.tcx.ensure().item_bounds(def_id); - self.tcx.ensure().item_super_predicates(def_id); + self.tcx.ensure().item_self_bounds(def_id); if self.tcx.is_conditionally_const(def_id) { self.tcx.ensure().explicit_implied_const_bounds(def_id); self.tcx.ensure().const_conditions(def_id); @@ -822,7 +822,7 @@ fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { hir::TraitItemKind::Type(_, Some(_)) => { tcx.ensure().item_bounds(def_id); - tcx.ensure().item_super_predicates(def_id); + tcx.ensure().item_self_bounds(def_id); tcx.ensure().type_of(def_id); // Account for `type T = _;`. let mut visitor = HirPlaceholderCollector::default(); @@ -839,7 +839,7 @@ fn lower_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) { hir::TraitItemKind::Type(_, None) => { tcx.ensure().item_bounds(def_id); - tcx.ensure().item_super_predicates(def_id); + tcx.ensure().item_self_bounds(def_id); // #74612: Visit and try to find bad placeholders // even if there is no concrete type. let mut visitor = HirPlaceholderCollector::default(); @@ -928,7 +928,7 @@ fn lower_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId) { tcx.dcx().emit_err(errors::EnumDiscriminantOverflowed { span, discr: prev_discr.unwrap().to_string(), - item_name: tcx.item_name(variant.def_id), + item_name: tcx.item_ident(variant.def_id), wrapped_discr: wrapped_discr.to_string(), }); None @@ -990,11 +990,10 @@ impl<'tcx> FieldUniquenessCheckContext<'tcx> { } /// Check if a given field `ident` declared at `field_decl` has been declared elsewhere before. - fn check_field_decl(&mut self, ident: Ident, field_decl: FieldDeclSpan) { + fn check_field_decl(&mut self, field_name: Ident, field_decl: FieldDeclSpan) { use FieldDeclSpan::*; - let field_name = ident.name; - let ident = ident.normalize_to_macros_2_0(); - match (field_decl, self.seen_fields.get(&ident).copied()) { + let field_name = field_name.normalize_to_macros_2_0(); + match (field_decl, self.seen_fields.get(&field_name).copied()) { (NotNested(span), Some(NotNested(prev_span))) => { self.tcx.dcx().emit_err(errors::FieldAlreadyDeclared::NotNested { field_name, @@ -1035,7 +1034,7 @@ impl<'tcx> FieldUniquenessCheckContext<'tcx> { }); } (field_decl, None) => { - self.seen_fields.insert(ident, field_decl); + self.seen_fields.insert(field_name, field_decl); } } } diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index d3ff1f7bebe65..e37a11b684453 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -350,7 +350,7 @@ pub(super) fn explicit_item_bounds( explicit_item_bounds_with_filter(tcx, def_id, PredicateFilter::All) } -pub(super) fn explicit_item_super_predicates( +pub(super) fn explicit_item_self_bounds( tcx: TyCtxt<'_>, def_id: LocalDefId, ) -> ty::EarlyBinder<'_, &'_ [(ty::Clause<'_>, Span)]> { @@ -434,11 +434,11 @@ pub(super) fn item_bounds(tcx: TyCtxt<'_>, def_id: DefId) -> ty::EarlyBinder<'_, }) } -pub(super) fn item_super_predicates( +pub(super) fn item_self_bounds( tcx: TyCtxt<'_>, def_id: DefId, ) -> ty::EarlyBinder<'_, ty::Clauses<'_>> { - tcx.explicit_item_super_predicates(def_id).map_bound(|bounds| { + tcx.explicit_item_self_bounds(def_id).map_bound(|bounds| { tcx.mk_clauses_from_iter( util::elaborate(tcx, bounds.iter().map(|&(bound, _span)| bound)).filter_only_self(), ) @@ -447,13 +447,12 @@ pub(super) fn item_super_predicates( /// This exists as an optimization to compute only the item bounds of the item /// that are not `Self` bounds. -pub(super) fn item_non_self_assumptions( +pub(super) fn item_non_self_bounds( tcx: TyCtxt<'_>, def_id: DefId, ) -> ty::EarlyBinder<'_, ty::Clauses<'_>> { let all_bounds: FxIndexSet<_> = tcx.item_bounds(def_id).skip_binder().iter().collect(); - let own_bounds: FxIndexSet<_> = - tcx.item_super_predicates(def_id).skip_binder().iter().collect(); + let own_bounds: FxIndexSet<_> = tcx.item_self_bounds(def_id).skip_binder().iter().collect(); if all_bounds.len() == own_bounds.len() { ty::EarlyBinder::bind(ty::ListWithCachedTypeInfo::empty()) } else { diff --git a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs index d1a1e36c1d5a0..e2b9fe0f9f72d 100644 --- a/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs +++ b/compiler/rustc_hir_analysis/src/collect/type_of/opaque.rs @@ -55,7 +55,7 @@ pub(super) fn find_opaque_ty_constraints_for_impl_trait_in_assoc_type( } else { let reported = tcx.dcx().emit_err(UnconstrainedOpaqueType { span: tcx.def_span(def_id), - name: tcx.item_name(parent_def_id.to_def_id()), + name: tcx.item_ident(parent_def_id.to_def_id()), what: "impl", }); Ty::new_error(tcx, reported) @@ -136,7 +136,7 @@ pub(super) fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: Local } let reported = tcx.dcx().emit_err(UnconstrainedOpaqueType { span: tcx.def_span(def_id), - name: tcx.item_name(parent_def_id.to_def_id()), + name: tcx.item_ident(parent_def_id.to_def_id()), what: match tcx.hir_node(scope) { _ if scope == hir::CRATE_HIR_ID => "module", Node::Item(hir::Item { kind: hir::ItemKind::Mod(_), .. }) => "module", diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index a0f365142baed..1dcea5d033537 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -217,7 +217,7 @@ pub(crate) struct DropImplOnWrongItem { pub(crate) enum FieldAlreadyDeclared { #[diag(hir_analysis_field_already_declared, code = E0124)] NotNested { - field_name: Symbol, + field_name: Ident, #[primary_span] #[label] span: Span, @@ -226,7 +226,7 @@ pub(crate) enum FieldAlreadyDeclared { }, #[diag(hir_analysis_field_already_declared_current_nested)] CurrentNested { - field_name: Symbol, + field_name: Ident, #[primary_span] #[label] span: Span, @@ -239,7 +239,7 @@ pub(crate) enum FieldAlreadyDeclared { }, #[diag(hir_analysis_field_already_declared_previous_nested)] PreviousNested { - field_name: Symbol, + field_name: Ident, #[primary_span] #[label] span: Span, @@ -252,7 +252,7 @@ pub(crate) enum FieldAlreadyDeclared { }, #[diag(hir_analysis_field_already_declared_both_nested)] BothNested { - field_name: Symbol, + field_name: Ident, #[primary_span] #[label] span: Span, @@ -418,7 +418,7 @@ pub(crate) struct ValueOfAssociatedStructAlreadySpecified { pub(crate) struct UnconstrainedOpaqueType { #[primary_span] pub span: Span, - pub name: Symbol, + pub name: Ident, pub what: &'static str, } @@ -802,7 +802,7 @@ pub(crate) struct EnumDiscriminantOverflowed { #[label] pub span: Span, pub discr: String, - pub item_name: Symbol, + pub item_name: Ident, pub wrapped_discr: String, } @@ -893,7 +893,7 @@ pub(crate) enum ImplNotMarkedDefault { span: Span, #[label(hir_analysis_ok_label)] ok_label: Span, - ident: Symbol, + ident: Ident, }, #[diag(hir_analysis_impl_not_marked_default_err, code = E0520)] #[note] @@ -901,7 +901,7 @@ pub(crate) enum ImplNotMarkedDefault { #[primary_span] span: Span, cname: Symbol, - ident: Symbol, + ident: Ident, }, } @@ -977,7 +977,7 @@ pub(crate) struct MissingTraitItemUnstable { pub some_note: bool, #[note(hir_analysis_none_note)] pub none_note: bool, - pub missing_item_name: Symbol, + pub missing_item_name: Ident, pub feature: Symbol, pub reason: String, } @@ -1249,7 +1249,7 @@ pub(crate) struct InherentNominal { pub(crate) struct DispatchFromDynZST<'a> { #[primary_span] pub span: Span, - pub name: Symbol, + pub name: Ident, pub ty: Ty<'a>, } @@ -1389,7 +1389,7 @@ pub(crate) struct TyParamFirstLocal<'tcx> { pub span: Span, #[note(hir_analysis_case_note)] pub note: (), - pub param: Symbol, + pub param: Ident, pub local_type: Ty<'tcx>, } @@ -1401,7 +1401,7 @@ pub(crate) struct TyParamFirstLocalLint<'tcx> { pub span: Span, #[note(hir_analysis_case_note)] pub note: (), - pub param: Symbol, + pub param: Ident, pub local_type: Ty<'tcx>, } @@ -1414,7 +1414,7 @@ pub(crate) struct TyParamSome { pub span: Span, #[note(hir_analysis_only_note)] pub note: (), - pub param: Symbol, + pub param: Ident, } #[derive(LintDiagnostic)] @@ -1425,7 +1425,7 @@ pub(crate) struct TyParamSomeLint { pub span: Span, #[note(hir_analysis_only_note)] pub note: (), - pub param: Symbol, + pub param: Ident, } #[derive(Diagnostic)] @@ -1533,7 +1533,7 @@ pub(crate) struct UnsupportedDelegation<'a> { pub(crate) struct MethodShouldReturnFuture { #[primary_span] pub span: Span, - pub method_name: Symbol, + pub method_name: Ident, #[note] pub trait_item_span: Option, } @@ -1585,7 +1585,7 @@ pub(crate) struct UnconstrainedGenericParameter { #[primary_span] #[label] pub span: Span, - pub param_name: Symbol, + pub param_name: Ident, pub param_def_kind: &'static str, #[note(hir_analysis_const_param_note)] pub const_param_note: bool, diff --git a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs index 5ae7944f6d53e..6740734972940 100644 --- a/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_hir_analysis/src/errors/wrong_number_of_generic_args.rs @@ -495,7 +495,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { .iter() .any(|constraint| constraint.ident.name == item.name) }) - .map(|item| item.name.to_ident_string()) + .map(|item| self.tcx.item_ident(item.def_id).to_string()) .collect() } else { Vec::default() diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs index 42034736ad674..fd5a7089b4cf8 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs @@ -152,7 +152,7 @@ pub(crate) fn enforce_impl_lifetime_params_are_constrained( { let mut diag = tcx.dcx().create_err(UnconstrainedGenericParameter { span: tcx.def_span(param.def_id), - param_name: param.name, + param_name: tcx.item_ident(param.def_id), param_def_kind: tcx.def_descr(param.def_id), const_param_note: false, const_param_note2: false, @@ -223,7 +223,7 @@ pub(crate) fn enforce_impl_non_lifetime_params_are_constrained( let const_param_note = matches!(param.kind, ty::GenericParamDefKind::Const { .. }); let mut diag = tcx.dcx().create_err(UnconstrainedGenericParameter { span: tcx.def_span(param.def_id), - param_name: param.name, + param_name: tcx.item_ident(param.def_id), param_def_kind: tcx.def_descr(param.def_id), const_param_note, const_param_note2: const_param_note, diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index b8652d82d91bc..62dfffb560bac 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -296,7 +296,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Given the expected type, figures out what it can about this closure we /// are about to type check: - #[instrument(skip(self), level = "debug")] + #[instrument(skip(self), level = "debug", ret)] fn deduce_closure_signature( &self, expected_ty: Ty<'tcx>, @@ -308,7 +308,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_ty, closure_kind, self.tcx - .explicit_item_super_predicates(def_id) + .explicit_item_self_bounds(def_id) .iter_instantiated_copied(self.tcx, args) .map(|(c, s)| (c.as_predicate(), s)), ), @@ -378,6 +378,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { bound_predicate.rebind(proj_predicate), ), ); + // Make sure that we didn't infer a signature that mentions itself. // This can happen when we elaborate certain supertrait bounds that // mention projections containing the `Self` type. See #105401. @@ -395,8 +396,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - if inferred_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() { - expected_sig = inferred_sig; + + // Don't infer a closure signature from a goal that names the closure type as this will + // (almost always) lead to occurs check errors later in type checking. + if self.next_trait_solver() + && let Some(inferred_sig) = inferred_sig + { + // In the new solver it is difficult to explicitly normalize the inferred signature as we + // would have to manually handle universes and rewriting bound vars and placeholders back + // and forth. + // + // Instead we take advantage of the fact that we relating an inference variable with an alias + // will only instantiate the variable if the alias is rigid(*not quite). Concretely we: + // - Create some new variable `?sig` + // - Equate `?sig` with the unnormalized signature, e.g. `fn( as Trait>::Assoc)` + // - Depending on whether ` as Trait>::Assoc` is rigid, ambiguous or normalizeable, + // we will either wind up with `?sig= as Trait>::Assoc/?y/ConcreteTy` respectively. + // + // *: In cases where there are ambiguous aliases in the signature that make use of bound vars + // they will wind up present in `?sig` even though they are non-rigid. + // + // This is a bit weird and means we may wind up discarding the goal due to it naming `expected_ty` + // even though the normalized form may not name `expected_ty`. However, this matches the existing + // behaviour of the old solver and would be technically a breaking change to fix. + let generalized_fnptr_sig = self.next_ty_var(span); + let inferred_fnptr_sig = Ty::new_fn_ptr(self.tcx, inferred_sig.sig); + self.demand_eqtype(span, inferred_fnptr_sig, generalized_fnptr_sig); + + let resolved_sig = self.resolve_vars_if_possible(generalized_fnptr_sig); + + if resolved_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() { + expected_sig = Some(ExpectedSig { + cause_span: inferred_sig.cause_span, + sig: resolved_sig.fn_sig(self.tcx), + }); + } + } else { + if inferred_sig.visit_with(&mut MentionsTy { expected_ty }).is_continue() { + expected_sig = inferred_sig; + } } } @@ -981,7 +1019,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => self .tcx - .explicit_item_super_predicates(def_id) + .explicit_item_self_bounds(def_id) .iter_instantiated_copied(self.tcx, args) .find_map(|(p, s)| get_future_output(p.as_predicate(), s))?, ty::Error(_) => return Some(ret_ty), diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 47abba1cc29c9..153fd6001bbc8 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -1847,30 +1847,26 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { fcx.probe(|_| { let ocx = ObligationCtxt::new(fcx); ocx.register_obligations( - fcx.tcx.item_super_predicates(rpit_def_id).iter_identity().filter_map( - |clause| { - let predicate = clause - .kind() - .map_bound(|clause| match clause { - ty::ClauseKind::Trait(trait_pred) => Some( - ty::ClauseKind::Trait(trait_pred.with_self_ty(fcx.tcx, ty)), - ), - ty::ClauseKind::Projection(proj_pred) => { - Some(ty::ClauseKind::Projection( - proj_pred.with_self_ty(fcx.tcx, ty), - )) - } - _ => None, - }) - .transpose()?; - Some(Obligation::new( - fcx.tcx, - ObligationCause::dummy(), - fcx.param_env, - predicate, - )) - }, - ), + fcx.tcx.item_self_bounds(rpit_def_id).iter_identity().filter_map(|clause| { + let predicate = clause + .kind() + .map_bound(|clause| match clause { + ty::ClauseKind::Trait(trait_pred) => Some(ty::ClauseKind::Trait( + trait_pred.with_self_ty(fcx.tcx, ty), + )), + ty::ClauseKind::Projection(proj_pred) => Some( + ty::ClauseKind::Projection(proj_pred.with_self_ty(fcx.tcx, ty)), + ), + _ => None, + }) + .transpose()?; + Some(Obligation::new( + fcx.tcx, + ObligationCause::dummy(), + fcx.param_env, + predicate, + )) + }), ); ocx.select_where_possible().is_empty() }) diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index bdd436302f489..1c828591bcbd2 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -3337,10 +3337,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) .map(|mut field_path| { field_path.pop(); - field_path - .iter() - .map(|id| format!("{}.", id.name.to_ident_string())) - .collect::() + field_path.iter().map(|id| format!("{}.", id)).collect::() }) .collect::>(); candidate_fields.sort(); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs index eb5fe3a86e4c5..dc10b53fd8390 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/inspect_obligations.rs @@ -7,6 +7,7 @@ use rustc_span::Span; use rustc_trait_selection::solve::inspect::{ InspectConfig, InspectGoal, ProofTreeInferCtxtExt, ProofTreeVisitor, }; +use rustc_type_ir::solve::GoalSource; use tracing::{debug, instrument, trace}; use crate::FnCtxt; @@ -119,7 +120,21 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for NestedObligationsForSelfTy<'a, 'tcx> { fn visit_goal(&mut self, inspect_goal: &InspectGoal<'_, 'tcx>) { let tcx = self.fcx.tcx; let goal = inspect_goal.goal(); - if self.fcx.predicate_has_self_ty(goal.predicate, self.self_ty) { + if self.fcx.predicate_has_self_ty(goal.predicate, self.self_ty) + // We do not push the instantiated forms of goals as it would cause any + // aliases referencing bound vars to go from having escaping bound vars to + // being able to be normalized to an inference variable. + // + // This is mostly just a hack as arbitrary nested goals could still contain + // such aliases while having a different `GoalSource`. Closure signature inference + // however can't really handle *every* higher ranked `Fn` goal also being present + // in the form of `?c: Fn<(>::Assoc)`. + // + // This also just better matches the behaviour of the old solver where we do not + // encounter instantiated forms of goals, only nested goals that referred to bound + // vars from instantiated goals. + && !matches!(inspect_goal.source(), GoalSource::InstantiateHigherRanked) + { self.obligations_for_self_ty.push(traits::Obligation::new( tcx, self.root_cause.clone(), diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index c9e55695e5d97..07e013e4afa67 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -453,7 +453,7 @@ fn report_unexpected_variant_res( ); let fields = fields .iter() - .map(|field| format!("{}: _", field.name.to_ident_string())) + .map(|field| format!("{}: _", field.ident(tcx))) .collect::>() .join(", "); let sugg = format!(" {{ {} }}", fields); diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 89843da9d7bcc..5d4e67d1a0e60 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2714,7 +2714,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .map(|field_path| { field_path .iter() - .map(|id| id.name.to_ident_string()) + .map(|id| id.to_string()) .collect::>() .join(".") }) diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index ba63507113578..e1bd9ae2e6727 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -1,6 +1,7 @@ use rustc_errors::Applicability; use rustc_hir_analysis::autoderef::Autoderef; use rustc_infer::infer::InferOk; +use rustc_infer::traits::{Obligation, ObligationCauseCode}; use rustc_middle::span_bug; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, OverloadedDeref, @@ -136,8 +137,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut self_ty = adjusted_ty; if unsize { // We only unsize arrays here. - if let ty::Array(element_ty, _) = adjusted_ty.kind() { - self_ty = Ty::new_slice(self.tcx, *element_ty); + if let ty::Array(element_ty, ct) = *adjusted_ty.kind() { + self.register_predicate(Obligation::new( + self.tcx, + self.cause(base_expr.span, ObligationCauseCode::ArrayLen(adjusted_ty)), + self.param_env, + ty::ClauseKind::ConstArgHasType(ct, self.tcx.types.usize), + )); + self_ty = Ty::new_slice(self.tcx, element_ty); } else { continue; } diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs index 7a21c2883d1ac..d68f3639176f9 100644 --- a/compiler/rustc_infer/src/infer/outlives/verify.rs +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -281,7 +281,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { alias_ty: ty::AliasTy<'tcx>, ) -> impl Iterator> { let tcx = self.tcx; - let bounds = tcx.item_super_predicates(alias_ty.def_id); + let bounds = tcx.item_self_bounds(alias_ty.def_id); trace!("{:#?}", bounds.skip_binder()); bounds .iter_instantiated(tcx, alias_ty.args) diff --git a/compiler/rustc_interface/messages.ftl b/compiler/rustc_interface/messages.ftl index 47dfbc1d7fbf1..31123625369bd 100644 --- a/compiler/rustc_interface/messages.ftl +++ b/compiler/rustc_interface/messages.ftl @@ -1,3 +1,8 @@ +interface_abi_required_feature = + target feature `{$feature}` must be {$enabled} to ensure that the ABI of the current target can be implemented correctly + .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +interface_abi_required_feature_issue = for more information, see issue #116344 + interface_cant_emit_mir = could not emit MIR: {$error} diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs index 939980a932fdb..b62950d670964 100644 --- a/compiler/rustc_interface/src/errors.rs +++ b/compiler/rustc_interface/src/errors.rs @@ -103,3 +103,12 @@ pub struct IgnoringOutDir; #[derive(Diagnostic)] #[diag(interface_multiple_output_types_to_stdout)] pub struct MultipleOutputTypesToStdout; + +#[derive(Diagnostic)] +#[diag(interface_abi_required_feature)] +#[note] +#[note(interface_abi_required_feature_issue)] +pub(crate) struct AbiRequiredTargetFeature<'a> { + pub feature: &'a str, + pub enabled: &'a str, +} diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 2113345eda3ac..d9803236f85cf 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -492,6 +492,8 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se } sess.lint_store = Some(Lrc::new(lint_store)); + util::check_abi_required_features(&sess); + let compiler = Compiler { sess, codegen_backend, diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 74d02ac22276c..9495030f124b5 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -797,7 +797,6 @@ fn test_unstable_options_tracking_hash() { tracked!(function_sections, Some(false)); tracked!(human_readable_cgu_names, true); tracked!(incremental_ignore_spans, true); - tracked!(inline_in_all_cgus, Some(true)); tracked!(inline_mir, Some(true)); tracked!(inline_mir_hint_threshold, Some(123)); tracked!(inline_mir_threshold, Some(123)); diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 984b8104f539f..e900ec14fcab6 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -18,21 +18,25 @@ use rustc_session::{EarlyDiagCtxt, Session, filesearch}; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; use rustc_span::source_map::SourceMapInputs; -use rustc_span::sym; +use rustc_span::{Symbol, sym}; use rustc_target::spec::Target; use tracing::info; use crate::errors; /// Function pointer type that constructs a new CodegenBackend. -pub type MakeBackendFn = fn() -> Box; +type MakeBackendFn = fn() -> Box; /// Adds `target_feature = "..."` cfgs for a variety of platform /// specific features (SSE, NEON etc.). /// /// This is performed by checking whether a set of permitted features /// is available on the target machine, by querying the codegen backend. -pub fn add_configuration(cfg: &mut Cfg, sess: &mut Session, codegen_backend: &dyn CodegenBackend) { +pub(crate) fn add_configuration( + cfg: &mut Cfg, + sess: &mut Session, + codegen_backend: &dyn CodegenBackend, +) { let tf = sym::target_feature; let unstable_target_features = codegen_backend.target_features_cfg(sess, true); @@ -48,6 +52,34 @@ pub fn add_configuration(cfg: &mut Cfg, sess: &mut Session, codegen_backend: &dy } } +/// Ensures that all target features required by the ABI are present. +/// Must be called after `unstable_target_features` has been populated! +pub(crate) fn check_abi_required_features(sess: &Session) { + let abi_feature_constraints = sess.target.abi_required_features(); + // We check this against `unstable_target_features` as that is conveniently already + // back-translated to rustc feature names, taking into account `-Ctarget-cpu` and `-Ctarget-feature`. + // Just double-check that the features we care about are actually on our list. + for feature in + abi_feature_constraints.required.iter().chain(abi_feature_constraints.incompatible.iter()) + { + assert!( + sess.target.rust_target_features().iter().any(|(name, ..)| feature == name), + "target feature {feature} is required/incompatible for the current ABI but not a recognized feature for this target" + ); + } + + for feature in abi_feature_constraints.required { + if !sess.unstable_target_features.contains(&Symbol::intern(feature)) { + sess.dcx().emit_warn(errors::AbiRequiredTargetFeature { feature, enabled: "enabled" }); + } + } + for feature in abi_feature_constraints.incompatible { + if sess.unstable_target_features.contains(&Symbol::intern(feature)) { + sess.dcx().emit_warn(errors::AbiRequiredTargetFeature { feature, enabled: "disabled" }); + } + } +} + pub static STACK_SIZE: OnceLock = OnceLock::new(); pub const DEFAULT_STACK_SIZE: usize = 8 * 1024 * 1024; diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index c03de687a338b..e8a4e9a84c467 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -330,10 +330,12 @@ impl EarlyLintPass for UnsafeCode { if let FnKind::Fn( ctxt, _, - ast::FnSig { header: ast::FnHeader { safety: ast::Safety::Unsafe(_), .. }, .. }, _, - _, - body, + ast::Fn { + sig: ast::FnSig { header: ast::FnHeader { safety: ast::Safety::Unsafe(_), .. }, .. }, + body, + .. + }, ) = fk { let decorator = match ctxt { diff --git a/compiler/rustc_lint/src/dangling.rs b/compiler/rustc_lint/src/dangling.rs index 98b717a307068..fd6b3e90adabc 100644 --- a/compiler/rustc_lint/src/dangling.rs +++ b/compiler/rustc_lint/src/dangling.rs @@ -141,7 +141,7 @@ fn lint_expr(cx: &LateContext<'_>, expr: &Expr<'_>) { expr.hir_id, method.ident.span, DanglingPointersFromTemporaries { - callee: method.ident.name, + callee: method.ident, ty, ptr_span: method.ident.span, temporary_span: receiver.span, diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 677fc86a23519..09b0e1ed8bdba 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1150,7 +1150,7 @@ pub(crate) struct IgnoredUnlessCrateSpecified<'a> { #[help(lint_help_visit)] // FIXME: put #[primary_span] on `ptr_span` once it does not cause conflicts pub(crate) struct DanglingPointersFromTemporaries<'tcx> { - pub callee: Symbol, + pub callee: Ident, pub ty: Ty<'tcx>, #[label(lint_label_ptr)] pub ptr_span: Span, @@ -1351,7 +1351,7 @@ pub(crate) enum NonUpperCaseGlobalSub { #[diag(lint_noop_method_call)] #[note] pub(crate) struct NoopMethodCallDiag<'a> { - pub method: Symbol, + pub method: Ident, pub orig_ty: Ty<'a>, pub trait_: Symbol, #[suggestion(code = "", applicability = "machine-applicable")] diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index 1bf19047ade4a..4e9d793be5b91 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -343,5 +343,5 @@ fn path_span_without_args(path: &Path<'_>) -> Span { /// Return a "error message-able" ident for the last segment of the `Path` fn path_name_to_string(path: &Path<'_>) -> String { - path.segments.last().unwrap().ident.name.to_ident_string() + path.segments.last().unwrap().ident.to_string() } diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index fa519281be531..790ef910b041b 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -129,7 +129,7 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { _ => None, }; cx.emit_span_lint(NOOP_METHOD_CALL, span, NoopMethodCallDiag { - method: call.ident.name, + method: call.ident, orig_ty, trait_, label: span, diff --git a/compiler/rustc_lint/src/pass_by_value.rs b/compiler/rustc_lint/src/pass_by_value.rs index 244cd358e9ce5..a1d660470589e 100644 --- a/compiler/rustc_lint/src/pass_by_value.rs +++ b/compiler/rustc_lint/src/pass_by_value.rs @@ -45,7 +45,7 @@ fn path_for_pass_by_value(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> Option { - let name = cx.tcx.item_name(def_id).to_ident_string(); + let name = cx.tcx.item_ident(def_id); let path_segment = path.segments.last().unwrap(); return Some(format!("{}{}", name, gen_args(cx, path_segment))); } diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 3059adb3fda10..5696fcaed1370 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -289,25 +289,22 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults { } ty::Adt(def, _) => is_def_must_use(cx, def.did(), span), ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => { - elaborate( - cx.tcx, - cx.tcx.explicit_item_super_predicates(def).iter_identity_copied(), - ) - // We only care about self bounds for the impl-trait - .filter_only_self() - .find_map(|(pred, _span)| { - // We only look at the `DefId`, so it is safe to skip the binder here. - if let ty::ClauseKind::Trait(ref poly_trait_predicate) = - pred.kind().skip_binder() - { - let def_id = poly_trait_predicate.trait_ref.def_id; - - is_def_must_use(cx, def_id, span) - } else { - None - } - }) - .map(|inner| MustUsePath::Opaque(Box::new(inner))) + elaborate(cx.tcx, cx.tcx.explicit_item_self_bounds(def).iter_identity_copied()) + // We only care about self bounds for the impl-trait + .filter_only_self() + .find_map(|(pred, _span)| { + // We only look at the `DefId`, so it is safe to skip the binder here. + if let ty::ClauseKind::Trait(ref poly_trait_predicate) = + pred.kind().skip_binder() + { + let def_id = poly_trait_predicate.trait_ref.def_id; + + is_def_must_use(cx, def_id, span) + } else { + None + } + }) + .map(|inner| MustUsePath::Opaque(Box::new(inner))) } ty::Dynamic(binders, _, _) => binders.iter().find_map(|predicate| { if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 527f2f10205a6..da07ad8f6c074 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -241,7 +241,7 @@ impl IntoArgs for (CrateNum, SimplifiedType) { provide! { tcx, def_id, other, cdata, explicit_item_bounds => { table_defaulted_array } - explicit_item_super_predicates => { table_defaulted_array } + explicit_item_self_bounds => { table_defaulted_array } explicit_predicates_of => { table } generics_of => { table } inferred_outlives_of => { table_defaulted_array } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index c538ab99fb54b..904409dd777ea 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1554,7 +1554,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } if let DefKind::OpaqueTy = def_kind { self.encode_explicit_item_bounds(def_id); - self.encode_explicit_item_super_predicates(def_id); + self.encode_explicit_item_self_bounds(def_id); record!(self.tables.opaque_ty_origin[def_id] <- self.tcx.opaque_ty_origin(def_id)); self.encode_precise_capturing_args(def_id); if tcx.is_conditionally_const(def_id) { @@ -1667,10 +1667,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { record_defaulted_array!(self.tables.explicit_item_bounds[def_id] <- bounds); } - fn encode_explicit_item_super_predicates(&mut self, def_id: DefId) { - debug!("EncodeContext::encode_explicit_item_super_predicates({:?})", def_id); - let bounds = self.tcx.explicit_item_super_predicates(def_id).skip_binder(); - record_defaulted_array!(self.tables.explicit_item_super_predicates[def_id] <- bounds); + fn encode_explicit_item_self_bounds(&mut self, def_id: DefId) { + debug!("EncodeContext::encode_explicit_item_self_bounds({:?})", def_id); + let bounds = self.tcx.explicit_item_self_bounds(def_id).skip_binder(); + record_defaulted_array!(self.tables.explicit_item_self_bounds[def_id] <- bounds); } #[instrument(level = "debug", skip(self))] @@ -1685,7 +1685,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { AssocItemContainer::Trait => { if let ty::AssocKind::Type = item.kind { self.encode_explicit_item_bounds(def_id); - self.encode_explicit_item_super_predicates(def_id); + self.encode_explicit_item_self_bounds(def_id); if tcx.is_conditionally_const(def_id) { record_defaulted_array!(self.tables.explicit_implied_const_bounds[def_id] <- self.tcx.explicit_implied_const_bounds(def_id).skip_binder()); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 4f9cdc9a474bd..d1d356c5220a3 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -386,7 +386,7 @@ define_tables! { // corresponding DefPathHash. def_path_hashes: Table, explicit_item_bounds: Table, Span)>>, - explicit_item_super_predicates: Table, Span)>>, + explicit_item_self_bounds: Table, Span)>>, inferred_outlives_of: Table, Span)>>, explicit_super_predicates_of: Table, Span)>>, explicit_implied_predicates_of: Table, Span)>>, diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 111c3b6956a24..6fa3fa2432de5 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -8,7 +8,7 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::stable_hasher::{Hash128, HashStable, StableHasher, ToStableHashKey}; use rustc_data_structures::unord::UnordMap; use rustc_hir::ItemId; -use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdSet, LOCAL_CRATE}; use rustc_index::Idx; use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use rustc_query_system::ich::StableHashingContext; @@ -91,13 +91,8 @@ impl<'tcx> MonoItem<'tcx> { } pub fn instantiation_mode(&self, tcx: TyCtxt<'tcx>) -> InstantiationMode { - let generate_cgu_internal_copies = tcx - .sess - .opts - .unstable_opts - .inline_in_all_cgus - .unwrap_or_else(|| tcx.sess.opts.optimize != OptLevel::No) - && !tcx.sess.link_dead_code(); + let generate_cgu_internal_copies = + (tcx.sess.opts.optimize != OptLevel::No) && !tcx.sess.link_dead_code(); match *self { MonoItem::Fn(ref instance) => { @@ -121,8 +116,8 @@ impl<'tcx> MonoItem<'tcx> { } // At this point we don't have explicit linkage and we're an - // inlined function. If we're inlining into all CGUs then we'll - // be creating a local copy per CGU. + // inlined function. If this crate's build settings permit, + // we'll be creating a local copy per CGU. if generate_cgu_internal_copies { return InstantiationMode::LocalCopy; } @@ -247,6 +242,12 @@ impl ToStableHashKey> for MonoItem<'_> { } } +#[derive(Debug, HashStable, Copy, Clone)] +pub struct MonoItemPartitions<'tcx> { + pub codegen_units: &'tcx [CodegenUnit<'tcx>], + pub all_mono_items: &'tcx DefIdSet, +} + #[derive(Debug, HashStable)] pub struct CodegenUnit<'tcx> { /// A name for this CGU. Incremental compilation requires that diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 29ae2e1bd6bc1..5868b64f6b5cb 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -180,6 +180,59 @@ pub enum BorrowKind { Mut { kind: MutBorrowKind }, } +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TyEncodable, TyDecodable)] +#[derive(Hash, HashStable)] +pub enum RawPtrKind { + Mut, + Const, + /// Creates a raw pointer to a place that will only be used to access its metadata, + /// not the data behind the pointer. Note that this limitation is *not* enforced + /// by the validator. + /// + /// The borrow checker allows overlap of these raw pointers with references to the + /// data. This is sound even if the pointer is "misused" since any such use is anyway + /// unsafe. In terms of the operational semantics (i.e., Miri), this is equivalent + /// to `RawPtrKind::Mut`, but will never incur a retag. + FakeForPtrMetadata, +} + +impl From for RawPtrKind { + fn from(other: Mutability) -> Self { + match other { + Mutability::Mut => RawPtrKind::Mut, + Mutability::Not => RawPtrKind::Const, + } + } +} + +impl RawPtrKind { + pub fn is_fake(self) -> bool { + match self { + RawPtrKind::Mut | RawPtrKind::Const => false, + RawPtrKind::FakeForPtrMetadata => true, + } + } + + pub fn to_mutbl_lossy(self) -> Mutability { + match self { + RawPtrKind::Mut => Mutability::Mut, + RawPtrKind::Const => Mutability::Not, + + // We have no type corresponding to a fake borrow, so use + // `*const` as an approximation. + RawPtrKind::FakeForPtrMetadata => Mutability::Not, + } + } + + pub fn ptr_str(self) -> &'static str { + match self { + RawPtrKind::Mut => "mut", + RawPtrKind::Const => "const", + RawPtrKind::FakeForPtrMetadata => "const (fake)", + } + } +} + #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, TyEncodable, TyDecodable)] #[derive(Hash, HashStable)] pub enum MutBorrowKind { @@ -1356,7 +1409,7 @@ pub enum Rvalue<'tcx> { /// /// Like with references, the semantics of this operation are heavily dependent on the aliasing /// model. - RawPtr(Mutability, Place<'tcx>), + RawPtr(RawPtrKind, Place<'tcx>), /// Yields the length of the place, as a `usize`. /// diff --git a/compiler/rustc_middle/src/mir/tcx.rs b/compiler/rustc_middle/src/mir/tcx.rs index db77017310af3..4d11492e94d31 100644 --- a/compiler/rustc_middle/src/mir/tcx.rs +++ b/compiler/rustc_middle/src/mir/tcx.rs @@ -206,9 +206,9 @@ impl<'tcx> Rvalue<'tcx> { let place_ty = place.ty(local_decls, tcx).ty; Ty::new_ref(tcx, reg, place_ty, bk.to_mutbl_lossy()) } - Rvalue::RawPtr(mutability, ref place) => { + Rvalue::RawPtr(kind, ref place) => { let place_ty = place.ty(local_decls, tcx).ty; - Ty::new_ptr(tcx, place_ty, mutability) + Ty::new_ptr(tcx, place_ty, kind.to_mutbl_lossy()) } Rvalue::Len(..) => tcx.types.usize, Rvalue::Cast(.., ty) => ty, diff --git a/compiler/rustc_middle/src/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs index b798f0788007f..b59b9e55fe8a0 100644 --- a/compiler/rustc_middle/src/mir/type_foldable.rs +++ b/compiler/rustc_middle/src/mir/type_foldable.rs @@ -15,6 +15,7 @@ TrivialTypeTraversalImpls! { SourceScopeLocalData, UserTypeAnnotationIndex, BorrowKind, + RawPtrKind, CastKind, BasicBlock, SwitchTargets, diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 058acbd4024d1..95de08ce9c874 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -685,12 +685,15 @@ macro_rules! make_mir_visitor { Rvalue::RawPtr(m, path) => { let ctx = match m { - Mutability::Mut => PlaceContext::MutatingUse( + RawPtrKind::Mut => PlaceContext::MutatingUse( MutatingUseContext::RawBorrow ), - Mutability::Not => PlaceContext::NonMutatingUse( + RawPtrKind::Const => PlaceContext::NonMutatingUse( NonMutatingUseContext::RawBorrow ), + RawPtrKind::FakeForPtrMetadata => PlaceContext::NonMutatingUse( + NonMutatingUseContext::Inspect + ), }; self.visit_place(path, ctx, location); } diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 1676afb4b6ec4..14f871cbbdcbc 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -349,6 +349,7 @@ tcx_lifetime! { rustc_middle::mir::interpret::GlobalId, rustc_middle::mir::interpret::LitToConstInput, rustc_middle::mir::interpret::EvalStaticInitializerRawResult, + rustc_middle::mir::mono::MonoItemPartitions, rustc_middle::traits::query::MethodAutoderefStepsResult, rustc_middle::traits::query::type_op::AscribeUserType, rustc_middle::traits::query::type_op::Eq, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 17e1fe35bba05..d83bc19a6a2f5 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -23,7 +23,7 @@ use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::{DefKind, DocLinkResMap}; use rustc_hir::def_id::{ - CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId, LocalDefIdMap, LocalDefIdSet, LocalModDefId, + CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap, LocalDefIdSet, LocalModDefId, }; use rustc_hir::lang_items::{LangItem, LanguageItems}; use rustc_hir::{Crate, ItemLocalId, ItemLocalMap, TraitCandidate}; @@ -58,7 +58,7 @@ use crate::mir::interpret::{ EvalStaticInitializerRawResult, EvalToAllocationRawResult, EvalToConstValueResult, EvalToValTreeResult, GlobalId, LitToConstInput, }; -use crate::mir::mono::{CodegenUnit, CollectionMode, MonoItem}; +use crate::mir::mono::{CodegenUnit, CollectionMode, MonoItem, MonoItemPartitions}; use crate::query::erase::{Erase, erase, restore}; use crate::query::plumbing::{ CyclePlaceholder, DynamicQuery, query_ensure, query_ensure_error_guaranteed, query_get_at, @@ -393,7 +393,7 @@ rustc_queries! { /// like closure signature deduction. /// /// [explicit item bounds]: Self::explicit_item_bounds - query explicit_item_super_predicates(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { + query explicit_item_self_bounds(key: DefId) -> ty::EarlyBinder<'tcx, &'tcx [(ty::Clause<'tcx>, Span)]> { desc { |tcx| "finding item bounds for `{}`", tcx.def_path_str(key) } cache_on_disk_if { key.is_local() } separate_provide_extern @@ -427,11 +427,11 @@ rustc_queries! { desc { |tcx| "elaborating item bounds for `{}`", tcx.def_path_str(key) } } - query item_super_predicates(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { + query item_self_bounds(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { desc { |tcx| "elaborating item assumptions for `{}`", tcx.def_path_str(key) } } - query item_non_self_assumptions(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { + query item_non_self_bounds(key: DefId) -> ty::EarlyBinder<'tcx, ty::Clauses<'tcx>> { desc { |tcx| "elaborating item assumptions for `{}`", tcx.def_path_str(key) } } @@ -2166,7 +2166,7 @@ rustc_queries! { separate_provide_extern } - query collect_and_partition_mono_items(_: ()) -> (&'tcx DefIdSet, &'tcx [CodegenUnit<'tcx>]) { + query collect_and_partition_mono_items(_: ()) -> MonoItemPartitions<'tcx> { eval_always desc { "collect_and_partition_mono_items" } } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 55d78e083e079..8a9110f842a94 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -194,6 +194,9 @@ pub enum ObligationCauseCode<'tcx> { /// A slice or array is WF only if `T: Sized`. SliceOrArrayElem, + /// An array `[T; N]` can only be indexed (and is only well-formed if) `N` has type usize. + ArrayLen(Ty<'tcx>), + /// A tuple is WF only if its middle elements are `Sized`. TupleElem, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index d1079743004b1..0c22c056dab5f 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -345,6 +345,20 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.item_bounds(def_id).map_bound(IntoIterator::into_iter) } + fn item_self_bounds( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { + self.item_self_bounds(def_id).map_bound(IntoIterator::into_iter) + } + + fn item_non_self_bounds( + self, + def_id: DefId, + ) -> ty::EarlyBinder<'tcx, impl IntoIterator>> { + self.item_non_self_bounds(def_id).map_bound(IntoIterator::into_iter) + } + fn predicates_of( self, def_id: DefId, @@ -1349,6 +1363,33 @@ pub struct GlobalCtxt<'tcx> { /// Stores memory for globals (statics/consts). pub(crate) alloc_map: Lock>, + + current_gcx: CurrentGcx, +} + +impl<'tcx> GlobalCtxt<'tcx> { + /// Installs `self` in a `TyCtxt` and `ImplicitCtxt` for the duration of + /// `f`. + pub fn enter(&'tcx self, f: F) -> R + where + F: FnOnce(TyCtxt<'tcx>) -> R, + { + let icx = tls::ImplicitCtxt::new(self); + + // Reset `current_gcx` to `None` when we exit. + let _on_drop = defer(move || { + *self.current_gcx.value.write() = None; + }); + + // Set this `GlobalCtxt` as the current one. + { + let mut guard = self.current_gcx.value.write(); + assert!(guard.is_none(), "no `GlobalCtxt` is currently set"); + *guard = Some(self as *const _ as *const ()); + } + + tls::enter_context(&icx, || f(icx.tcx)) + } } /// This is used to get a reference to a `GlobalCtxt` if one is available. @@ -1539,23 +1580,11 @@ impl<'tcx> TyCtxt<'tcx> { canonical_param_env_cache: Default::default(), data_layout, alloc_map: Lock::new(interpret::AllocMap::new()), + current_gcx, }); - let icx = tls::ImplicitCtxt::new(&gcx); - - // Reset `current_gcx` to `None` when we exit. - let _on_drop = defer(|| { - *current_gcx.value.write() = None; - }); - - // Set this `GlobalCtxt` as the current one. - { - let mut guard = current_gcx.value.write(); - assert!(guard.is_none(), "no `GlobalCtxt` is currently set"); - *guard = Some(&gcx as *const _ as *const ()); - } - - tls::enter_context(&icx, || f(icx.tcx)) + // This is a separate function to work around a crash with parallel rustc (#135870) + gcx.enter(f) } /// Obtain all lang items of this crate and all dependencies (recursively) @@ -2562,7 +2591,7 @@ impl<'tcx> TyCtxt<'tcx> { let ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) = ty.kind() else { return false }; let future_trait = self.require_lang_item(LangItem::Future, None); - self.explicit_item_super_predicates(def_id).skip_binder().iter().any(|&(predicate, _)| { + self.explicit_item_self_bounds(def_id).skip_binder().iter().any(|&(predicate, _)| { let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() else { return false; }; diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index ca70ae794c530..8cd632790a8ae 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1596,6 +1596,15 @@ impl<'tcx> TyCtxt<'tcx> { Some(Ident::new(def, span)) } + /// Look up the name and span of a definition. + /// + /// See [`item_name`][Self::item_name] for more information. + pub fn item_ident(self, def_id: DefId) -> Ident { + self.opt_item_ident(def_id).unwrap_or_else(|| { + bug!("item_ident: no name for {:?}", self.def_path(def_id)); + }) + } + pub fn opt_associated_item(self, def_id: DefId) -> Option { if let DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy = self.def_kind(def_id) { Some(self.associated_item(def_id)) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index ac900edefe12f..027a4315b4bf3 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -1740,6 +1740,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write { " as ", )?; } + ty::Pat(base_ty, pat) => { + self.pretty_print_const_scalar_int(int, *base_ty, print_ty)?; + p!(write(" is {pat:?}")); + } // Nontrivial types with scalar bit representation _ => { let print = |this: &mut Self| { diff --git a/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs b/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs index 21c605f8296d1..cbc02097d827d 100644 --- a/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs +++ b/compiler/rustc_middle/src/ty/return_position_impl_trait_in_trait.rs @@ -64,7 +64,7 @@ impl<'tcx> TyCtxt<'tcx> { args: ty::GenericArgsRef<'tcx>, ) -> &'tcx ty::List> { let mut bounds: Vec<_> = self - .item_super_predicates(def_id) + .item_self_bounds(def_id) .iter_instantiated(self, args) .filter_map(|clause| { clause diff --git a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs index 59f440432ebc1..eab414e150fa3 100644 --- a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs @@ -253,7 +253,7 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { Rvalue::Ref(self.tcx.lifetimes.re_erased, *borrow_kind, self.parse_place(*arg)?) ), ExprKind::RawBorrow { mutability, arg } => Ok( - Rvalue::RawPtr(*mutability, self.parse_place(*arg)?) + Rvalue::RawPtr((*mutability).into(), self.parse_place(*arg)?) ), ExprKind::Binary { op, lhs, rhs } => Ok( Rvalue::BinaryOp(*op, Box::new((self.parse_operand(*lhs)?, self.parse_operand(*rhs)?))) diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index b1851e79d5c65..0086775e9f46d 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -630,6 +630,69 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.and(base_place.index(idx)) } + /// Given a place that's either an array or a slice, returns an operand + /// with the length of the array/slice. + /// + /// For arrays it'll be `Operand::Constant` with the actual length; + /// For slices it'll be `Operand::Move` of a local using `PtrMetadata`. + fn len_of_slice_or_array( + &mut self, + block: BasicBlock, + place: Place<'tcx>, + span: Span, + source_info: SourceInfo, + ) -> Operand<'tcx> { + let place_ty = place.ty(&self.local_decls, self.tcx).ty; + match place_ty.kind() { + ty::Array(_elem_ty, len_const) => { + // We know how long an array is, so just use that as a constant + // directly -- no locals needed. We do need one statement so + // that borrow- and initialization-checking consider it used, + // though. FIXME: Do we really *need* to count this as a use? + // Could partial array tracking work off something else instead? + self.cfg.push_fake_read(block, source_info, FakeReadCause::ForIndex, place); + let const_ = Const::Ty(self.tcx.types.usize, *len_const); + Operand::Constant(Box::new(ConstOperand { span, user_ty: None, const_ })) + } + ty::Slice(_elem_ty) => { + let ptr_or_ref = if let [PlaceElem::Deref] = place.projection[..] + && let local_ty = self.local_decls[place.local].ty + && local_ty.is_trivially_pure_clone_copy() + { + // It's extremely common that we have something that can be + // directly passed to `PtrMetadata`, so avoid an unnecessary + // temporary and statement in those cases. Note that we can + // only do that for `Copy` types -- not `&mut [_]` -- because + // the MIR we're building here needs to pass NLL later. + Operand::Copy(Place::from(place.local)) + } else { + let ptr_ty = Ty::new_imm_ptr(self.tcx, place_ty); + let slice_ptr = self.temp(ptr_ty, span); + self.cfg.push_assign( + block, + source_info, + slice_ptr, + Rvalue::RawPtr(RawPtrKind::FakeForPtrMetadata, place), + ); + Operand::Move(slice_ptr) + }; + + let len = self.temp(self.tcx.types.usize, span); + self.cfg.push_assign( + block, + source_info, + len, + Rvalue::UnaryOp(UnOp::PtrMetadata, ptr_or_ref), + ); + + Operand::Move(len) + } + _ => { + span_bug!(span, "len called on place of type {place_ty:?}") + } + } + } + fn bounds_check( &mut self, block: BasicBlock, @@ -638,25 +701,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { expr_span: Span, source_info: SourceInfo, ) -> BasicBlock { - let usize_ty = self.tcx.types.usize; - let bool_ty = self.tcx.types.bool; - // bounds check: - let len = self.temp(usize_ty, expr_span); - let lt = self.temp(bool_ty, expr_span); + let slice = slice.to_place(self); // len = len(slice) - self.cfg.push_assign(block, source_info, len, Rvalue::Len(slice.to_place(self))); + let len = self.len_of_slice_or_array(block, slice, expr_span, source_info); + // lt = idx < len + let bool_ty = self.tcx.types.bool; + let lt = self.temp(bool_ty, expr_span); self.cfg.push_assign( block, source_info, lt, Rvalue::BinaryOp( BinOp::Lt, - Box::new((Operand::Copy(Place::from(index)), Operand::Copy(len))), + Box::new((Operand::Copy(Place::from(index)), len.to_copy())), ), ); - let msg = BoundsCheck { len: Operand::Move(len), index: Operand::Copy(Place::from(index)) }; + let msg = BoundsCheck { len, index: Operand::Copy(Place::from(index)) }; + // assert!(lt, "...") self.assert(block, Operand::Move(lt), true, msg, expr_span) } diff --git a/compiler/rustc_mir_build/src/builder/expr/into.rs b/compiler/rustc_mir_build/src/builder/expr/into.rs index 88f63d4e22cbd..928156572d50a 100644 --- a/compiler/rustc_mir_build/src/builder/expr/into.rs +++ b/compiler/rustc_mir_build/src/builder/expr/into.rs @@ -303,7 +303,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { hir::Mutability::Not => this.as_read_only_place(block, arg), hir::Mutability::Mut => this.as_place(block, arg), }; - let address_of = Rvalue::RawPtr(mutability, unpack!(block = place)); + let address_of = Rvalue::RawPtr(mutability.into(), unpack!(block = place)); this.cfg.push_assign(block, source_info, destination, address_of); block.unit() } diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index 8cca84d7fcc64..afe6b4475be3c 100644 --- a/compiler/rustc_mir_build/src/builder/matches/test.rs +++ b/compiler/rustc_mir_build/src/builder/matches/test.rs @@ -141,43 +141,49 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.cfg.terminate(block, self.source_info(match_start_span), terminator); } - TestKind::Eq { value, ty } => { + TestKind::Eq { value, mut ty } => { let tcx = self.tcx; let success_block = target_block(TestBranch::Success); let fail_block = target_block(TestBranch::Failure); - if let ty::Adt(def, _) = ty.kind() - && tcx.is_lang_item(def.did(), LangItem::String) - { - if !tcx.features().string_deref_patterns() { - span_bug!( + + let expect_ty = value.ty(); + let expect = self.literal_operand(test.span, value); + + let mut place = place; + let mut block = block; + match ty.kind() { + ty::Adt(def, _) if tcx.is_lang_item(def.did(), LangItem::String) => { + if !tcx.features().string_deref_patterns() { + span_bug!( + test.span, + "matching on `String` went through without enabling string_deref_patterns" + ); + } + let re_erased = tcx.lifetimes.re_erased; + let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_); + let ref_str = self.temp(ref_str_ty, test.span); + let eq_block = self.cfg.start_new_block(); + // `let ref_str: &str = ::deref(&place);` + self.call_deref( + block, + eq_block, + place, + Mutability::Not, + ty, + ref_str, test.span, - "matching on `String` went through without enabling string_deref_patterns" ); + // Since we generated a `ref_str = ::deref(&place) -> eq_block` terminator, + // we need to add all further statements to `eq_block`. + // Similarly, the normal test code should be generated for the `&str`, instead of the `String`. + block = eq_block; + place = ref_str; + ty = ref_str_ty; } - let re_erased = tcx.lifetimes.re_erased; - let ref_str_ty = Ty::new_imm_ref(tcx, re_erased, tcx.types.str_); - let ref_str = self.temp(ref_str_ty, test.span); - let eq_block = self.cfg.start_new_block(); - // `let ref_str: &str = ::deref(&place);` - self.call_deref( - block, - eq_block, - place, - Mutability::Not, - ty, - ref_str, - test.span, - ); - self.non_scalar_compare( - eq_block, - success_block, - fail_block, - source_info, - value, - ref_str, - ref_str_ty, - ); - } else if !ty.is_scalar() { + _ => {} + } + + if !ty.is_scalar() { // Use `PartialEq::eq` instead of `BinOp::Eq` // (the binop can only handle primitives) self.non_scalar_compare( @@ -185,14 +191,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { success_block, fail_block, source_info, - value, - place, + expect, + expect_ty, + Operand::Copy(place), ty, ); } else { - assert_eq!(value.ty(), ty); - let expect = self.literal_operand(test.span, value); - let val = Operand::Copy(place); + assert_eq!(expect_ty, ty); self.compare( block, success_block, @@ -200,7 +205,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { source_info, BinOp::Eq, expect, - val, + Operand::Copy(place), ); } } @@ -371,12 +376,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { success_block: BasicBlock, fail_block: BasicBlock, source_info: SourceInfo, - value: Const<'tcx>, - mut val: Place<'tcx>, + mut expect: Operand<'tcx>, + expect_ty: Ty<'tcx>, + mut val: Operand<'tcx>, mut ty: Ty<'tcx>, ) { - let mut expect = self.literal_operand(source_info.span, value); - // If we're using `b"..."` as a pattern, we need to insert an // unsizing coercion, as the byte string has the type `&[u8; N]`. // @@ -391,7 +395,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { _ => None, }; let opt_ref_ty = unsize(ty); - let opt_ref_test_ty = unsize(value.ty()); + let opt_ref_test_ty = unsize(expect_ty); match (opt_ref_ty, opt_ref_test_ty) { // nothing to do, neither is an array (None, None) => {} @@ -410,11 +414,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { PointerCoercion::Unsize, CoercionSource::Implicit, ), - Operand::Copy(val), + val, ty, ), ); - val = temp; + val = Operand::Copy(temp); } if opt_ref_test_ty.is_some() { let slice = self.temp(ty, source_info.span); @@ -470,11 +474,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { const_: method, })), - args: [Spanned { node: Operand::Copy(val), span: DUMMY_SP }, Spanned { - node: expect, - span: DUMMY_SP, - }] - .into(), + args: [Spanned { node: val, span: DUMMY_SP }, Spanned { node: expect, span: DUMMY_SP }] + .into(), destination: eq_result, target: Some(eq_block), unwind: UnwindAction::Continue, diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index c3bf5868eecde..1f87bf0dbbbd6 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -7,7 +7,7 @@ use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{self, Ty}; use rustc_pattern_analysis::errors::Uncovered; use rustc_pattern_analysis::rustc::RustcPatCtxt; -use rustc_span::{Span, Symbol}; +use rustc_span::{Ident, Span, Symbol}; use crate::fluent_generated as fluent; @@ -753,7 +753,7 @@ pub(crate) struct BindingsWithVariantName { #[suggestion(code = "{ty_path}::{name}", applicability = "machine-applicable")] pub(crate) suggestion: Option, pub(crate) ty_path: String, - pub(crate) name: Symbol, + pub(crate) name: Ident, } #[derive(LintDiagnostic)] @@ -797,7 +797,7 @@ pub(crate) struct BorrowOfMovedValue { pub(crate) binding_span: Span, #[label(mir_build_value_borrowed_label)] pub(crate) conflicts_ref: Vec, - pub(crate) name: Symbol, + pub(crate) name: Ident, pub(crate) ty: String, #[suggestion(code = "ref ", applicability = "machine-applicable")] pub(crate) suggest_borrowing: Option, diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index d8b04398d9a51..e0a1117f905c6 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -25,7 +25,7 @@ use rustc_session::lint::builtin::{ }; use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::hygiene::DesugaringKind; -use rustc_span::{Span, sym}; +use rustc_span::{Ident, Span, sym}; use rustc_trait_selection::infer::InferCtxtExt; use tracing::instrument; @@ -800,7 +800,7 @@ fn check_borrow_conflicts_in_at_patterns<'tcx>(cx: &MatchVisitor<'_, 'tcx>, pat: sess.dcx().emit_err(BorrowOfMovedValue { binding_span: pat.span, conflicts_ref, - name, + name: Ident::new(name, pat.span), ty, suggest_borrowing: Some(pat.span.shrink_to_lo()), has_path: path.is_some(), @@ -908,7 +908,7 @@ fn check_for_bindings_named_same_as_variants( None }, ty_path, - name, + name: Ident::new(name, pat.span), }, ) } diff --git a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs index f8a8467494753..74b0e84068c67 100644 --- a/compiler/rustc_mir_dataflow/src/elaborate_drops.rs +++ b/compiler/rustc_mir_dataflow/src/elaborate_drops.rs @@ -700,7 +700,7 @@ where statements: vec![ self.assign( ptr, - Rvalue::RawPtr(Mutability::Mut, tcx.mk_place_index(self.place, cur)), + Rvalue::RawPtr(RawPtrKind::Mut, tcx.mk_place_index(self.place, cur)), ), self.assign( cur.into(), @@ -816,7 +816,7 @@ where let mut delegate_block = BasicBlockData { statements: vec![ - self.assign(Place::from(array_ptr), Rvalue::RawPtr(Mutability::Mut, self.place)), + self.assign(Place::from(array_ptr), Rvalue::RawPtr(RawPtrKind::Mut, self.place)), self.assign( Place::from(slice_ptr), Rvalue::Cast( diff --git a/compiler/rustc_mir_transform/src/coverage/counters.rs b/compiler/rustc_mir_transform/src/coverage/counters.rs index 50ebde3292ea7..adb99a75a9e47 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters.rs @@ -10,14 +10,12 @@ use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::coverage::{CounterId, CovTerm, Expression, ExpressionId, Op}; use crate::coverage::counters::balanced_flow::BalancedFlowGraph; -use crate::coverage::counters::iter_nodes::IterNodes; use crate::coverage::counters::node_flow::{ CounterTerm, NodeCounters, make_node_counters, node_flow_data_for_balanced_graph, }; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; mod balanced_flow; -mod iter_nodes; mod node_flow; mod union_find; diff --git a/compiler/rustc_mir_transform/src/coverage/counters/balanced_flow.rs b/compiler/rustc_mir_transform/src/coverage/counters/balanced_flow.rs index c108f96a564cf..4c20722a04347 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters/balanced_flow.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters/balanced_flow.rs @@ -20,8 +20,6 @@ use rustc_data_structures::graph::reversed::ReversedGraph; use rustc_index::Idx; use rustc_index::bit_set::DenseBitSet; -use crate::coverage::counters::iter_nodes::IterNodes; - /// A view of an underlying graph that has been augmented to have “balanced flow”. /// This means that the flow (execution count) of each node is equal to the /// sum of its in-edge flows, and also equal to the sum of its out-edge flows. diff --git a/compiler/rustc_mir_transform/src/coverage/counters/iter_nodes.rs b/compiler/rustc_mir_transform/src/coverage/counters/iter_nodes.rs deleted file mode 100644 index 9d87f7af1b04e..0000000000000 --- a/compiler/rustc_mir_transform/src/coverage/counters/iter_nodes.rs +++ /dev/null @@ -1,16 +0,0 @@ -use rustc_data_structures::graph; -use rustc_index::Idx; - -pub(crate) trait IterNodes: graph::DirectedGraph { - /// Iterates over all nodes of a graph in ascending numeric order. - /// Assumes that nodes are densely numbered, i.e. every index in - /// `0..num_nodes` is a valid node. - /// - /// FIXME: Can this just be part of [`graph::DirectedGraph`]? - fn iter_nodes( - &self, - ) -> impl Iterator + DoubleEndedIterator + ExactSizeIterator { - (0..self.num_nodes()).map(::new) - } -} -impl IterNodes for G {} diff --git a/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs b/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs index 3647c88993746..9d80b3af42d8b 100644 --- a/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs +++ b/compiler/rustc_mir_transform/src/coverage/counters/node_flow.rs @@ -11,7 +11,6 @@ use rustc_index::bit_set::DenseBitSet; use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_middle::mir::coverage::Op; -use crate::coverage::counters::iter_nodes::IterNodes; use crate::coverage::counters::union_find::UnionFind; #[cfg(test)] diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index a2fd46043ca0f..29698b0c2e445 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -5,7 +5,7 @@ use rustc_middle::mir::AssertKind; use rustc_middle::ty::TyCtxt; use rustc_session::lint::{self, Lint}; use rustc_span::def_id::DefId; -use rustc_span::{Span, Symbol}; +use rustc_span::{Ident, Span, Symbol}; use crate::fluent_generated as fluent; @@ -114,7 +114,7 @@ pub(crate) struct FnItemRef { #[suggestion(code = "{sugg}", applicability = "unspecified")] pub span: Span, pub sugg: String, - pub ident: String, + pub ident: Ident, } #[derive(Diagnostic)] diff --git a/compiler/rustc_mir_transform/src/function_item_references.rs b/compiler/rustc_mir_transform/src/function_item_references.rs index fb21bf9977f15..7e88925b2e1b6 100644 --- a/compiler/rustc_mir_transform/src/function_item_references.rs +++ b/compiler/rustc_mir_transform/src/function_item_references.rs @@ -168,7 +168,7 @@ impl<'tcx> FunctionItemRefChecker<'_, 'tcx> { s } }; - let ident = self.tcx.item_name(fn_id).to_ident_string(); + let ident = self.tcx.item_ident(fn_id); let ty_params = fn_args.types().map(|ty| format!("{ty}")); let const_params = fn_args.consts().map(|c| format!("{c}")); let params = ty_params.chain(const_params).join(", "); @@ -177,7 +177,7 @@ impl<'tcx> FunctionItemRefChecker<'_, 'tcx> { let ret = if fn_sig.output().skip_binder().is_unit() { "" } else { " -> _" }; let sugg = format!( "{} as {}{}fn({}{}){}", - if params.is_empty() { ident.clone() } else { format!("{ident}::<{params}>") }, + if params.is_empty() { ident.to_string() } else { format!("{ident}::<{params}>") }, unsafety, abi, vec!["_"; num_args].join(", "), diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 1c2c82d4cd57f..16e15fa12e07f 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -192,7 +192,7 @@ enum AggregateTy<'tcx> { #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] enum AddressKind { Ref(BorrowKind), - Address(Mutability), + Address(RawPtrKind), } #[derive(Debug, PartialEq, Eq, Hash)] @@ -504,7 +504,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> { mplace.layout.ty, bk.to_mutbl_lossy(), ), - AddressKind::Address(mutbl) => Ty::new_ptr(self.tcx, mplace.layout.ty, mutbl), + AddressKind::Address(mutbl) => { + Ty::new_ptr(self.tcx, mplace.layout.ty, mutbl.to_mutbl_lossy()) + } }; let layout = self.ecx.layout_of(ty).ok()?; ImmTy::from_immediate(pointer, layout).into() diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index 4b9ebd40b8574..3dc4edaaa5ae4 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -46,7 +46,6 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify { } ctx.simplify_bool_cmp(rvalue); ctx.simplify_ref_deref(rvalue); - ctx.simplify_len(rvalue); ctx.simplify_ptr_aggregate(rvalue); ctx.simplify_cast(rvalue); ctx.simplify_repeated_aggregate(rvalue); @@ -166,18 +165,6 @@ impl<'tcx> InstSimplifyContext<'_, 'tcx> { } } - /// Transform `Len([_; N])` ==> `N`. - fn simplify_len(&self, rvalue: &mut Rvalue<'tcx>) { - if let Rvalue::Len(ref place) = *rvalue { - let place_ty = place.ty(self.local_decls, self.tcx).ty; - if let ty::Array(_, len) = *place_ty.kind() { - let const_ = Const::Ty(self.tcx.types.usize, len); - let constant = ConstOperand { span: DUMMY_SP, const_, user_ty: None }; - *rvalue = Rvalue::Use(Operand::Constant(Box::new(constant))); - } - } - } - /// Transform `Aggregate(RawPtr, [p, ()])` ==> `Cast(PtrToPtr, p)`. fn simplify_ptr_aggregate(&self, rvalue: &mut Rvalue<'tcx>) { if let Rvalue::Aggregate(box AggregateKind::RawPtr(pointee_ty, mutability), fields) = rvalue diff --git a/compiler/rustc_mir_transform/src/large_enums.rs b/compiler/rustc_mir_transform/src/large_enums.rs index e201763468b31..1e546bfbeb303 100644 --- a/compiler/rustc_mir_transform/src/large_enums.rs +++ b/compiler/rustc_mir_transform/src/large_enums.rs @@ -125,7 +125,7 @@ impl<'tcx> crate::MirPass<'tcx> for EnumSizeOpt { source_info, kind: StatementKind::Assign(Box::new(( dst, - Rvalue::RawPtr(Mutability::Mut, *lhs), + Rvalue::RawPtr(RawPtrKind::Mut, *lhs), ))), }; @@ -146,7 +146,7 @@ impl<'tcx> crate::MirPass<'tcx> for EnumSizeOpt { source_info, kind: StatementKind::Assign(Box::new(( src, - Rvalue::RawPtr(Mutability::Not, *rhs), + Rvalue::RawPtr(RawPtrKind::Const, *rhs), ))), }; diff --git a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs index f01bab75c4a1d..1d53440cf0bad 100644 --- a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs +++ b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs @@ -2,17 +2,11 @@ use std::iter; use itertools::Itertools; use rustc_abi::{FieldIdx, VariantIdx}; -use rustc_ast::Mutability; use rustc_const_eval::interpret; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_index::{Idx, IndexVec}; -use rustc_middle::mir::{ - BasicBlock, BasicBlockData, Body, CallSource, CastKind, CoercionSource, Const, ConstOperand, - ConstValue, Local, LocalDecl, MirSource, Operand, Place, PlaceElem, RETURN_PLACE, Rvalue, - SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, UnwindAction, - UnwindTerminateReason, -}; +use rustc_middle::mir::*; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::util::{AsyncDropGlueMorphology, Discr}; use rustc_middle::ty::{self, Ty, TyCtxt}; @@ -345,7 +339,7 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> { .tcx .mk_place_elems(&[PlaceElem::Deref, PlaceElem::Field(field, field_ty)]), }; - self.put_temp_rvalue(Rvalue::RawPtr(Mutability::Mut, place)) + self.put_temp_rvalue(Rvalue::RawPtr(RawPtrKind::Mut, place)) } /// If given Self is an enum puts `to_drop: *mut FieldTy` on top of @@ -365,7 +359,7 @@ impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> { PlaceElem::Field(field, field_ty), ]), }; - self.put_temp_rvalue(Rvalue::RawPtr(Mutability::Mut, place)) + self.put_temp_rvalue(Rvalue::RawPtr(RawPtrKind::Mut, place)) } /// If given Self is an enum puts `to_drop: *mut FieldTy` on top of diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 026923ad786bb..5881264cba52e 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -1128,14 +1128,6 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ); } UnOp::PtrMetadata => { - if !matches!(self.body.phase, MirPhase::Runtime(_)) { - // It would probably be fine to support this in earlier phases, but at - // the time of writing it's only ever introduced from intrinsic - // lowering or other runtime-phase optimization passes, so earlier - // things can just `bug!` on it. - self.fail(location, "PtrMetadata should be in runtime MIR only"); - } - check_kinds!( a, "Cannot PtrMetadata non-pointer non-reference type {:?}", diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index bb603df112942..d53848f7461fd 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1454,11 +1454,14 @@ impl<'v> RootCollector<'_, 'v> { self.output.push(dummy_spanned(MonoItem::Static(def_id))); } DefKind::Const => { - // const items only generate mono items if they are - // actually used somewhere. Just declaring them is insufficient. + // Const items only generate mono items if they are actually used somewhere. + // Just declaring them is insufficient. - // but even just declaring them must collect the items they refer to - if let Ok(val) = self.tcx.const_eval_poly(id.owner_id.to_def_id()) { + // But even just declaring them must collect the items they refer to + // unless their generics require monomorphization. + if !self.tcx.generics_of(id.owner_id).requires_monomorphization(self.tcx) + && let Ok(val) = self.tcx.const_eval_poly(id.owner_id.to_def_id()) + { collect_const_value(self.tcx, val, self.output); } } diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 7b17966343084..e08c348a64d74 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -110,7 +110,7 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel}; use rustc_middle::mir::mono::{ CodegenUnit, CodegenUnitNameBuilder, InstantiationMode, Linkage, MonoItem, MonoItemData, - Visibility, + MonoItemPartitions, Visibility, }; use rustc_middle::ty::print::{characteristic_def_id_of_type, with_no_trimmed_paths}; use rustc_middle::ty::{self, InstanceKind, TyCtxt}; @@ -1114,7 +1114,7 @@ where } } -fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[CodegenUnit<'_>]) { +fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> MonoItemPartitions<'_> { let collection_strategy = match tcx.sess.opts.unstable_opts.print_mono_items { Some(ref s) => { let mode = s.to_lowercase(); @@ -1236,7 +1236,7 @@ fn collect_and_partition_mono_items(tcx: TyCtxt<'_>, (): ()) -> (&DefIdSet, &[Co } } - (tcx.arena.alloc(mono_items), codegen_units) + MonoItemPartitions { all_mono_items: tcx.arena.alloc(mono_items), codegen_units } } /// Outputs stats about instantiation counts and estimated size, per `MonoItem`'s @@ -1319,14 +1319,13 @@ fn dump_mono_items_stats<'tcx>( pub(crate) fn provide(providers: &mut Providers) { providers.collect_and_partition_mono_items = collect_and_partition_mono_items; - providers.is_codegened_item = |tcx, def_id| { - let (all_mono_items, _) = tcx.collect_and_partition_mono_items(()); - all_mono_items.contains(&def_id) - }; + providers.is_codegened_item = + |tcx, def_id| tcx.collect_and_partition_mono_items(()).all_mono_items.contains(&def_id); providers.codegen_unit = |tcx, name| { - let (_, all) = tcx.collect_and_partition_mono_items(()); - all.iter() + tcx.collect_and_partition_mono_items(()) + .codegen_units + .iter() .find(|cgu| cgu.name() == name) .unwrap_or_else(|| panic!("failed to find cgu with name {name:?}")) }; diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 63432dc199b98..d0b01b14d6358 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -19,6 +19,11 @@ use crate::solve::{ MaybeCause, NoSolution, QueryResult, }; +enum AliasBoundKind { + SelfBounds, + NonSelfBounds, +} + /// A candidate is a possible way to prove a goal. /// /// It consists of both the `source`, which describes how that goal would be proven, @@ -510,7 +515,12 @@ where candidates: &mut Vec>, ) { let () = self.probe(|_| ProbeKind::NormalizedSelfTyAssembly).enter(|ecx| { - ecx.assemble_alias_bound_candidates_recur(goal.predicate.self_ty(), goal, candidates); + ecx.assemble_alias_bound_candidates_recur( + goal.predicate.self_ty(), + goal, + candidates, + AliasBoundKind::SelfBounds, + ); }); } @@ -528,6 +538,7 @@ where self_ty: I::Ty, goal: Goal, candidates: &mut Vec>, + consider_self_bounds: AliasBoundKind, ) { let (kind, alias_ty) = match self_ty.kind() { ty::Bool @@ -580,16 +591,37 @@ where } }; - for assumption in - self.cx().item_bounds(alias_ty.def_id).iter_instantiated(self.cx(), alias_ty.args) - { - candidates.extend(G::probe_and_consider_implied_clause( - self, - CandidateSource::AliasBound, - goal, - assumption, - [], - )); + match consider_self_bounds { + AliasBoundKind::SelfBounds => { + for assumption in self + .cx() + .item_self_bounds(alias_ty.def_id) + .iter_instantiated(self.cx(), alias_ty.args) + { + candidates.extend(G::probe_and_consider_implied_clause( + self, + CandidateSource::AliasBound, + goal, + assumption, + [], + )); + } + } + AliasBoundKind::NonSelfBounds => { + for assumption in self + .cx() + .item_non_self_bounds(alias_ty.def_id) + .iter_instantiated(self.cx(), alias_ty.args) + { + candidates.extend(G::probe_and_consider_implied_clause( + self, + CandidateSource::AliasBound, + goal, + assumption, + [], + )); + } + } } candidates.extend(G::consider_additional_alias_assumptions(self, goal, alias_ty)); @@ -600,9 +632,12 @@ where // Recurse on the self type of the projection. match self.structurally_normalize_ty(goal.param_env, alias_ty.self_ty()) { - Ok(next_self_ty) => { - self.assemble_alias_bound_candidates_recur(next_self_ty, goal, candidates) - } + Ok(next_self_ty) => self.assemble_alias_bound_candidates_recur( + next_self_ty, + goal, + candidates, + AliasBoundKind::NonSelfBounds, + ), Err(NoSolution) => {} } } diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index f78d9dc2bfc28..2373ab67d42ed 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2830,9 +2830,10 @@ pub(crate) struct DynAfterMut { pub(crate) struct FnPointerCannotBeConst { #[primary_span] pub span: Span, - #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")] #[label] pub qualifier: Span, + #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")] + pub suggestion: Span, } #[derive(Diagnostic)] @@ -2840,9 +2841,10 @@ pub(crate) struct FnPointerCannotBeConst { pub(crate) struct FnPointerCannotBeAsync { #[primary_span] pub span: Span, - #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")] #[label] pub qualifier: Span, + #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")] + pub suggestion: Span, } #[derive(Diagnostic)] @@ -3233,7 +3235,7 @@ pub(crate) struct MalformedCfgAttr { pub(crate) struct UnknownBuiltinConstruct { #[primary_span] pub span: Span, - pub name: Symbol, + pub name: Ident, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 5cd02128287e2..a5b73ce4098ed 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1958,7 +1958,7 @@ impl<'a> Parser<'a> { } else { let err = self.dcx().create_err(errors::UnknownBuiltinConstruct { span: lo.to(ident.span), - name: ident.name, + name: ident, }); return Err(err); }; diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 6497d19a173ca..dc5919b3630ce 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -609,16 +609,58 @@ impl<'a> Parser<'a> { let span_start = self.token.span; let ast::FnHeader { ext, safety, constness, coroutine_kind } = self.parse_fn_front_matter(&inherited_vis, Case::Sensitive)?; + let fn_start_lo = self.prev_token.span.lo(); if self.may_recover() && self.token == TokenKind::Lt { self.recover_fn_ptr_with_generics(lo, &mut params, param_insertion_point)?; } let decl = self.parse_fn_decl(|_| false, AllowPlus::No, recover_return_sign)?; let whole_span = lo.to(self.prev_token.span); - if let ast::Const::Yes(span) = constness { - self.dcx().emit_err(FnPointerCannotBeConst { span: whole_span, qualifier: span }); + + // Order/parsing of "front matter" follows: + // ` fn()` + // ^ ^ ^ ^ ^ + // | | | | fn_start_lo + // | | | ext_sp.lo + // | | safety_sp.lo + // | coroutine_sp.lo + // const_sp.lo + if let ast::Const::Yes(const_span) = constness { + let next_token_lo = if let Some( + ast::CoroutineKind::Async { span, .. } + | ast::CoroutineKind::Gen { span, .. } + | ast::CoroutineKind::AsyncGen { span, .. }, + ) = coroutine_kind + { + span.lo() + } else if let ast::Safety::Unsafe(span) | ast::Safety::Safe(span) = safety { + span.lo() + } else if let ast::Extern::Implicit(span) | ast::Extern::Explicit(_, span) = ext { + span.lo() + } else { + fn_start_lo + }; + let sugg_span = const_span.with_hi(next_token_lo); + self.dcx().emit_err(FnPointerCannotBeConst { + span: whole_span, + qualifier: const_span, + suggestion: sugg_span, + }); } - if let Some(ast::CoroutineKind::Async { span, .. }) = coroutine_kind { - self.dcx().emit_err(FnPointerCannotBeAsync { span: whole_span, qualifier: span }); + if let Some(ast::CoroutineKind::Async { span: async_span, .. }) = coroutine_kind { + let next_token_lo = if let ast::Safety::Unsafe(span) | ast::Safety::Safe(span) = safety + { + span.lo() + } else if let ast::Extern::Implicit(span) | ast::Extern::Explicit(_, span) = ext { + span.lo() + } else { + fn_start_lo + }; + let sugg_span = async_span.with_hi(next_token_lo); + self.dcx().emit_err(FnPointerCannotBeAsync { + span: whole_span, + qualifier: async_span, + suggestion: sugg_span, + }); } // FIXME(gen_blocks): emit a similar error for `gen fn()` let decl_span = span_start.to(self.prev_token.span); diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 5418f054bebd2..09c88e7f83bb6 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -16,11 +16,8 @@ #![warn(unreachable_pub)] // tidy-alphabetical-end -use std::{iter, str, string}; - pub use Alignment::*; pub use Count::*; -pub use Piece::*; pub use Position::*; use rustc_lexer::unescape; @@ -86,7 +83,7 @@ impl InnerOffset { #[derive(Clone, Debug, PartialEq)] pub enum Piece<'a> { /// A literal string which should directly be emitted - String(&'a str), + Lit(&'a str), /// This describes that formatting should process the next argument (as /// specified inside) for emission. NextArgument(Box>), @@ -205,11 +202,11 @@ pub enum Count<'a> { } pub struct ParseError { - pub description: string::String, - pub note: Option, - pub label: string::String, + pub description: String, + pub note: Option, + pub label: String, pub span: InnerSpan, - pub secondary_label: Option<(string::String, InnerSpan)>, + pub secondary_label: Option<(String, InnerSpan)>, pub suggestion: Suggestion, } @@ -225,7 +222,7 @@ pub enum Suggestion { /// `format!("{foo:?#}")` -> `format!("{foo:#?}")` /// `format!("{foo:?x}")` -> `format!("{foo:x?}")` /// `format!("{foo:?X}")` -> `format!("{foo:X?}")` - ReorderFormatParameter(InnerSpan, string::String), + ReorderFormatParameter(InnerSpan, String), } /// The parser structure for interpreting the input format string. This is @@ -237,7 +234,7 @@ pub enum Suggestion { pub struct Parser<'a> { mode: ParseMode, input: &'a str, - cur: iter::Peekable>, + cur: std::iter::Peekable>, /// Error messages accumulated during parsing pub errors: Vec, /// Current position of implicit positional argument pointer @@ -278,7 +275,7 @@ impl<'a> Iterator for Parser<'a> { if self.consume('{') { self.last_opening_brace = curr_last_brace; - Some(String(self.string(pos + 1))) + Some(Piece::Lit(self.string(pos + 1))) } else { let arg = self.argument(lbrace_end); if let Some(rbrace_pos) = self.consume_closing_brace(&arg) { @@ -299,13 +296,13 @@ impl<'a> Iterator for Parser<'a> { _ => self.suggest_positional_arg_instead_of_captured_arg(arg), } } - Some(NextArgument(Box::new(arg))) + Some(Piece::NextArgument(Box::new(arg))) } } '}' => { self.cur.next(); if self.consume('}') { - Some(String(self.string(pos + 1))) + Some(Piece::Lit(self.string(pos + 1))) } else { let err_pos = self.to_span_index(pos); self.err_with_note( @@ -317,7 +314,7 @@ impl<'a> Iterator for Parser<'a> { None } } - _ => Some(String(self.string(pos))), + _ => Some(Piece::Lit(self.string(pos))), } } else { if self.is_source_literal { @@ -336,7 +333,7 @@ impl<'a> Parser<'a> { pub fn new( s: &'a str, style: Option, - snippet: Option, + snippet: Option, append_newline: bool, mode: ParseMode, ) -> Parser<'a> { @@ -366,7 +363,7 @@ impl<'a> Parser<'a> { /// Notifies of an error. The message doesn't actually need to be of type /// String, but I think it does when this eventually uses conditions so it /// might as well start using it now. - fn err, S2: Into>( + fn err, S2: Into>( &mut self, description: S1, label: S2, @@ -385,11 +382,7 @@ impl<'a> Parser<'a> { /// Notifies of an error. The message doesn't actually need to be of type /// String, but I think it does when this eventually uses conditions so it /// might as well start using it now. - fn err_with_note< - S1: Into, - S2: Into, - S3: Into, - >( + fn err_with_note, S2: Into, S3: Into>( &mut self, description: S1, label: S2, @@ -968,7 +961,7 @@ impl<'a> Parser<'a> { /// in order to properly synthesise the intra-string `Span`s for error diagnostics. fn find_width_map_from_snippet( input: &str, - snippet: Option, + snippet: Option, str_style: Option, ) -> InputStringKind { let snippet = match snippet { @@ -1083,8 +1076,8 @@ fn find_width_map_from_snippet( InputStringKind::Literal { width_mappings } } -fn unescape_string(string: &str) -> Option { - let mut buf = string::String::new(); +fn unescape_string(string: &str) -> Option { + let mut buf = String::new(); let mut ok = true; unescape::unescape_unicode(string, unescape::Mode::Str, &mut |_, unescaped_char| { match unescaped_char { diff --git a/compiler/rustc_parse_format/src/tests.rs b/compiler/rustc_parse_format/src/tests.rs index 81e5bca0ba9fa..fbb217b16fc32 100644 --- a/compiler/rustc_parse_format/src/tests.rs +++ b/compiler/rustc_parse_format/src/tests.rs @@ -1,3 +1,5 @@ +use Piece::*; + use super::*; #[track_caller] @@ -32,12 +34,12 @@ fn musterr(s: &str) { #[test] fn simple() { - same("asdf", &[String("asdf")]); - same("a{{b", &[String("a"), String("{b")]); - same("a}}b", &[String("a"), String("}b")]); - same("a}}", &[String("a"), String("}")]); - same("}}", &[String("}")]); - same("\\}}", &[String("\\"), String("}")]); + same("asdf", &[Lit("asdf")]); + same("a{{b", &[Lit("a"), Lit("{b")]); + same("a}}b", &[Lit("a"), Lit("}b")]); + same("a}}", &[Lit("a"), Lit("}")]); + same("}}", &[Lit("}")]); + same("\\}}", &[Lit("\\"), Lit("}")]); } #[test] @@ -370,7 +372,7 @@ fn format_flags() { #[test] fn format_mixture() { same("abcd {3:x} efg", &[ - String("abcd "), + Lit("abcd "), NextArgument(Box::new(Argument { position: ArgumentIs(3), position_span: InnerSpan { start: 7, end: 8 }, @@ -390,7 +392,7 @@ fn format_mixture() { ty_span: None, }, })), - String(" efg"), + Lit(" efg"), ]); } #[test] diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 87024c487df71..16c0a345f8791 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -170,9 +170,12 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) { match fn_kind { - FnKind::Fn(_ctxt, _ident, FnSig { header, decl, span: _ }, _vis, generics, body) - if let Some(coroutine_kind) = header.coroutine_kind => - { + FnKind::Fn( + _ctxt, + _ident, + _vis, + Fn { sig: FnSig { header, decl, span: _ }, generics, body, .. }, + ) if let Some(coroutine_kind) = header.coroutine_kind => { self.visit_fn_header(header); self.visit_generics(generics); diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index ccd5b519cb047..8dc752c2cb38d 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -677,7 +677,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } if could_be_path { let import_suggestions = self.lookup_import_candidates( - Ident::with_dummy_span(name), + name, Namespace::ValueNS, &parent_scope, &|res: Res| { diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 3bfe98f7091b4..7eb795034b030 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -59,7 +59,7 @@ pub(crate) struct NameAlreadyUsedInParameterList { pub(crate) span: Span, #[label(resolve_first_use_of_name)] pub(crate) first_use_span: Span, - pub(crate) name: Symbol, + pub(crate) name: Ident, } #[derive(Diagnostic)] @@ -142,7 +142,7 @@ pub(crate) struct VariableBoundWithDifferentMode { pub(crate) span: Span, #[label(resolve_first_binding_span)] pub(crate) first_binding_span: Span, - pub(crate) variable_name: Symbol, + pub(crate) variable_name: Ident, } #[derive(Diagnostic)] @@ -151,7 +151,7 @@ pub(crate) struct IdentifierBoundMoreThanOnceInParameterList { #[primary_span] #[label] pub(crate) span: Span, - pub(crate) identifier: Symbol, + pub(crate) identifier: Ident, } #[derive(Diagnostic)] @@ -160,7 +160,7 @@ pub(crate) struct IdentifierBoundMoreThanOnceInSamePattern { #[primary_span] #[label] pub(crate) span: Span, - pub(crate) identifier: Symbol, + pub(crate) identifier: Ident, } #[derive(Diagnostic)] @@ -478,7 +478,7 @@ pub(crate) struct TraitImplDuplicate { pub(crate) old_span: Span, #[label(resolve_trait_item_span)] pub(crate) trait_item_span: Span, - pub(crate) name: Symbol, + pub(crate) name: Ident, } #[derive(Diagnostic)] @@ -976,7 +976,7 @@ pub(crate) struct AttemptToDefineBuiltinMacroTwice { pub(crate) struct VariableIsNotBoundInAllPatterns { #[primary_span] pub(crate) multispan: MultiSpan, - pub(crate) name: Symbol, + pub(crate) name: Ident, } #[derive(Subdiagnostic, Debug, Clone)] @@ -984,7 +984,7 @@ pub(crate) struct VariableIsNotBoundInAllPatterns { pub(crate) struct PatternDoesntBindName { #[primary_span] pub(crate) span: Span, - pub(crate) name: Symbol, + pub(crate) name: Ident, } #[derive(Subdiagnostic, Debug, Clone)] @@ -1260,7 +1260,7 @@ pub(crate) struct TraitImplMismatch { #[primary_span] #[label] pub(crate) span: Span, - pub(crate) name: Symbol, + pub(crate) name: Ident, pub(crate) kind: &'static str, pub(crate) trait_path: String, #[label(resolve_trait_impl_mismatch_label_item)] diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 8bd40ed3a73b2..4842cbd556c37 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -986,8 +986,8 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r match fn_kind { // Bail if the function is foreign, and thus cannot validly have // a body, or if there's no body for some other reason. - FnKind::Fn(FnCtxt::Foreign, _, sig, _, generics, _) - | FnKind::Fn(_, _, sig, _, generics, None) => { + FnKind::Fn(FnCtxt::Foreign, _, _, Fn { sig, generics, .. }) + | FnKind::Fn(_, _, _, Fn { sig, generics, body: None, .. }) => { self.visit_fn_header(&sig.header); self.visit_generics(generics); self.with_lifetime_rib( @@ -1019,7 +1019,7 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r // Create a label rib for the function. this.with_label_rib(RibKind::FnOrCoroutine, |this| { match fn_kind { - FnKind::Fn(_, _, sig, _, generics, body) => { + FnKind::Fn(_, _, _, Fn { sig, generics, body, .. }) => { this.visit_generics(generics); let declaration = &sig.decl; @@ -2835,7 +2835,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { match seen_bindings.entry(ident) { Entry::Occupied(entry) => { let span = *entry.get(); - let err = ResolutionError::NameAlreadyUsedInParameterList(ident.name, span); + let err = ResolutionError::NameAlreadyUsedInParameterList(ident, span); self.report_error(param.ident.span, err); let rib = match param.kind { GenericParamKind::Lifetime => { @@ -3422,7 +3422,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { match seen_trait_items.entry(id_in_trait) { Entry::Occupied(entry) => { self.report_error(span, ResolutionError::TraitImplDuplicate { - name: ident.name, + name: ident, old_span: *entry.get(), trait_item_span: binding.span, }); @@ -3457,7 +3457,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { }; let trait_path = path_names_to_string(path); self.report_error(span, ResolutionError::TraitImplMismatch { - name: ident.name, + name: ident, kind, code, trait_path, @@ -3640,9 +3640,8 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { .filter(|(_, pat)| pat.id != pat_outer.id) .flat_map(|(map, _)| map); - for (key, binding_inner) in inners { - let name = key.name; - match map_outer.get(key) { + for (&name, binding_inner) in inners { + match map_outer.get(&name) { None => { // The inner binding is missing in the outer. let binding_error = @@ -3880,7 +3879,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // `Variant(a, a)`: _ => IdentifierBoundMoreThanOnceInSamePattern, }; - self.report_error(ident.span, error(ident.name)); + self.report_error(ident.span, error(ident)); } // Record as bound if it's valid: diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 17c92c7b501cc..57679d595da3e 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -224,7 +224,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let suggestion = if self.current_trait_ref.is_none() && let Some((fn_kind, _)) = self.diag_metadata.current_function && let Some(FnCtxt::Assoc(_)) = fn_kind.ctxt() - && let FnKind::Fn(_, _, sig, ..) = fn_kind + && let FnKind::Fn(_, _, _, ast::Fn { sig, .. }) = fn_kind && let Some(items) = self.diag_metadata.current_impl_items && let Some(item) = items.iter().find(|i| { i.ident.name == item_str.name @@ -560,7 +560,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { Applicability::MaybeIncorrect, ); if !self.self_value_is_available(path[0].ident.span) { - if let Some((FnKind::Fn(_, _, sig, ..), fn_span)) = + if let Some((FnKind::Fn(_, _, _, ast::Fn { sig, .. }), fn_span)) = &self.diag_metadata.current_function { let (span, sugg) = if let Some(param) = sig.decl.inputs.get(0) { @@ -1636,13 +1636,12 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { .enumerate() .map(|(idx, new)| (new, old_fields.get(idx))) .map(|(new, old)| { - let new = new.name.to_ident_string(); if let Some(Some(old)) = old - && new != *old + && new.as_str() != old { format!("{new}: {old}") } else { - new + new.to_string() } }) .collect::>() @@ -3250,7 +3249,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { { let pre = if lt.kind == MissingLifetimeKind::Ampersand && let Some((kind, _span)) = self.diag_metadata.current_function - && let FnKind::Fn(_, _, sig, _, _, _) = kind + && let FnKind::Fn(_, _, _, ast::Fn { sig, .. }) = kind && !sig.decl.inputs.is_empty() && let sugg = sig .decl @@ -3291,7 +3290,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } else if (lt.kind == MissingLifetimeKind::Ampersand || lt.kind == MissingLifetimeKind::Underscore) && let Some((kind, _span)) = self.diag_metadata.current_function - && let FnKind::Fn(_, _, sig, _, _, _) = kind + && let FnKind::Fn(_, _, _, ast::Fn { sig, .. }) = kind && let ast::FnRetTy::Ty(ret_ty) = &sig.decl.output && !sig.decl.inputs.is_empty() && let arg_refs = sig @@ -3351,7 +3350,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { let mut owned_sugg = lt.kind == MissingLifetimeKind::Ampersand; let mut sugg = vec![(lt.span, String::new())]; if let Some((kind, _span)) = self.diag_metadata.current_function - && let FnKind::Fn(_, _, sig, _, _, _) = kind + && let FnKind::Fn(_, _, _, ast::Fn { sig, .. }) = kind && let ast::FnRetTy::Ty(ty) = &sig.decl.output { let mut lt_finder = diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 3b18e480be442..04144eb616fad 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -214,7 +214,7 @@ enum Used { #[derive(Debug)] struct BindingError { - name: Symbol, + name: Ident, origin: BTreeSet, target: BTreeSet, could_be_path: bool, @@ -226,7 +226,7 @@ enum ResolutionError<'ra> { GenericParamsFromOuterItem(Res, HasGenericParams, DefKind), /// Error E0403: the name is already used for a type or const parameter in this generic /// parameter list. - NameAlreadyUsedInParameterList(Symbol, Span), + NameAlreadyUsedInParameterList(Ident, Span), /// Error E0407: method is not a member of trait. MethodNotMemberOfTrait(Ident, String, Option), /// Error E0437: type is not a member of trait. @@ -236,11 +236,11 @@ enum ResolutionError<'ra> { /// Error E0408: variable `{}` is not bound in all patterns. VariableNotBoundInPattern(BindingError, ParentScope<'ra>), /// Error E0409: variable `{}` is bound in inconsistent ways within the same match arm. - VariableBoundWithDifferentMode(Symbol, Span), + VariableBoundWithDifferentMode(Ident, Span), /// Error E0415: identifier is bound more than once in this parameter list. - IdentifierBoundMoreThanOnceInParameterList(Symbol), + IdentifierBoundMoreThanOnceInParameterList(Ident), /// Error E0416: identifier is bound more than once in the same pattern. - IdentifierBoundMoreThanOnceInSamePattern(Symbol), + IdentifierBoundMoreThanOnceInSamePattern(Ident), /// Error E0426: use of undeclared label. UndeclaredLabel { name: Symbol, suggestion: Option }, /// Error E0429: `self` imports are only allowed within a `{ }` list. @@ -292,14 +292,14 @@ enum ResolutionError<'ra> { UnreachableLabel { name: Symbol, definition_span: Span, suggestion: Option }, /// Error E0323, E0324, E0325: mismatch between trait item and impl item. TraitImplMismatch { - name: Symbol, + name: Ident, kind: &'static str, trait_path: String, trait_item_span: Span, code: ErrCode, }, /// Error E0201: multiple impl items for the same trait item. - TraitImplDuplicate { name: Symbol, trait_item_span: Span, old_span: Span }, + TraitImplDuplicate { name: Ident, trait_item_span: Span, old_span: Span }, /// Inline asm `sym` operand must refer to a `fn` or `static`. InvalidAsmSym, /// `self` used instead of `Self` in a generic parameter diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 63aaa3abc8e56..4ce6382512978 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1870,8 +1870,6 @@ options! { "verify extended properties for incr. comp. (default: no): - hashes of green query instances - hash collisions of query keys"), - inline_in_all_cgus: Option = (None, parse_opt_bool, [TRACKED], - "control whether `#[inline]` functions are in all CGUs"), inline_llvm: bool = (true, parse_bool, [TRACKED], "enable LLVM inlining (default: yes)"), inline_mir: Option = (None, parse_opt_bool, [TRACKED], diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index a5a17b4b5730e..aee98d7d410ea 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -232,6 +232,18 @@ impl<'tcx> Stable<'tcx> for mir::Mutability { } } +impl<'tcx> Stable<'tcx> for mir::RawPtrKind { + type T = stable_mir::mir::RawPtrKind; + fn stable(&self, _: &mut Tables<'_>) -> Self::T { + use mir::RawPtrKind::*; + match *self { + Const => stable_mir::mir::RawPtrKind::Const, + Mut => stable_mir::mir::RawPtrKind::Mut, + FakeForPtrMetadata => stable_mir::mir::RawPtrKind::FakeForPtrMetadata, + } + } +} + impl<'tcx> Stable<'tcx> for mir::BorrowKind { type T = stable_mir::mir::BorrowKind; fn stable(&self, tables: &mut Tables<'_>) -> Self::T { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 1fb15fe98000b..6f1d3a74a8165 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -2184,8 +2184,10 @@ symbols! { vec_macro, vec_new, vec_pop, + vec_reserve, vec_with_capacity, vecdeque_iter, + vecdeque_reserve, vector, version, vfp2, diff --git a/compiler/rustc_target/src/callconv/x86_win64.rs b/compiler/rustc_target/src/callconv/x86_win64.rs index 0944bda26875d..816564d2fed8b 100644 --- a/compiler/rustc_target/src/callconv/x86_win64.rs +++ b/compiler/rustc_target/src/callconv/x86_win64.rs @@ -1,4 +1,4 @@ -use rustc_abi::{BackendRepr, Float, Primitive}; +use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind, Size}; use crate::abi::call::{ArgAbi, FnAbi, Reg}; use crate::spec::HasTargetSpec; @@ -6,7 +6,7 @@ use crate::spec::HasTargetSpec; // Win64 ABI: https://docs.microsoft.com/en-us/cpp/build/parameter-passing pub(crate) fn compute_abi_info(_cx: &impl HasTargetSpec, fn_abi: &mut FnAbi<'_, Ty>) { - let fixup = |a: &mut ArgAbi<'_, Ty>| { + let fixup = |a: &mut ArgAbi<'_, Ty>, is_ret: bool| { match a.layout.backend_repr { BackendRepr::Uninhabited | BackendRepr::Memory { sized: false } => {} BackendRepr::ScalarPair(..) | BackendRepr::Memory { sized: true } => { @@ -23,11 +23,16 @@ pub(crate) fn compute_abi_info(_cx: &impl HasTargetSpec, fn_abi: &mut FnAbi< // (probably what clang calls "illegal vectors"). } BackendRepr::Scalar(scalar) => { - // Match what LLVM does for `f128` so that `compiler-builtins` builtins match up - // with what LLVM expects. - if a.layout.size.bytes() > 8 + if is_ret && matches!(scalar.primitive(), Primitive::Int(Integer::I128, _)) { + // `i128` is returned in xmm0 by Clang and GCC + // FIXME(#134288): This may change for the `-msvc` targets in the future. + let reg = Reg { kind: RegKind::Vector, size: Size::from_bits(128) }; + a.cast_to(reg); + } else if a.layout.size.bytes() > 8 && !matches!(scalar.primitive(), Primitive::Float(Float::F128)) { + // Match what LLVM does for `f128` so that `compiler-builtins` builtins match up + // with what LLVM expects. a.make_indirect(); } else { a.extend_integer_width_to(32); @@ -37,8 +42,9 @@ pub(crate) fn compute_abi_info(_cx: &impl HasTargetSpec, fn_abi: &mut FnAbi< }; if !fn_abi.ret.is_ignore() { - fixup(&mut fn_abi.ret); + fixup(&mut fn_abi.ret, true); } + for arg in fn_abi.args.iter_mut() { if arg.is_ignore() && arg.layout.is_zst() { // Windows ABIs do not talk about ZST since such types do not exist in MSVC. @@ -49,7 +55,7 @@ pub(crate) fn compute_abi_info(_cx: &impl HasTargetSpec, fn_abi: &mut FnAbi< arg.make_indirect_from_ignore(); continue; } - fixup(arg); + fixup(arg, false); } // FIXME: We should likely also do something about ZST return types, similar to above. // However, that's non-trivial due to `()`. diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nuttx.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nuttx.rs index 04fd3ec1c26de..582211b02b64d 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_nuttx.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_nuttx.rs @@ -36,7 +36,7 @@ pub(crate) fn target() -> Target { description: Some("AArch64 NuttX".into()), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 64, data_layout: "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), diff --git a/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabi.rs index 138716e8f1433..08cbfc743968e 100644 --- a/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabi.rs @@ -31,7 +31,7 @@ pub(crate) fn target() -> Target { description: Some("ARMv7-A Cortex-A with NuttX".into()), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabihf.rs b/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabihf.rs index 40391c9f48e40..f68c11a9c687e 100644 --- a/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/armv7a_nuttx_eabihf.rs @@ -31,7 +31,7 @@ pub(crate) fn target() -> Target { description: Some("ARMv7-A Cortex-A with NuttX (hard float)".into()), tier: Some(3), host_tools: Some(false), - std: Some(false), + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_nuttx_elf.rs index 31c9180c509ce..3eb3d18faf44b 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_nuttx_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32imac_unknown_nuttx_elf.rs @@ -6,9 +6,9 @@ pub(crate) fn target() -> Target { llvm_target: "riscv32".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, arch: "riscv32".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32imafc_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32imafc_unknown_nuttx_elf.rs index 08dd3cc2a09de..7864f7f8f9a36 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32imafc_unknown_nuttx_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32imafc_unknown_nuttx_elf.rs @@ -6,9 +6,9 @@ pub(crate) fn target() -> Target { llvm_target: "riscv32".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, arch: "riscv32".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_nuttx_elf.rs index e86549806ddbf..60d8ec576af69 100644 --- a/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_nuttx_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv32imc_unknown_nuttx_elf.rs @@ -6,9 +6,9 @@ pub(crate) fn target() -> Target { llvm_target: "riscv32".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, arch: "riscv32".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs index c389759aecd2c..2cbb8c19b849e 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_nuttx_elf.rs @@ -8,9 +8,9 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, llvm_target: "riscv64".into(), pointer_width: 64, diff --git a/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_nuttx_elf.rs b/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_nuttx_elf.rs index 9c18166558115..306b23d278761 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_nuttx_elf.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64imac_unknown_nuttx_elf.rs @@ -8,9 +8,9 @@ pub(crate) fn target() -> Target { data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, llvm_target: "riscv64".into(), pointer_width: 64, diff --git a/compiler/rustc_target/src/spec/targets/thumbv6m_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv6m_nuttx_eabi.rs index 5799bbf551f8b..dcf98acc41f8a 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv6m_nuttx_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv6m_nuttx_eabi.rs @@ -7,9 +7,9 @@ pub(crate) fn target() -> Target { llvm_target: "thumbv6m-none-eabi".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), @@ -22,12 +22,9 @@ pub(crate) fn target() -> Target { llvm_floatabi: Some(FloatAbi::Soft), // The ARMv6-M architecture doesn't support unaligned loads/stores so we disable them // with +strict-align. - // Also force-enable 32-bit atomics, which allows the use of atomic load/store only. - // The resulting atomics are ABI incompatible with atomics backed by libatomic. - features: "+strict-align,+atomics-32".into(), - // There are no atomic CAS instructions available in the instruction set of the ARMv6-M - // architecture - atomic_cas: false, + // The ARMv6-M doesn't support hardware atomic operations, use atomic builtins instead. + features: "+strict-align".into(), + max_atomic_width: Some(32), ..base::thumb::opts() }, } diff --git a/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabi.rs index 7fd22602e5622..b5cb393f4b001 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabi.rs @@ -11,9 +11,9 @@ pub(crate) fn target() -> Target { llvm_target: "thumbv7a-none-eabi".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabihf.rs index d3148c53a829d..1aa44a8cc939f 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7a_nuttx_eabihf.rs @@ -14,9 +14,9 @@ pub(crate) fn target() -> Target { llvm_target: "thumbv7a-none-eabihf".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabi.rs index 536d128590fb4..a3bc4013e530a 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabi.rs @@ -16,9 +16,9 @@ pub(crate) fn target() -> Target { llvm_target: "thumbv7em-none-eabi".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabihf.rs index 35e92b81d87da..14bbe38257d3f 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7em_nuttx_eabihf.rs @@ -15,9 +15,9 @@ pub(crate) fn target() -> Target { llvm_target: "thumbv7em-none-eabihf".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/thumbv7m_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv7m_nuttx_eabi.rs index 320867444ad29..2a77f48a9cd34 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv7m_nuttx_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv7m_nuttx_eabi.rs @@ -7,9 +7,9 @@ pub(crate) fn target() -> Target { llvm_target: "thumbv7m-none-eabi".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/thumbv8m_base_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv8m_base_nuttx_eabi.rs index 1af01b97666fe..25a100e9c7e56 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv8m_base_nuttx_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv8m_base_nuttx_eabi.rs @@ -7,9 +7,9 @@ pub(crate) fn target() -> Target { llvm_target: "thumbv8m.base-none-eabi".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabi.rs b/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabi.rs index 661d74217adf6..0bfe2b32ad4a5 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabi.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabi.rs @@ -8,9 +8,9 @@ pub(crate) fn target() -> Target { llvm_target: "thumbv8m.main-none-eabi".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabihf.rs b/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabihf.rs index 484d35bfc2028..9f75f23aa93e1 100644 --- a/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabihf.rs +++ b/compiler/rustc_target/src/spec/targets/thumbv8m_main_nuttx_eabihf.rs @@ -8,9 +8,9 @@ pub(crate) fn target() -> Target { llvm_target: "thumbv8m.main-none-eabihf".into(), metadata: crate::spec::TargetMetadata { description: None, - tier: None, + tier: Some(3), host_tools: None, - std: None, + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(), diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 9fd07c8634aa1..0d8a4988dce0e 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -108,21 +108,19 @@ impl Stability { // per-function level, since we would then allow safe calls from functions with `+soft-float` to // functions without that feature! // -// It is important for soundness that features allowed here do *not* change the function call ABI. -// For example, disabling the `x87` feature on x86 changes how scalar floats are passed as -// arguments, so enabling toggling that feature would be unsound. In fact, since `-Ctarget-feature` -// will just allow unknown features (with a warning), we have to explicitly list features that change -// the ABI as `Forbidden` to ensure using them causes an error. Note that this is only effective if -// such features can never be toggled via `-Ctarget-cpu`! If that is ever a possibility, we will need -// extra checks ensuring that the LLVM-computed target features for a CPU did not (un)set a -// `Forbidden` feature. See https://github.com/rust-lang/rust/issues/116344 for some more context. -// FIXME: add such "forbidden" features for non-x86 targets. +// It is important for soundness to consider the interaction of targets features and the function +// call ABI. For example, disabling the `x87` feature on x86 changes how scalar floats are passed as +// arguments, so letting people toggle that feature would be unsound. To this end, the +// `abi_required_features` function computes which target features must and must not be enabled for +// any given target, and individual features can also be marked as `Forbidden`. +// See https://github.com/rust-lang/rust/issues/116344 for some more context. // // The one exception to features that change the ABI is features that enable larger vector -// registers. Those are permitted to be listed here. This is currently unsound (see -// https://github.com/rust-lang/rust/issues/116558); in the future we will have to ensure that -// functions can only use such vectors as arguments/return types if the corresponding target feature -// is enabled. +// registers. Those are permitted to be listed here. The `*_FOR_CORRECT_VECTOR_ABI` arrays store +// information about which target feature is ABI-required for which vector size; this is used to +// ensure that vectors can only be passed via `extern "C"` when the right feature is enabled. (For +// the "Rust" ABI we generally pass vectors by-ref exactly to avoid these issues.) +// Also see https://github.com/rust-lang/rust/issues/116558. // // Stabilizing a target feature requires t-lang approval. @@ -137,6 +135,11 @@ const ARM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-start ("aclass", Unstable(sym::arm_target_feature), &[]), ("aes", Unstable(sym::arm_target_feature), &["neon"]), + ( + "atomics-32", + Stability::Forbidden { reason: "unsound because it changes the ABI of atomic operations" }, + &[], + ), ("crc", Unstable(sym::arm_target_feature), &[]), ("d32", Unstable(sym::arm_target_feature), &[]), ("dotprod", Unstable(sym::arm_target_feature), &["neon"]), diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl index 750d2756b4a42..7c72318b4d78f 100644 --- a/compiler/rustc_trait_selection/messages.ftl +++ b/compiler/rustc_trait_selection/messages.ftl @@ -165,6 +165,8 @@ trait_selection_explicit_lifetime_required_with_param_type = explicit lifetime r trait_selection_fn_consider_casting = consider casting the fn item to a fn pointer: `{$casting}` +trait_selection_fn_consider_casting_both = consider casting both fn items to fn pointers using `as {$sig}` + trait_selection_fn_uniq_types = different fn items have unique types, even if their signatures are the same trait_selection_fps_cast = consider casting to a fn pointer trait_selection_fps_cast_both = consider casting both fn items to fn pointers using `as {$expected_sig}` diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index bcb6ac13b8faf..9eacd377361a7 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -196,7 +196,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0]; self.tcx - .explicit_item_super_predicates(def_id) + .explicit_item_self_bounds(def_id) .iter_instantiated_copied(self.tcx, args) .find_map(|(predicate, _)| { predicate @@ -1844,7 +1844,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.suggest_tuple_pattern(cause, &exp_found, diag); self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag); self.suggest_await_on_expect_found(cause, span, &exp_found, diag); - self.suggest_function_pointers(cause, span, &exp_found, diag); + self.suggest_function_pointers(cause, span, &exp_found, terr, diag); self.suggest_turning_stmt_into_expr(cause, &exp_found, diag); } } diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs index 1dd09fe7aafa2..e8d14b89d698e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/note_and_explain.rs @@ -293,7 +293,7 @@ impl Trait for X { (ty::Dynamic(t, _, ty::DynKind::Dyn), ty::Alias(ty::Opaque, alias)) if let Some(def_id) = t.principal_def_id() && tcx - .explicit_item_super_predicates(alias.def_id) + .explicit_item_self_bounds(alias.def_id) .skip_binder() .iter() .any(|(pred, _span)| match pred.kind().skip_binder() { @@ -422,7 +422,7 @@ impl Trait for X { ty::Alias(..) => values.expected, _ => values.found, }; - let preds = tcx.explicit_item_super_predicates(opaque_ty.def_id); + let preds = tcx.explicit_item_self_bounds(opaque_ty.def_id); for (pred, _span) in preds.skip_binder() { let ty::ClauseKind::Trait(trait_predicate) = pred.kind().skip_binder() else { diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs index af7e56961b726..231fecf7a4a83 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs @@ -12,6 +12,7 @@ use rustc_middle::traits::{ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, StatementAsExpression, }; +use rustc_middle::ty::error::TypeError; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self as ty, GenericArgKind, IsSuggestable, Ty, TypeVisitableExt}; use rustc_span::{Span, sym}; @@ -20,7 +21,7 @@ use tracing::debug; use crate::error_reporting::TypeErrCtxt; use crate::error_reporting::infer::hir::Path; use crate::errors::{ - ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes, + ConsiderAddingAwait, FnConsiderCasting, FnConsiderCastingBoth, FnItemsAreDistinct, FnUniqTypes, FunctionPointerSuggestion, SuggestAccessingField, SuggestRemoveSemiOrReturnBinding, SuggestTuplePatternMany, SuggestTuplePatternOne, TypeErrorAdditionalDiags, }; @@ -381,14 +382,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } } - pub(super) fn suggest_function_pointers( + pub fn suggest_function_pointers_impl( &self, - cause: &ObligationCause<'tcx>, - span: Span, + span: Option, exp_found: &ty::error::ExpectedFound>, diag: &mut Diag<'_>, ) { - debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found); let ty::error::ExpectedFound { expected, found } = exp_found; let expected_inner = expected.peel_refs(); let found_inner = found.peel_refs(); @@ -411,6 +410,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { return; } + let Some(span) = span else { + let casting = format!("{fn_name} as {sig}"); + diag.subdiagnostic(FnItemsAreDistinct); + diag.subdiagnostic(FnConsiderCasting { casting }); + return; + }; + let sugg = match (expected.is_ref(), found.is_ref()) { (true, false) => FunctionPointerSuggestion::UseRef { span, fn_name }, (false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name }, @@ -445,6 +451,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } let fn_name = self.tcx.def_path_str_with_args(*did2, args2); + + let Some(span) = span else { + diag.subdiagnostic(FnConsiderCastingBoth { sig: *expected_sig }); + return; + }; + let sug = if found.is_ref() { FunctionPointerSuggestion::CastBothRef { span, @@ -488,6 +500,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }; } + pub(super) fn suggest_function_pointers( + &self, + cause: &ObligationCause<'tcx>, + span: Span, + exp_found: &ty::error::ExpectedFound>, + terr: TypeError<'tcx>, + diag: &mut Diag<'_>, + ) { + debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found); + + if exp_found.expected.peel_refs().is_fn() && exp_found.found.peel_refs().is_fn() { + self.suggest_function_pointers_impl(Some(span), exp_found, diag); + } else if let TypeError::Sorts(exp_found) = terr { + self.suggest_function_pointers_impl(None, &exp_found, diag); + } + } + pub fn should_suggest_as_ref_kind( &self, expected: Ty<'tcx>, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 961719f263c51..6d39cbce3b788 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -462,6 +462,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err.note( "`#[target_feature]` functions do not implement the `Fn` traits", ); + err.note( + "try casting the function to a `fn` pointer or wrapping it in a closure", + ); } self.try_to_add_help_message( @@ -1966,6 +1969,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { StringPart::highlighted(exp_found.found.to_string()), StringPart::normal("`"), ]); + self.suggest_function_pointers_impl(None, &exp_found, err); } true diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index 2d932e36470e2..3d79b0acf83a9 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -13,7 +13,7 @@ use rustc_middle::ty::print::PrintTraitRefExt as _; use rustc_middle::ty::{self, GenericArgsRef, GenericParamDefKind, TyCtxt}; use rustc_parse_format::{ParseMode, Parser, Piece, Position}; use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES; -use rustc_span::{Span, Symbol, kw, sym}; +use rustc_span::{Ident, Span, Symbol, kw, sym}; use tracing::{debug, info}; use {rustc_attr_parsing as attr, rustc_hir as hir}; @@ -375,7 +375,7 @@ impl IgnoredDiagnosticOption { #[help] pub struct UnknownFormatParameterForOnUnimplementedAttr { argument_name: Symbol, - trait_name: Symbol, + trait_name: Ident, } #[derive(LintDiagnostic)] @@ -792,14 +792,14 @@ impl<'tcx> OnUnimplementedFormatString { tcx.trait_id_of_impl(item_def_id) .expect("expected `on_unimplemented` to correspond to a trait") }; - let trait_name = tcx.item_name(trait_def_id); + let trait_name = tcx.item_ident(trait_def_id); let generics = tcx.generics_of(item_def_id); let s = self.symbol.as_str(); let mut parser = Parser::new(s, None, None, false, ParseMode::Format); let mut result = Ok(()); for token in &mut parser { match token { - Piece::String(_) => (), // Normal string, no need to check it + Piece::Lit(_) => (), // Normal string, no need to check it Piece::NextArgument(a) => { let format_spec = a.format; if self.is_diagnostic_namespace_variant @@ -821,7 +821,11 @@ impl<'tcx> OnUnimplementedFormatString { Position::ArgumentNamed(s) => { match Symbol::intern(s) { // `{ThisTraitsName}` is allowed - s if s == trait_name && !self.is_diagnostic_namespace_variant => (), + s if s == trait_name.name + && !self.is_diagnostic_namespace_variant => + { + () + } s if ALLOWED_FORMAT_SYMBOLS.contains(&s) && !self.is_diagnostic_namespace_variant => { @@ -946,7 +950,7 @@ impl<'tcx> OnUnimplementedFormatString { let item_context = (options.get(&sym::ItemContext)).unwrap_or(&empty_string); let constructed_message = (&mut parser) .map(|p| match p { - Piece::String(s) => s.to_owned(), + Piece::Lit(s) => s.to_owned(), Piece::NextArgument(a) => match a.position { Position::ArgumentNamed(arg) => { let s = Symbol::intern(arg); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 471105773e2b0..ab25bef4120ee 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -1087,28 +1087,27 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { sig_parts.map_bound(|sig| sig.tupled_inputs_ty.tuple_fields().as_slice()), )) } - ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => self - .tcx - .item_super_predicates(def_id) - .instantiate(self.tcx, args) - .iter() - .find_map(|pred| { - if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder() + ty::Alias(ty::Opaque, ty::AliasTy { def_id, args, .. }) => { + self.tcx.item_self_bounds(def_id).instantiate(self.tcx, args).iter().find_map( + |pred| { + if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder() && self .tcx .is_lang_item(proj.projection_term.def_id, LangItem::FnOnceOutput) // args tuple will always be args[1] && let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind() - { - Some(( - DefIdOrName::DefId(def_id), - pred.kind().rebind(proj.term.expect_type()), - pred.kind().rebind(args.as_slice()), - )) - } else { - None - } - }), + { + Some(( + DefIdOrName::DefId(def_id), + pred.kind().rebind(proj.term.expect_type()), + pred.kind().rebind(args.as_slice()), + )) + } else { + None + } + }, + ) + } ty::Dynamic(data, _, ty::Dyn) => data.iter().find_map(|pred| { if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder() && self.tcx.is_lang_item(proj.def_id, LangItem::FnOnceOutput) @@ -2770,6 +2769,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ObligationCauseCode::SliceOrArrayElem => { err.note("slice and array elements must have `Sized` type"); } + ObligationCauseCode::ArrayLen(array_ty) => { + err.note(format!("the length of array `{array_ty}` must be type `usize`")); + } ObligationCauseCode::TupleElem => { err.note("only the last element of a tuple may have a dynamically sized type"); } diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 2dfa72972ba1d..c8672b9dbd2fe 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -1496,6 +1496,12 @@ pub struct FnConsiderCasting { pub casting: String, } +#[derive(Subdiagnostic)] +#[help(trait_selection_fn_consider_casting_both)] +pub struct FnConsiderCastingBoth<'a> { + pub sig: Binder<'a, FnSig<'a>>, +} + #[derive(Subdiagnostic)] pub enum SuggestAccessingField<'a> { #[suggestion( diff --git a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs index 26ba1511b540e..92098e204487a 100644 --- a/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs @@ -6,7 +6,8 @@ use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument}; use crate::traits::query::NoSolution; -use crate::traits::{ObligationCause, ObligationCtxt}; +use crate::traits::query::normalize::QueryNormalizeExt; +use crate::traits::{Normalized, ObligationCause, ObligationCtxt}; /// This returns true if the type `ty` is "trivial" for /// dropck-outlives -- that is, if it doesn't require any types to @@ -90,6 +91,7 @@ pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool { pub fn compute_dropck_outlives_inner<'tcx>( ocx: &ObligationCtxt<'_, 'tcx>, goal: ParamEnvAnd<'tcx, DropckOutlives<'tcx>>, + span: Span, ) -> Result, NoSolution> { let tcx = ocx.infcx.tcx; let ParamEnvAnd { param_env, value: DropckOutlives { dropped_ty } } = goal; @@ -135,7 +137,7 @@ pub fn compute_dropck_outlives_inner<'tcx>( // Set used to detect infinite recursion. let mut ty_set = FxHashSet::default(); - let cause = ObligationCause::dummy(); + let cause = ObligationCause::dummy_with_span(span); let mut constraints = DropckConstraint::empty(); while let Some((ty, depth)) = ty_stack.pop() { debug!( @@ -171,18 +173,13 @@ pub fn compute_dropck_outlives_inner<'tcx>( // do not themselves define a destructor", more or less. We have // to push them onto the stack to be expanded. for ty in constraints.dtorck_types.drain(..) { - let normalized_ty = ocx.normalize(&cause, param_env, ty); - - let errors = ocx.select_where_possible(); - if !errors.is_empty() { - debug!("failed to normalize dtorck type: {ty} ~> {errors:#?}"); - return Err(NoSolution); - } + let Normalized { value: ty, obligations } = + ocx.infcx.at(&cause, param_env).query_normalize(ty)?; + ocx.register_obligations(obligations); - let normalized_ty = ocx.infcx.resolve_vars_if_possible(normalized_ty); - debug!("dropck_outlives: ty from dtorck_types = {:?}", normalized_ty); + debug!("dropck_outlives: ty from dtorck_types = {:?}", ty); - match normalized_ty.kind() { + match ty.kind() { // All parameters live for the duration of the // function. ty::Param(..) => {} @@ -190,12 +187,12 @@ pub fn compute_dropck_outlives_inner<'tcx>( // A projection that we couldn't resolve - it // might have a destructor. ty::Alias(..) => { - result.kinds.push(normalized_ty.into()); + result.kinds.push(ty.into()); } _ => { - if ty_set.insert(normalized_ty) { - ty_stack.push((normalized_ty, depth + 1)); + if ty_set.insert(ty) { + ty_stack.push((ty, depth + 1)); } } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs index 254dee794f1bc..4eecde00eaa1e 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs @@ -30,8 +30,9 @@ impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> { fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, + span: Span, ) -> Result { - type_op_ascribe_user_type_with_span(ocx, key, None) + type_op_ascribe_user_type_with_span(ocx, key, span) } } @@ -41,11 +42,10 @@ impl<'tcx> super::QueryTypeOp<'tcx> for AscribeUserType<'tcx> { pub fn type_op_ascribe_user_type_with_span<'tcx>( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, AscribeUserType<'tcx>>, - span: Option, + span: Span, ) -> Result<(), NoSolution> { let (param_env, AscribeUserType { mir_ty, user_ty }) = key.into_parts(); debug!("type_op_ascribe_user_type: mir_ty={:?} user_ty={:?}", mir_ty, user_ty); - let span = span.unwrap_or(DUMMY_SP); match user_ty.kind { UserTypeKind::Ty(user_ty) => relate_mir_and_user_ty(ocx, param_env, span, mir_ty, user_ty)?, UserTypeKind::TypeOf(def_id, user_args) => { diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index fe47e837dfb53..1339739ce7f5e 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -5,7 +5,7 @@ use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; use rustc_middle::infer::canonical::CanonicalQueryResponse; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, ParamEnvAnd, Ty, TyCtxt, TypeFolder, TypeVisitableExt}; -use rustc_span::DUMMY_SP; +use rustc_span::Span; use rustc_span::def_id::CRATE_DEF_ID; use rustc_type_ir::outlives::{Component, push_outlives_components}; use smallvec::{SmallVec, smallvec}; @@ -45,11 +45,12 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ImpliedOutlivesBounds<'tcx> { fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, + span: Span, ) -> Result { if ocx.infcx.tcx.sess.opts.unstable_opts.no_implied_bounds_compat { - compute_implied_outlives_bounds_inner(ocx, key.param_env, key.value.ty) + compute_implied_outlives_bounds_inner(ocx, key.param_env, key.value.ty, span) } else { - compute_implied_outlives_bounds_compat_inner(ocx, key.param_env, key.value.ty) + compute_implied_outlives_bounds_compat_inner(ocx, key.param_env, key.value.ty, span) } } } @@ -58,13 +59,14 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( ocx: &ObligationCtxt<'_, 'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, + span: Span, ) -> Result>, NoSolution> { let normalize_op = |ty| -> Result<_, NoSolution> { // We must normalize the type so we can compute the right outlives components. // for example, if we have some constrained param type like `T: Trait`, // and we know that `&'a T::Out` is WF, then we want to imply `U: 'a`. let ty = ocx - .deeply_normalize(&ObligationCause::dummy(), param_env, ty) + .deeply_normalize(&ObligationCause::dummy_with_span(span), param_env, ty) .map_err(|_| NoSolution)?; if !ocx.select_all_or_error().is_empty() { return Err(NoSolution); @@ -142,6 +144,7 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>( ocx: &ObligationCtxt<'_, 'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>, + span: Span, ) -> Result>, NoSolution> { let tcx = ocx.infcx.tcx; @@ -171,8 +174,8 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>( // FIXME(@lcnr): It's not really "always fine", having fewer implied // bounds can be backward incompatible, e.g. #101951 was caused by // us not dealing with inference vars in `TypeOutlives` predicates. - let obligations = wf::obligations(ocx.infcx, param_env, CRATE_DEF_ID, 0, arg, DUMMY_SP) - .unwrap_or_default(); + let obligations = + wf::obligations(ocx.infcx, param_env, CRATE_DEF_ID, 0, arg, span).unwrap_or_default(); for obligation in obligations { debug!(?obligation); @@ -255,7 +258,7 @@ pub fn compute_implied_outlives_bounds_compat_inner<'tcx>( // Need to manually normalize in the new solver as `wf::obligations` does not. if ocx.infcx.next_trait_solver() { ty_a = ocx - .deeply_normalize(&ObligationCause::dummy(), param_env, ty_a) + .deeply_normalize(&ObligationCause::dummy_with_span(span), param_env, ty_a) .map_err(|_| NoSolution)?; } let mut components = smallvec![]; diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs index 54fce914bb658..68feb19c55b89 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/mod.rs @@ -92,6 +92,7 @@ pub trait QueryTypeOp<'tcx>: fmt::Debug + Copy + TypeFoldable> + 't fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, + span: Span, ) -> Result; fn fully_perform_into( @@ -152,7 +153,7 @@ where if infcx.next_trait_solver() { return Ok(scrape_region_constraints( infcx, - |ocx| QueryTypeOp::perform_locally_with_next_solver(ocx, self), + |ocx| QueryTypeOp::perform_locally_with_next_solver(ocx, self, span), "query type op", span, )? diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs index 94df222932efe..e8c2528aa6eeb 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/normalize.rs @@ -5,6 +5,7 @@ use rustc_middle::traits::query::NoSolution; pub use rustc_middle::traits::query::type_op::Normalize; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{self, Lift, ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt}; +use rustc_span::Span; use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; use crate::traits::ObligationCtxt; @@ -29,9 +30,10 @@ where fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, + span: Span, ) -> Result { // FIXME(-Znext-solver): shouldn't be using old normalizer - Ok(ocx.normalize(&ObligationCause::dummy(), key.param_env, key.value.value)) + Ok(ocx.normalize(&ObligationCause::dummy_with_span(span), key.param_env, key.value.value)) } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs index fa05f901f663d..99a2779aa8212 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/outlives.rs @@ -1,5 +1,6 @@ use rustc_middle::traits::query::{DropckOutlivesResult, NoSolution}; use rustc_middle::ty::{ParamEnvAnd, TyCtxt}; +use rustc_span::Span; use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; use crate::traits::ObligationCtxt; @@ -28,7 +29,8 @@ impl<'tcx> super::QueryTypeOp<'tcx> for DropckOutlives<'tcx> { fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, + span: Span, ) -> Result { - compute_dropck_outlives_inner(ocx, key.param_env.and(key.value)) + compute_dropck_outlives_inner(ocx, key.param_env.and(key.value), span) } } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs index b2dab379262f5..4f9e2e79d624c 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs @@ -4,6 +4,7 @@ use rustc_middle::traits::ObligationCause; use rustc_middle::traits::query::NoSolution; pub use rustc_middle::traits::query::type_op::ProvePredicate; use rustc_middle::ty::{self, ParamEnvAnd, TyCtxt}; +use rustc_span::Span; use crate::infer::canonical::{CanonicalQueryInput, CanonicalQueryResponse}; use crate::traits::ObligationCtxt; @@ -57,10 +58,11 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { fn perform_locally_with_next_solver( ocx: &ObligationCtxt<'_, 'tcx>, key: ParamEnvAnd<'tcx, Self>, + span: Span, ) -> Result { ocx.register_obligation(Obligation::new( ocx.infcx.tcx, - ObligationCause::dummy(), + ObligationCause::dummy_with_span(span), key.param_env, key.value.predicate, )); diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 0cc0d7f786b0f..6b6e0b32385c4 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1620,9 +1620,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // projections, we will never be able to equate, e.g. `::A` // with `<::A as Tr>::A`. let relevant_bounds = if in_parent_alias_type { - self.tcx().item_non_self_assumptions(alias_ty.def_id) + self.tcx().item_non_self_bounds(alias_ty.def_id) } else { - self.tcx().item_super_predicates(alias_ty.def_id) + self.tcx().item_self_bounds(alias_ty.def_id) }; for bound in relevant_bounds.instantiate(self.tcx(), alias_ty.args) { diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 9d32eb0538606..20b675bcb76b7 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -689,7 +689,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { self.require_sized(subty, ObligationCauseCode::SliceOrArrayElem); // Note that the len being WF is implicitly checked while visiting. // Here we just check that it's of type usize. - let cause = self.cause(ObligationCauseCode::Misc); + let cause = self.cause(ObligationCauseCode::ArrayLen(t)); self.out.push(traits::Obligation::with_depth( tcx, cause, diff --git a/compiler/rustc_traits/src/dropck_outlives.rs b/compiler/rustc_traits/src/dropck_outlives.rs index 51e4dbe81b3d0..b3377e15aa791 100644 --- a/compiler/rustc_traits/src/dropck_outlives.rs +++ b/compiler/rustc_traits/src/dropck_outlives.rs @@ -6,6 +6,7 @@ use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::traits::query::{DropckConstraint, DropckOutlivesResult}; use rustc_middle::ty::{self, GenericArgs, TyCtxt}; +use rustc_span::DUMMY_SP; use rustc_trait_selection::infer::InferCtxtBuilderExt; use rustc_trait_selection::traits::query::dropck_outlives::{ compute_dropck_outlives_inner, dtorck_constraint_for_ty_inner, @@ -24,7 +25,7 @@ fn dropck_outlives<'tcx>( debug!("dropck_outlives(goal={:#?})", canonical_goal); tcx.infer_ctxt().enter_canonical_trait_query(&canonical_goal, |ocx, goal| { - compute_dropck_outlives_inner(ocx, goal) + compute_dropck_outlives_inner(ocx, goal, DUMMY_SP) }) } diff --git a/compiler/rustc_traits/src/implied_outlives_bounds.rs b/compiler/rustc_traits/src/implied_outlives_bounds.rs index a51eefd908cc7..5f75e242a50fb 100644 --- a/compiler/rustc_traits/src/implied_outlives_bounds.rs +++ b/compiler/rustc_traits/src/implied_outlives_bounds.rs @@ -8,6 +8,7 @@ use rustc_infer::traits::query::OutlivesBound; use rustc_infer::traits::query::type_op::ImpliedOutlivesBounds; use rustc_middle::query::Providers; use rustc_middle::ty::TyCtxt; +use rustc_span::DUMMY_SP; use rustc_trait_selection::infer::InferCtxtBuilderExt; use rustc_trait_selection::traits::query::type_op::implied_outlives_bounds::{ compute_implied_outlives_bounds_compat_inner, compute_implied_outlives_bounds_inner, @@ -28,7 +29,7 @@ fn implied_outlives_bounds_compat<'tcx>( > { tcx.infer_ctxt().enter_canonical_trait_query(&goal, |ocx, key| { let (param_env, ImpliedOutlivesBounds { ty }) = key.into_parts(); - compute_implied_outlives_bounds_compat_inner(ocx, param_env, ty) + compute_implied_outlives_bounds_compat_inner(ocx, param_env, ty, DUMMY_SP) }) } @@ -41,6 +42,6 @@ fn implied_outlives_bounds<'tcx>( > { tcx.infer_ctxt().enter_canonical_trait_query(&goal, |ocx, key| { let (param_env, ImpliedOutlivesBounds { ty }) = key.into_parts(); - compute_implied_outlives_bounds_inner(ocx, param_env, ty) + compute_implied_outlives_bounds_inner(ocx, param_env, ty, DUMMY_SP) }) } diff --git a/compiler/rustc_traits/src/type_op.rs b/compiler/rustc_traits/src/type_op.rs index 5d041c2623aa8..e58ad639d206d 100644 --- a/compiler/rustc_traits/src/type_op.rs +++ b/compiler/rustc_traits/src/type_op.rs @@ -5,13 +5,15 @@ use rustc_infer::infer::canonical::{Canonical, CanonicalQueryInput, QueryRespons use rustc_middle::query::Providers; use rustc_middle::traits::query::NoSolution; use rustc_middle::ty::{Clause, FnSig, ParamEnvAnd, PolyFnSig, Ty, TyCtxt, TypeFoldable}; +use rustc_span::DUMMY_SP; use rustc_trait_selection::infer::InferCtxtBuilderExt; +use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; use rustc_trait_selection::traits::query::type_op::ascribe_user_type::{ AscribeUserType, type_op_ascribe_user_type_with_span, }; use rustc_trait_selection::traits::query::type_op::normalize::Normalize; use rustc_trait_selection::traits::query::type_op::prove_predicate::ProvePredicate; -use rustc_trait_selection::traits::{Obligation, ObligationCause, ObligationCtxt}; +use rustc_trait_selection::traits::{Normalized, Obligation, ObligationCause, ObligationCtxt}; pub(crate) fn provide(p: &mut Providers) { *p = Providers { @@ -30,7 +32,7 @@ fn type_op_ascribe_user_type<'tcx>( canonicalized: CanonicalQueryInput<'tcx, ParamEnvAnd<'tcx, AscribeUserType<'tcx>>>, ) -> Result<&'tcx Canonical<'tcx, QueryResponse<'tcx, ()>>, NoSolution> { tcx.infer_ctxt().enter_canonical_trait_query(&canonicalized, |ocx, key| { - type_op_ascribe_user_type_with_span(ocx, key, None) + type_op_ascribe_user_type_with_span(ocx, key, DUMMY_SP) }) } @@ -42,7 +44,10 @@ where T: fmt::Debug + TypeFoldable>, { let (param_env, Normalize { value }) = key.into_parts(); - Ok(ocx.normalize(&ObligationCause::dummy(), param_env, value)) + let Normalized { value, obligations } = + ocx.infcx.at(&ObligationCause::dummy(), param_env).query_normalize(value)?; + ocx.register_obligations(obligations); + Ok(value) } fn type_op_normalize_ty<'tcx>( diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 4fec606a83152..0c3b0758f0f95 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -203,6 +203,16 @@ pub trait Interner: def_id: Self::DefId, ) -> ty::EarlyBinder>; + fn item_self_bounds( + self, + def_id: Self::DefId, + ) -> ty::EarlyBinder>; + + fn item_non_self_bounds( + self, + def_id: Self::DefId, + ) -> ty::EarlyBinder>; + fn predicates_of( self, def_id: Self::DefId, diff --git a/compiler/stable_mir/src/mir/body.rs b/compiler/stable_mir/src/mir/body.rs index dfd090b39563f..8686169c15d66 100644 --- a/compiler/stable_mir/src/mir/body.rs +++ b/compiler/stable_mir/src/mir/body.rs @@ -457,7 +457,7 @@ pub enum Rvalue { /// /// This is generated by pointer casts like `&v as *const _` or raw address of expressions like /// `&raw v` or `addr_of!(v)`. - AddressOf(Mutability, Place), + AddressOf(RawPtrKind, Place), /// Creates an aggregate value, like a tuple or struct. /// @@ -577,7 +577,7 @@ impl Rvalue { } Rvalue::AddressOf(mutability, place) => { let place_ty = place.ty(locals)?; - Ok(Ty::new_ptr(place_ty, *mutability)) + Ok(Ty::new_ptr(place_ty, mutability.to_mutable_lossy())) } Rvalue::Len(..) => Ok(Ty::usize_ty()), Rvalue::Cast(.., ty) => Ok(*ty), @@ -903,6 +903,24 @@ impl BorrowKind { } } +#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] +pub enum RawPtrKind { + Mut, + Const, + FakeForPtrMetadata, +} + +impl RawPtrKind { + pub fn to_mutable_lossy(self) -> Mutability { + match self { + RawPtrKind::Mut { .. } => Mutability::Mut, + RawPtrKind::Const => Mutability::Not, + // FIXME: There's no type corresponding to a shallow borrow, so use `&` as an approximation. + RawPtrKind::FakeForPtrMetadata => Mutability::Not, + } + } +} + #[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize)] pub enum MutBorrowKind { Default, diff --git a/compiler/stable_mir/src/mir/pretty.rs b/compiler/stable_mir/src/mir/pretty.rs index 93ed32e258a8b..81981bce2026f 100644 --- a/compiler/stable_mir/src/mir/pretty.rs +++ b/compiler/stable_mir/src/mir/pretty.rs @@ -6,7 +6,9 @@ use std::{fmt, io, iter}; use fmt::{Display, Formatter}; use super::{AggregateKind, AssertMessage, BinOp, BorrowKind, FakeBorrowKind, TerminatorKind}; -use crate::mir::{Operand, Place, Rvalue, StatementKind, UnwindAction, VarDebugInfoContents}; +use crate::mir::{ + Operand, Place, RawPtrKind, Rvalue, StatementKind, UnwindAction, VarDebugInfoContents, +}; use crate::ty::{AdtKind, IndexedVal, MirConst, Ty, TyConst}; use crate::{Body, CrateDef, Mutability, with}; @@ -325,7 +327,7 @@ fn pretty_ty_const(ct: &TyConst) -> String { fn pretty_rvalue(writer: &mut W, rval: &Rvalue) -> io::Result<()> { match rval { Rvalue::AddressOf(mutability, place) => { - write!(writer, "&raw {} {:?}", pretty_mut(*mutability), place) + write!(writer, "&raw {} {:?}", pretty_raw_ptr_kind(*mutability), place) } Rvalue::Aggregate(aggregate_kind, operands) => { // FIXME: Add pretty_aggregate function that returns a pretty string @@ -437,3 +439,11 @@ fn pretty_mut(mutability: Mutability) -> &'static str { Mutability::Mut => "mut ", } } + +fn pretty_raw_ptr_kind(kind: RawPtrKind) -> &'static str { + match kind { + RawPtrKind::Const => "const", + RawPtrKind::Mut => "mut", + RawPtrKind::FakeForPtrMetadata => "const (fake)", + } +} diff --git a/compiler/stable_mir/src/mir/visit.rs b/compiler/stable_mir/src/mir/visit.rs index d73e79c48937e..79efb83cebdec 100644 --- a/compiler/stable_mir/src/mir/visit.rs +++ b/compiler/stable_mir/src/mir/visit.rs @@ -308,7 +308,7 @@ pub trait MirVisitor { fn super_rvalue(&mut self, rvalue: &Rvalue, location: Location) { match rvalue { Rvalue::AddressOf(mutability, place) => { - let pcx = PlaceContext { is_mut: *mutability == Mutability::Mut }; + let pcx = PlaceContext { is_mut: *mutability == RawPtrKind::Mut }; self.visit_place(place, pcx, location); } Rvalue::Aggregate(_, operands) => { diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 1b5e44a913467..8b38e6fc259af 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1115,6 +1115,8 @@ impl Box { /// memory problems. For example, a double-free may occur if the /// function is called twice on the same `NonNull` pointer. /// + /// The non-null pointer must point to a block of memory allocated by the global allocator. + /// /// The safety conditions are described in the [memory layout] section. /// /// # Examples @@ -1170,7 +1172,7 @@ impl Box { /// memory problems. For example, a double-free may occur if the /// function is called twice on the same raw pointer. /// - /// The raw pointer must point to a block of memory allocated by `alloc` + /// The raw pointer must point to a block of memory allocated by `alloc`. /// /// # Examples /// @@ -1225,6 +1227,7 @@ impl Box { /// memory problems. For example, a double-free may occur if the /// function is called twice on the same raw pointer. /// + /// The non-null pointer must point to a block of memory allocated by `alloc`. /// /// # Examples /// diff --git a/library/alloc/src/collections/btree/append.rs b/library/alloc/src/collections/btree/append.rs index d137d2721ee4f..091376d5d685b 100644 --- a/library/alloc/src/collections/btree/append.rs +++ b/library/alloc/src/collections/btree/append.rs @@ -16,7 +16,7 @@ impl Root { /// a `BTreeMap`, both iterators should produce keys in strictly ascending /// order, each greater than all keys in the tree, including any keys /// already in the tree upon entry. - pub fn append_from_sorted_iters( + pub(super) fn append_from_sorted_iters( &mut self, left: I, right: I, @@ -36,8 +36,12 @@ impl Root { /// Pushes all key-value pairs to the end of the tree, incrementing a /// `length` variable along the way. The latter makes it easier for the /// caller to avoid a leak when the iterator panicks. - pub fn bulk_push(&mut self, iter: I, length: &mut usize, alloc: A) - where + pub(super) fn bulk_push( + &mut self, + iter: I, + length: &mut usize, + alloc: A, + ) where I: Iterator, { let mut cur_node = self.borrow_mut().last_leaf_edge().into_node(); diff --git a/library/alloc/src/collections/btree/borrow.rs b/library/alloc/src/collections/btree/borrow.rs index 000b9bd0fab42..e848ac3f2d192 100644 --- a/library/alloc/src/collections/btree/borrow.rs +++ b/library/alloc/src/collections/btree/borrow.rs @@ -11,7 +11,7 @@ use core::ptr::NonNull; /// the compiler to follow. A `DormantMutRef` allows you to check borrowing /// yourself, while still expressing its stacked nature, and encapsulating /// the raw pointer code needed to do this without undefined behavior. -pub struct DormantMutRef<'a, T> { +pub(super) struct DormantMutRef<'a, T> { ptr: NonNull, _marker: PhantomData<&'a mut T>, } @@ -23,7 +23,7 @@ impl<'a, T> DormantMutRef<'a, T> { /// Capture a unique borrow, and immediately reborrow it. For the compiler, /// the lifetime of the new reference is the same as the lifetime of the /// original reference, but you promise to use it for a shorter period. - pub fn new(t: &'a mut T) -> (&'a mut T, Self) { + pub(super) fn new(t: &'a mut T) -> (&'a mut T, Self) { let ptr = NonNull::from(t); // SAFETY: we hold the borrow throughout 'a via `_marker`, and we expose // only this reference, so it is unique. @@ -37,7 +37,7 @@ impl<'a, T> DormantMutRef<'a, T> { /// /// The reborrow must have ended, i.e., the reference returned by `new` and /// all pointers and references derived from it, must not be used anymore. - pub unsafe fn awaken(self) -> &'a mut T { + pub(super) unsafe fn awaken(self) -> &'a mut T { // SAFETY: our own safety conditions imply this reference is again unique. unsafe { &mut *self.ptr.as_ptr() } } @@ -48,7 +48,7 @@ impl<'a, T> DormantMutRef<'a, T> { /// /// The reborrow must have ended, i.e., the reference returned by `new` and /// all pointers and references derived from it, must not be used anymore. - pub unsafe fn reborrow(&mut self) -> &'a mut T { + pub(super) unsafe fn reborrow(&mut self) -> &'a mut T { // SAFETY: our own safety conditions imply this reference is again unique. unsafe { &mut *self.ptr.as_ptr() } } @@ -59,7 +59,7 @@ impl<'a, T> DormantMutRef<'a, T> { /// /// The reborrow must have ended, i.e., the reference returned by `new` and /// all pointers and references derived from it, must not be used anymore. - pub unsafe fn reborrow_shared(&self) -> &'a T { + pub(super) unsafe fn reborrow_shared(&self) -> &'a T { // SAFETY: our own safety conditions imply this reference is again unique. unsafe { &*self.ptr.as_ptr() } } diff --git a/library/alloc/src/collections/btree/dedup_sorted_iter.rs b/library/alloc/src/collections/btree/dedup_sorted_iter.rs index cd6a88f329125..6bcf0bca519af 100644 --- a/library/alloc/src/collections/btree/dedup_sorted_iter.rs +++ b/library/alloc/src/collections/btree/dedup_sorted_iter.rs @@ -6,7 +6,7 @@ use core::iter::Peekable; /// Used by [`BTreeMap::bulk_build_from_sorted_iter`][1]. /// /// [1]: crate::collections::BTreeMap::bulk_build_from_sorted_iter -pub struct DedupSortedIter +pub(super) struct DedupSortedIter where I: Iterator, { @@ -17,7 +17,7 @@ impl DedupSortedIter where I: Iterator, { - pub fn new(iter: I) -> Self { + pub(super) fn new(iter: I) -> Self { Self { iter: iter.peekable() } } } diff --git a/library/alloc/src/collections/btree/fix.rs b/library/alloc/src/collections/btree/fix.rs index 09edea3555ad5..b0c6759794691 100644 --- a/library/alloc/src/collections/btree/fix.rs +++ b/library/alloc/src/collections/btree/fix.rs @@ -57,7 +57,10 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { /// /// This method does not expect ancestors to already be underfull upon entry /// and panics if it encounters an empty ancestor. - pub fn fix_node_and_affected_ancestors(mut self, alloc: A) -> bool { + pub(super) fn fix_node_and_affected_ancestors( + mut self, + alloc: A, + ) -> bool { loop { match self.fix_node_through_parent(alloc.clone()) { Ok(Some(parent)) => self = parent.forget_type(), @@ -70,7 +73,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { impl Root { /// Removes empty levels on the top, but keeps an empty leaf if the entire tree is empty. - pub fn fix_top(&mut self, alloc: A) { + pub(super) fn fix_top(&mut self, alloc: A) { while self.height() > 0 && self.len() == 0 { self.pop_internal_level(alloc.clone()); } @@ -79,7 +82,7 @@ impl Root { /// Stocks up or merge away any underfull nodes on the right border of the /// tree. The other nodes, those that are not the root nor a rightmost edge, /// must already have at least MIN_LEN elements. - pub fn fix_right_border(&mut self, alloc: A) { + pub(super) fn fix_right_border(&mut self, alloc: A) { self.fix_top(alloc.clone()); if self.len() > 0 { self.borrow_mut().last_kv().fix_right_border_of_right_edge(alloc.clone()); @@ -88,7 +91,7 @@ impl Root { } /// The symmetric clone of `fix_right_border`. - pub fn fix_left_border(&mut self, alloc: A) { + pub(super) fn fix_left_border(&mut self, alloc: A) { self.fix_top(alloc.clone()); if self.len() > 0 { self.borrow_mut().first_kv().fix_left_border_of_left_edge(alloc.clone()); @@ -99,7 +102,7 @@ impl Root { /// Stocks up any underfull nodes on the right border of the tree. /// The other nodes, those that are neither the root nor a rightmost edge, /// must be prepared to have up to MIN_LEN elements stolen. - pub fn fix_right_border_of_plentiful(&mut self) { + pub(super) fn fix_right_border_of_plentiful(&mut self) { let mut cur_node = self.borrow_mut(); while let Internal(internal) = cur_node.force() { // Check if rightmost child is underfull. diff --git a/library/alloc/src/collections/btree/mem.rs b/library/alloc/src/collections/btree/mem.rs index d738c5c47b4cc..4643c4133d55d 100644 --- a/library/alloc/src/collections/btree/mem.rs +++ b/library/alloc/src/collections/btree/mem.rs @@ -6,7 +6,7 @@ use core::{intrinsics, mem, ptr}; /// If a panic occurs in the `change` closure, the entire process will be aborted. #[allow(dead_code)] // keep as illustration and for future use #[inline] -pub fn take_mut(v: &mut T, change: impl FnOnce(T) -> T) { +pub(super) fn take_mut(v: &mut T, change: impl FnOnce(T) -> T) { replace(v, |value| (change(value), ())) } @@ -15,7 +15,7 @@ pub fn take_mut(v: &mut T, change: impl FnOnce(T) -> T) { /// /// If a panic occurs in the `change` closure, the entire process will be aborted. #[inline] -pub fn replace(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R { +pub(super) fn replace(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R { struct PanicGuard; impl Drop for PanicGuard { fn drop(&mut self) { diff --git a/library/alloc/src/collections/btree/merge_iter.rs b/library/alloc/src/collections/btree/merge_iter.rs index 7f23d93b990f5..c5b93d30a1185 100644 --- a/library/alloc/src/collections/btree/merge_iter.rs +++ b/library/alloc/src/collections/btree/merge_iter.rs @@ -4,7 +4,7 @@ use core::iter::FusedIterator; /// Core of an iterator that merges the output of two strictly ascending iterators, /// for instance a union or a symmetric difference. -pub struct MergeIterInner { +pub(super) struct MergeIterInner { a: I, b: I, peeked: Option>, @@ -40,7 +40,7 @@ where impl MergeIterInner { /// Creates a new core for an iterator merging a pair of sources. - pub fn new(a: I, b: I) -> Self { + pub(super) fn new(a: I, b: I) -> Self { MergeIterInner { a, b, peeked: None } } @@ -51,7 +51,7 @@ impl MergeIterInner { /// the sources are not strictly ascending). If neither returned option /// contains a value, iteration has finished and subsequent calls will /// return the same empty pair. - pub fn nexts Ordering>( + pub(super) fn nexts Ordering>( &mut self, cmp: Cmp, ) -> (Option, Option) @@ -85,7 +85,7 @@ impl MergeIterInner { } /// Returns a pair of upper bounds for the `size_hint` of the final iterator. - pub fn lens(&self) -> (usize, usize) + pub(super) fn lens(&self) -> (usize, usize) where I: ExactSizeIterator, { diff --git a/library/alloc/src/collections/btree/mod.rs b/library/alloc/src/collections/btree/mod.rs index b8667d09c33b3..6651480667391 100644 --- a/library/alloc/src/collections/btree/mod.rs +++ b/library/alloc/src/collections/btree/mod.rs @@ -2,13 +2,13 @@ mod append; mod borrow; mod dedup_sorted_iter; mod fix; -pub mod map; +pub(super) mod map; mod mem; mod merge_iter; mod navigate; mod node; mod remove; mod search; -pub mod set; +pub(super) mod set; mod set_val; mod split; diff --git a/library/alloc/src/collections/btree/navigate.rs b/library/alloc/src/collections/btree/navigate.rs index 14b7d4ad71f86..b2a7de74875d9 100644 --- a/library/alloc/src/collections/btree/navigate.rs +++ b/library/alloc/src/collections/btree/navigate.rs @@ -7,7 +7,7 @@ use super::node::{Handle, NodeRef, marker}; use super::search::SearchBound; use crate::alloc::Allocator; // `front` and `back` are always both `None` or both `Some`. -pub struct LeafRange { +pub(super) struct LeafRange { front: Option, marker::Edge>>, back: Option, marker::Edge>>, } @@ -25,7 +25,7 @@ impl Default for LeafRange { } impl LeafRange { - pub fn none() -> Self { + pub(super) fn none() -> Self { LeafRange { front: None, back: None } } @@ -34,7 +34,7 @@ impl LeafRange { } /// Temporarily takes out another, immutable equivalent of the same range. - pub fn reborrow(&self) -> LeafRange, K, V> { + pub(super) fn reborrow(&self) -> LeafRange, K, V> { LeafRange { front: self.front.as_ref().map(|f| f.reborrow()), back: self.back.as_ref().map(|b| b.reborrow()), @@ -44,24 +44,24 @@ impl LeafRange { impl<'a, K, V> LeafRange, K, V> { #[inline] - pub fn next_checked(&mut self) -> Option<(&'a K, &'a V)> { + pub(super) fn next_checked(&mut self) -> Option<(&'a K, &'a V)> { self.perform_next_checked(|kv| kv.into_kv()) } #[inline] - pub fn next_back_checked(&mut self) -> Option<(&'a K, &'a V)> { + pub(super) fn next_back_checked(&mut self) -> Option<(&'a K, &'a V)> { self.perform_next_back_checked(|kv| kv.into_kv()) } } impl<'a, K, V> LeafRange, K, V> { #[inline] - pub fn next_checked(&mut self) -> Option<(&'a K, &'a mut V)> { + pub(super) fn next_checked(&mut self) -> Option<(&'a K, &'a mut V)> { self.perform_next_checked(|kv| unsafe { ptr::read(kv) }.into_kv_valmut()) } #[inline] - pub fn next_back_checked(&mut self) -> Option<(&'a K, &'a mut V)> { + pub(super) fn next_back_checked(&mut self) -> Option<(&'a K, &'a mut V)> { self.perform_next_back_checked(|kv| unsafe { ptr::read(kv) }.into_kv_valmut()) } } @@ -124,7 +124,7 @@ impl LazyLeafHandle { } // `front` and `back` are always both `None` or both `Some`. -pub struct LazyLeafRange { +pub(super) struct LazyLeafRange { front: Option>, back: Option>, } @@ -142,12 +142,12 @@ impl<'a, K: 'a, V: 'a> Clone for LazyLeafRange, K, V> { } impl LazyLeafRange { - pub fn none() -> Self { + pub(super) fn none() -> Self { LazyLeafRange { front: None, back: None } } /// Temporarily takes out another, immutable equivalent of the same range. - pub fn reborrow(&self) -> LazyLeafRange, K, V> { + pub(super) fn reborrow(&self) -> LazyLeafRange, K, V> { LazyLeafRange { front: self.front.as_ref().map(|f| f.reborrow()), back: self.back.as_ref().map(|b| b.reborrow()), @@ -157,24 +157,24 @@ impl LazyLeafRange { impl<'a, K, V> LazyLeafRange, K, V> { #[inline] - pub unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) { + pub(super) unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) { unsafe { self.init_front().unwrap().next_unchecked() } } #[inline] - pub unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) { + pub(super) unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) { unsafe { self.init_back().unwrap().next_back_unchecked() } } } impl<'a, K, V> LazyLeafRange, K, V> { #[inline] - pub unsafe fn next_unchecked(&mut self) -> (&'a K, &'a mut V) { + pub(super) unsafe fn next_unchecked(&mut self) -> (&'a K, &'a mut V) { unsafe { self.init_front().unwrap().next_unchecked() } } #[inline] - pub unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a mut V) { + pub(super) unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a mut V) { unsafe { self.init_back().unwrap().next_back_unchecked() } } } @@ -190,7 +190,7 @@ impl LazyLeafRange { } #[inline] - pub unsafe fn deallocating_next_unchecked( + pub(super) unsafe fn deallocating_next_unchecked( &mut self, alloc: A, ) -> Handle, marker::KV> { @@ -200,7 +200,7 @@ impl LazyLeafRange { } #[inline] - pub unsafe fn deallocating_next_back_unchecked( + pub(super) unsafe fn deallocating_next_back_unchecked( &mut self, alloc: A, ) -> Handle, marker::KV> { @@ -210,7 +210,7 @@ impl LazyLeafRange { } #[inline] - pub fn deallocating_end(&mut self, alloc: A) { + pub(super) fn deallocating_end(&mut self, alloc: A) { if let Some(front) = self.take_front() { front.deallocating_end(alloc) } @@ -313,7 +313,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> /// /// The result is meaningful only if the tree is ordered by key, like the tree /// in a `BTreeMap` is. - pub fn range_search(self, range: R) -> LeafRange, K, V> + pub(super) fn range_search(self, range: R) -> LeafRange, K, V> where Q: ?Sized + Ord, K: Borrow, @@ -324,7 +324,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> } /// Finds the pair of leaf edges delimiting an entire tree. - pub fn full_range(self) -> LazyLeafRange, K, V> { + pub(super) fn full_range(self) -> LazyLeafRange, K, V> { full_range(self, self) } } @@ -339,7 +339,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> /// /// # Safety /// Do not use the duplicate handles to visit the same KV twice. - pub fn range_search(self, range: R) -> LeafRange, K, V> + pub(super) fn range_search(self, range: R) -> LeafRange, K, V> where Q: ?Sized + Ord, K: Borrow, @@ -351,7 +351,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> /// Splits a unique reference into a pair of leaf edges delimiting the full range of the tree. /// The results are non-unique references allowing mutation (of values only), so must be used /// with care. - pub fn full_range(self) -> LazyLeafRange, K, V> { + pub(super) fn full_range(self) -> LazyLeafRange, K, V> { // We duplicate the root NodeRef here -- we will never visit the same KV // twice, and never end up with overlapping value references. let self2 = unsafe { ptr::read(&self) }; @@ -363,7 +363,7 @@ impl NodeRef { /// Splits a unique reference into a pair of leaf edges delimiting the full range of the tree. /// The results are non-unique references allowing massively destructive mutation, so must be /// used with the utmost care. - pub fn full_range(self) -> LazyLeafRange { + pub(super) fn full_range(self) -> LazyLeafRange { // We duplicate the root NodeRef here -- we will never access it in a way // that overlaps references obtained from the root. let self2 = unsafe { ptr::read(&self) }; @@ -377,7 +377,7 @@ impl /// Given a leaf edge handle, returns [`Result::Ok`] with a handle to the neighboring KV /// on the right side, which is either in the same leaf node or in an ancestor node. /// If the leaf edge is the last one in the tree, returns [`Result::Err`] with the root node. - pub fn next_kv( + pub(super) fn next_kv( self, ) -> Result< Handle, marker::KV>, @@ -398,7 +398,7 @@ impl /// Given a leaf edge handle, returns [`Result::Ok`] with a handle to the neighboring KV /// on the left side, which is either in the same leaf node or in an ancestor node. /// If the leaf edge is the first one in the tree, returns [`Result::Err`] with the root node. - pub fn next_back_kv( + pub(super) fn next_back_kv( self, ) -> Result< Handle, marker::KV>, @@ -627,7 +627,9 @@ impl NodeRef Handle, marker::Edge> { + pub(super) fn first_leaf_edge( + self, + ) -> Handle, marker::Edge> { let mut node = self; loop { match node.force() { @@ -640,7 +642,9 @@ impl NodeRef Handle, marker::Edge> { + pub(super) fn last_leaf_edge( + self, + ) -> Handle, marker::Edge> { let mut node = self; loop { match node.force() { @@ -651,7 +655,7 @@ impl NodeRef { +pub(super) enum Position { Leaf(NodeRef), Internal(NodeRef), InternalKV, @@ -661,7 +665,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> /// Visits leaf nodes and internal KVs in order of ascending keys, and also /// visits internal nodes as a whole in a depth first order, meaning that /// internal nodes precede their individual KVs and their child nodes. - pub fn visit_nodes_in_order(self, mut visit: F) + pub(super) fn visit_nodes_in_order(self, mut visit: F) where F: FnMut(Position, K, V>), { @@ -693,7 +697,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> } /// Calculates the number of elements in a (sub)tree. - pub fn calc_length(self) -> usize { + pub(super) fn calc_length(self) -> usize { let mut result = 0; self.visit_nodes_in_order(|pos| match pos { Position::Leaf(node) => result += node.len(), @@ -708,7 +712,9 @@ impl Handle, marker::KV> { /// Returns the leaf edge closest to a KV for forward navigation. - pub fn next_leaf_edge(self) -> Handle, marker::Edge> { + pub(super) fn next_leaf_edge( + self, + ) -> Handle, marker::Edge> { match self.force() { Leaf(leaf_kv) => leaf_kv.right_edge(), Internal(internal_kv) => { @@ -719,7 +725,7 @@ impl } /// Returns the leaf edge closest to a KV for backward navigation. - pub fn next_back_leaf_edge( + pub(super) fn next_back_leaf_edge( self, ) -> Handle, marker::Edge> { match self.force() { @@ -735,7 +741,7 @@ impl impl NodeRef { /// Returns the leaf edge corresponding to the first point at which the /// given bound is true. - pub fn lower_bound( + pub(super) fn lower_bound( self, mut bound: SearchBound<&Q>, ) -> Handle, marker::Edge> @@ -758,7 +764,7 @@ impl NodeRef( + pub(super) fn upper_bound( self, mut bound: SearchBound<&Q>, ) -> Handle, marker::Edge> diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs index 4057657632ba4..6815ac1c19305 100644 --- a/library/alloc/src/collections/btree/node.rs +++ b/library/alloc/src/collections/btree/node.rs @@ -40,8 +40,8 @@ use crate::alloc::{Allocator, Layout}; use crate::boxed::Box; const B: usize = 6; -pub const CAPACITY: usize = 2 * B - 1; -pub const MIN_LEN_AFTER_SPLIT: usize = B - 1; +pub(super) const CAPACITY: usize = 2 * B - 1; +pub(super) const MIN_LEN_AFTER_SPLIT: usize = B - 1; const KV_IDX_CENTER: usize = B - 1; const EDGE_IDX_LEFT_OF_CENTER: usize = B - 1; const EDGE_IDX_RIGHT_OF_CENTER: usize = B; @@ -179,7 +179,7 @@ type BoxedNode = NonNull>; /// as the returned reference is used. /// The methods supporting insert bend this rule by returning a raw pointer, /// i.e., a reference without any lifetime. -pub struct NodeRef { +pub(super) struct NodeRef { /// The number of levels that the node and the level of leaves are apart, a /// constant of the node that cannot be entirely described by `Type`, and that /// the node itself does not store. We only need to store the height of the root @@ -195,7 +195,7 @@ pub struct NodeRef { /// The root node of an owned tree. /// /// Note that this does not have a destructor, and must be cleaned up manually. -pub type Root = NodeRef; +pub(super) type Root = NodeRef; impl<'a, K: 'a, V: 'a, Type> Copy for NodeRef, K, V, Type> {} impl<'a, K: 'a, V: 'a, Type> Clone for NodeRef, K, V, Type> { @@ -213,7 +213,7 @@ unsafe impl Send for NodeRef unsafe impl Send for NodeRef {} impl NodeRef { - pub fn new_leaf(alloc: A) -> Self { + pub(super) fn new_leaf(alloc: A) -> Self { Self::from_new_leaf(LeafNode::new(alloc)) } @@ -274,7 +274,7 @@ impl NodeRef { /// The number of edges is `len() + 1`. /// Note that, despite being safe, calling this function can have the side effect /// of invalidating mutable references that unsafe code has created. - pub fn len(&self) -> usize { + pub(super) fn len(&self) -> usize { // Crucially, we only access the `len` field here. If BorrowType is marker::ValMut, // there might be outstanding mutable references to values that we must not invalidate. unsafe { usize::from((*Self::as_leaf_ptr(self)).len) } @@ -285,12 +285,12 @@ impl NodeRef { /// root on top, the number says at which elevation the node appears. /// If you picture trees with leaves on top, the number says how high /// the tree extends above the node. - pub fn height(&self) -> usize { + pub(super) fn height(&self) -> usize { self.height } /// Temporarily takes out another, immutable reference to the same node. - pub fn reborrow(&self) -> NodeRef, K, V, Type> { + pub(super) fn reborrow(&self) -> NodeRef, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } @@ -315,7 +315,7 @@ impl NodeRef /// /// `edge.descend().ascend().unwrap()` and `node.ascend().unwrap().descend()` should /// both, upon success, do nothing. - pub fn ascend( + pub(super) fn ascend( self, ) -> Result, marker::Edge>, Self> { const { @@ -335,24 +335,24 @@ impl NodeRef .ok_or(self) } - pub fn first_edge(self) -> Handle { + pub(super) fn first_edge(self) -> Handle { unsafe { Handle::new_edge(self, 0) } } - pub fn last_edge(self) -> Handle { + pub(super) fn last_edge(self) -> Handle { let len = self.len(); unsafe { Handle::new_edge(self, len) } } /// Note that `self` must be nonempty. - pub fn first_kv(self) -> Handle { + pub(super) fn first_kv(self) -> Handle { let len = self.len(); assert!(len > 0); unsafe { Handle::new_kv(self, 0) } } /// Note that `self` must be nonempty. - pub fn last_kv(self) -> Handle { + pub(super) fn last_kv(self) -> Handle { let len = self.len(); assert!(len > 0); unsafe { Handle::new_kv(self, len - 1) } @@ -381,7 +381,7 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { } /// Borrows a view into the keys stored in the node. - pub fn keys(&self) -> &[K] { + pub(super) fn keys(&self) -> &[K] { let leaf = self.into_leaf(); unsafe { leaf.keys.get_unchecked(..usize::from(leaf.len)).assume_init_ref() } } @@ -391,7 +391,7 @@ impl NodeRef { /// Similar to `ascend`, gets a reference to a node's parent node, but also /// deallocates the current node in the process. This is unsafe because the /// current node will still be accessible despite being deallocated. - pub unsafe fn deallocate_and_ascend( + pub(super) unsafe fn deallocate_and_ascend( self, alloc: A, ) -> Option, marker::Edge>> { @@ -443,7 +443,7 @@ impl<'a, K, V, Type> NodeRef, K, V, Type> { /// Returns a dormant copy of this node with its lifetime erased which can /// be reawakened later. - pub fn dormant(&self) -> NodeRef { + pub(super) fn dormant(&self) -> NodeRef { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } @@ -455,7 +455,7 @@ impl NodeRef { /// /// The reborrow must have ended, i.e., the reference returned by `new` and /// all pointers and references derived from it, must not be used anymore. - pub unsafe fn awaken<'a>(self) -> NodeRef, K, V, Type> { + pub(super) unsafe fn awaken<'a>(self) -> NodeRef, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } @@ -536,7 +536,7 @@ impl<'a, K, V, Type> NodeRef, K, V, Type> { impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { /// Borrows exclusive access to the length of the node. - pub fn len_mut(&mut self) -> &mut u16 { + pub(super) fn len_mut(&mut self) -> &mut u16 { &mut self.as_leaf_mut().len } } @@ -578,14 +578,14 @@ impl NodeRef { impl NodeRef { /// Returns a new owned tree, with its own root node that is initially empty. - pub fn new(alloc: A) -> Self { + pub(super) fn new(alloc: A) -> Self { NodeRef::new_leaf(alloc).forget_type() } /// Adds a new internal node with a single edge pointing to the previous root node, /// make that new node the root node, and return it. This increases the height by 1 /// and is the opposite of `pop_internal_level`. - pub fn push_internal_level( + pub(super) fn push_internal_level( &mut self, alloc: A, ) -> NodeRef, K, V, marker::Internal> { @@ -604,7 +604,7 @@ impl NodeRef { /// it will not invalidate other handles or references to the root node. /// /// Panics if there is no internal level, i.e., if the root node is a leaf. - pub fn pop_internal_level(&mut self, alloc: A) { + pub(super) fn pop_internal_level(&mut self, alloc: A) { assert!(self.height > 0); let top = self.node; @@ -628,18 +628,18 @@ impl NodeRef { /// Mutably borrows the owned root node. Unlike `reborrow_mut`, this is safe /// because the return value cannot be used to destroy the root, and there /// cannot be other references to the tree. - pub fn borrow_mut(&mut self) -> NodeRef, K, V, Type> { + pub(super) fn borrow_mut(&mut self) -> NodeRef, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } /// Slightly mutably borrows the owned root node. - pub fn borrow_valmut(&mut self) -> NodeRef, K, V, Type> { + pub(super) fn borrow_valmut(&mut self) -> NodeRef, K, V, Type> { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } /// Irreversibly transitions to a reference that permits traversal and offers /// destructive methods and little else. - pub fn into_dying(self) -> NodeRef { + pub(super) fn into_dying(self) -> NodeRef { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } @@ -651,7 +651,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Leaf> { /// # Safety /// /// The returned handle has an unbound lifetime. - pub unsafe fn push_with_handle<'b>( + pub(super) unsafe fn push_with_handle<'b>( &mut self, key: K, val: V, @@ -672,7 +672,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Leaf> { /// Adds a key-value pair to the end of the node, and returns /// the mutable reference of the inserted value. - pub fn push(&mut self, key: K, val: V) -> *mut V { + pub(super) fn push(&mut self, key: K, val: V) -> *mut V { // SAFETY: The unbound handle is no longer accessible. unsafe { self.push_with_handle(key, val).into_val_mut() } } @@ -681,7 +681,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Leaf> { impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { /// Adds a key-value pair, and an edge to go to the right of that pair, /// to the end of the node. - pub fn push(&mut self, key: K, val: V, edge: Root) { + pub(super) fn push(&mut self, key: K, val: V, edge: Root) { assert!(edge.height == self.height - 1); let len = self.len_mut(); @@ -699,21 +699,21 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::Internal> { impl NodeRef { /// Removes any static information asserting that this node is a `Leaf` node. - pub fn forget_type(self) -> NodeRef { + pub(super) fn forget_type(self) -> NodeRef { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } impl NodeRef { /// Removes any static information asserting that this node is an `Internal` node. - pub fn forget_type(self) -> NodeRef { + pub(super) fn forget_type(self) -> NodeRef { NodeRef { height: self.height, node: self.node, _marker: PhantomData } } } impl NodeRef { /// Checks whether a node is an `Internal` node or a `Leaf` node. - pub fn force( + pub(super) fn force( self, ) -> ForceResult< NodeRef, @@ -737,7 +737,9 @@ impl NodeRef { impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { /// Unsafely asserts to the compiler the static information that this node is a `Leaf`. - pub unsafe fn cast_to_leaf_unchecked(self) -> NodeRef, K, V, marker::Leaf> { + pub(super) unsafe fn cast_to_leaf_unchecked( + self, + ) -> NodeRef, K, V, marker::Leaf> { debug_assert!(self.height == 0); NodeRef { height: self.height, node: self.node, _marker: PhantomData } } @@ -757,7 +759,7 @@ impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { /// a child node, these represent the spaces where child pointers would go between the key-value /// pairs. For example, in a node with length 2, there would be 3 possible edge locations - one /// to the left of the node, one between the two pairs, and one at the right of the node. -pub struct Handle { +pub(super) struct Handle { node: Node, idx: usize, _marker: PhantomData, @@ -774,12 +776,12 @@ impl Clone for Handle { impl Handle { /// Retrieves the node that contains the edge or key-value pair this handle points to. - pub fn into_node(self) -> Node { + pub(super) fn into_node(self) -> Node { self.node } /// Returns the position of this handle in the node. - pub fn idx(&self) -> usize { + pub(super) fn idx(&self) -> usize { self.idx } } @@ -787,17 +789,17 @@ impl Handle { impl Handle, marker::KV> { /// Creates a new handle to a key-value pair in `node`. /// Unsafe because the caller must ensure that `idx < node.len()`. - pub unsafe fn new_kv(node: NodeRef, idx: usize) -> Self { + pub(super) unsafe fn new_kv(node: NodeRef, idx: usize) -> Self { debug_assert!(idx < node.len()); Handle { node, idx, _marker: PhantomData } } - pub fn left_edge(self) -> Handle, marker::Edge> { + pub(super) fn left_edge(self) -> Handle, marker::Edge> { unsafe { Handle::new_edge(self.node, self.idx) } } - pub fn right_edge(self) -> Handle, marker::Edge> { + pub(super) fn right_edge(self) -> Handle, marker::Edge> { unsafe { Handle::new_edge(self.node, self.idx + 1) } } } @@ -815,7 +817,9 @@ impl Handle, HandleType> { /// Temporarily takes out another immutable handle on the same location. - pub fn reborrow(&self) -> Handle, K, V, NodeType>, HandleType> { + pub(super) fn reborrow( + &self, + ) -> Handle, K, V, NodeType>, HandleType> { // We can't use Handle::new_kv or Handle::new_edge because we don't know our type Handle { node: self.node.reborrow(), idx: self.idx, _marker: PhantomData } } @@ -827,7 +831,7 @@ impl<'a, K, V, NodeType, HandleType> Handle, K, V, NodeT /// dangerous. /// /// For details, see `NodeRef::reborrow_mut`. - pub unsafe fn reborrow_mut( + pub(super) unsafe fn reborrow_mut( &mut self, ) -> Handle, K, V, NodeType>, HandleType> { // We can't use Handle::new_kv or Handle::new_edge because we don't know our type @@ -837,7 +841,9 @@ impl<'a, K, V, NodeType, HandleType> Handle, K, V, NodeT /// Returns a dormant copy of this handle which can be reawakened later. /// /// See `DormantMutRef` for more details. - pub fn dormant(&self) -> Handle, HandleType> { + pub(super) fn dormant( + &self, + ) -> Handle, HandleType> { Handle { node: self.node.dormant(), idx: self.idx, _marker: PhantomData } } } @@ -849,7 +855,9 @@ impl Handle(self) -> Handle, K, V, NodeType>, HandleType> { + pub(super) unsafe fn awaken<'a>( + self, + ) -> Handle, K, V, NodeType>, HandleType> { Handle { node: unsafe { self.node.awaken() }, idx: self.idx, _marker: PhantomData } } } @@ -857,13 +865,15 @@ impl Handle Handle, marker::Edge> { /// Creates a new handle to an edge in `node`. /// Unsafe because the caller must ensure that `idx <= node.len()`. - pub unsafe fn new_edge(node: NodeRef, idx: usize) -> Self { + pub(super) unsafe fn new_edge(node: NodeRef, idx: usize) -> Self { debug_assert!(idx <= node.len()); Handle { node, idx, _marker: PhantomData } } - pub fn left_kv(self) -> Result, marker::KV>, Self> { + pub(super) fn left_kv( + self, + ) -> Result, marker::KV>, Self> { if self.idx > 0 { Ok(unsafe { Handle::new_kv(self.node, self.idx - 1) }) } else { @@ -871,7 +881,9 @@ impl Handle, mar } } - pub fn right_kv(self) -> Result, marker::KV>, Self> { + pub(super) fn right_kv( + self, + ) -> Result, marker::KV>, Self> { if self.idx < self.node.len() { Ok(unsafe { Handle::new_kv(self.node, self.idx) }) } else { @@ -880,7 +892,7 @@ impl Handle, mar } } -pub enum LeftOrRight { +pub(super) enum LeftOrRight { Left(T), Right(T), } @@ -1034,7 +1046,7 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark /// If the returned result is some `SplitResult`, the `left` field will be the root node. /// The returned pointer points to the inserted value, which in the case of `SplitResult` /// is in the `left` or `right` tree. - pub fn insert_recursing( + pub(super) fn insert_recursing( self, key: K, value: V, @@ -1078,7 +1090,7 @@ impl /// /// `edge.descend().ascend().unwrap()` and `node.ascend().unwrap().descend()` should /// both, upon success, do nothing. - pub fn descend(self) -> NodeRef { + pub(super) fn descend(self) -> NodeRef { const { assert!(BorrowType::TRAVERSAL_PERMIT); } @@ -1097,7 +1109,7 @@ impl } impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { - pub fn into_kv(self) -> (&'a K, &'a V) { + pub(super) fn into_kv(self) -> (&'a K, &'a V) { debug_assert!(self.idx < self.node.len()); let leaf = self.node.into_leaf(); let k = unsafe { leaf.keys.get_unchecked(self.idx).assume_init_ref() }; @@ -1107,17 +1119,17 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeTyp } impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { - pub fn key_mut(&mut self) -> &mut K { + pub(super) fn key_mut(&mut self) -> &mut K { unsafe { self.node.key_area_mut(self.idx).assume_init_mut() } } - pub fn into_val_mut(self) -> &'a mut V { + pub(super) fn into_val_mut(self) -> &'a mut V { debug_assert!(self.idx < self.node.len()); let leaf = self.node.into_leaf_mut(); unsafe { leaf.vals.get_unchecked_mut(self.idx).assume_init_mut() } } - pub fn into_kv_mut(self) -> (&'a mut K, &'a mut V) { + pub(super) fn into_kv_mut(self) -> (&'a mut K, &'a mut V) { debug_assert!(self.idx < self.node.len()); let leaf = self.node.into_leaf_mut(); let k = unsafe { leaf.keys.get_unchecked_mut(self.idx).assume_init_mut() }; @@ -1127,13 +1139,13 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType> } impl<'a, K, V, NodeType> Handle, K, V, NodeType>, marker::KV> { - pub fn into_kv_valmut(self) -> (&'a K, &'a mut V) { + pub(super) fn into_kv_valmut(self) -> (&'a K, &'a mut V) { unsafe { self.node.into_key_val_mut_at(self.idx) } } } impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType>, marker::KV> { - pub fn kv_mut(&mut self) -> (&mut K, &mut V) { + pub(super) fn kv_mut(&mut self) -> (&mut K, &mut V) { debug_assert!(self.idx < self.node.len()); // We cannot call separate key and value methods, because calling the second one // invalidates the reference returned by the first. @@ -1146,7 +1158,7 @@ impl<'a, K: 'a, V: 'a, NodeType> Handle, K, V, NodeType> } /// Replaces the key and value that the KV handle refers to. - pub fn replace_kv(&mut self, k: K, v: V) -> (K, V) { + pub(super) fn replace_kv(&mut self, k: K, v: V) -> (K, V) { let (key, val) = self.kv_mut(); (mem::replace(key, k), mem::replace(val, v)) } @@ -1156,7 +1168,7 @@ impl Handle, marker::KV> /// Extracts the key and value that the KV handle refers to. /// # Safety /// The node that the handle refers to must not yet have been deallocated. - pub unsafe fn into_key_val(mut self) -> (K, V) { + pub(super) unsafe fn into_key_val(mut self) -> (K, V) { debug_assert!(self.idx < self.node.len()); let leaf = self.node.as_leaf_dying(); unsafe { @@ -1170,7 +1182,7 @@ impl Handle, marker::KV> /// # Safety /// The node that the handle refers to must not yet have been deallocated. #[inline] - pub unsafe fn drop_key_val(mut self) { + pub(super) unsafe fn drop_key_val(mut self) { // Run the destructor of the value even if the destructor of the key panics. struct Dropper<'a, T>(&'a mut MaybeUninit); impl Drop for Dropper<'_, T> { @@ -1229,7 +1241,10 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark /// - The key and value pointed to by this handle are extracted. /// - All the key-value pairs to the right of this handle are put into a newly /// allocated node. - pub fn split(mut self, alloc: A) -> SplitResult<'a, K, V, marker::Leaf> { + pub(super) fn split( + mut self, + alloc: A, + ) -> SplitResult<'a, K, V, marker::Leaf> { let mut new_node = LeafNode::new(alloc); let kv = self.split_leaf_data(&mut new_node); @@ -1240,7 +1255,7 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Leaf>, mark /// Removes the key-value pair pointed to by this handle and returns it, along with the edge /// that the key-value pair collapsed into. - pub fn remove( + pub(super) fn remove( mut self, ) -> ((K, V), Handle, K, V, marker::Leaf>, marker::Edge>) { let old_len = self.node.len(); @@ -1261,7 +1276,7 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, /// - The key and value pointed to by this handle are extracted. /// - All the edges and key-value pairs to the right of this handle are put into /// a newly allocated node. - pub fn split( + pub(super) fn split( mut self, alloc: A, ) -> SplitResult<'a, K, V, marker::Internal> { @@ -1285,14 +1300,14 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::Internal>, /// Represents a session for evaluating and performing a balancing operation /// around an internal key-value pair. -pub struct BalancingContext<'a, K, V> { +pub(super) struct BalancingContext<'a, K, V> { parent: Handle, K, V, marker::Internal>, marker::KV>, left_child: NodeRef, K, V, marker::LeafOrInternal>, right_child: NodeRef, K, V, marker::LeafOrInternal>, } impl<'a, K, V> Handle, K, V, marker::Internal>, marker::KV> { - pub fn consider_for_balancing(self) -> BalancingContext<'a, K, V> { + pub(super) fn consider_for_balancing(self) -> BalancingContext<'a, K, V> { let self1 = unsafe { ptr::read(&self) }; let self2 = unsafe { ptr::read(&self) }; BalancingContext { @@ -1318,7 +1333,7 @@ impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { /// typically faster, since we only need to shift the node's N elements to /// the right, instead of shifting at least N of the sibling's elements to /// the left. - pub fn choose_parent_kv(self) -> Result>, Self> { + pub(super) fn choose_parent_kv(self) -> Result>, Self> { match unsafe { ptr::read(&self) }.ascend() { Ok(parent_edge) => match parent_edge.left_kv() { Ok(left_parent_kv) => Ok(LeftOrRight::Left(BalancingContext { @@ -1341,25 +1356,25 @@ impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { } impl<'a, K, V> BalancingContext<'a, K, V> { - pub fn left_child_len(&self) -> usize { + pub(super) fn left_child_len(&self) -> usize { self.left_child.len() } - pub fn right_child_len(&self) -> usize { + pub(super) fn right_child_len(&self) -> usize { self.right_child.len() } - pub fn into_left_child(self) -> NodeRef, K, V, marker::LeafOrInternal> { + pub(super) fn into_left_child(self) -> NodeRef, K, V, marker::LeafOrInternal> { self.left_child } - pub fn into_right_child(self) -> NodeRef, K, V, marker::LeafOrInternal> { + pub(super) fn into_right_child(self) -> NodeRef, K, V, marker::LeafOrInternal> { self.right_child } /// Returns whether merging is possible, i.e., whether there is enough room /// in a node to combine the central KV with both adjacent child nodes. - pub fn can_merge(&self) -> bool { + pub(super) fn can_merge(&self) -> bool { self.left_child.len() + 1 + self.right_child.len() <= CAPACITY } } @@ -1433,7 +1448,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// the left child node and returns the shrunk parent node. /// /// Panics unless we `.can_merge()`. - pub fn merge_tracking_parent( + pub(super) fn merge_tracking_parent( self, alloc: A, ) -> NodeRef, K, V, marker::Internal> { @@ -1444,7 +1459,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// the left child node and returns that child node. /// /// Panics unless we `.can_merge()`. - pub fn merge_tracking_child( + pub(super) fn merge_tracking_child( self, alloc: A, ) -> NodeRef, K, V, marker::LeafOrInternal> { @@ -1456,7 +1471,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// where the tracked child edge ended up, /// /// Panics unless we `.can_merge()`. - pub fn merge_tracking_child_edge( + pub(super) fn merge_tracking_child_edge( self, track_edge_idx: LeftOrRight, alloc: A, @@ -1479,7 +1494,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// of the parent, while pushing the old parent key-value pair into the right child. /// Returns a handle to the edge in the right child corresponding to where the original /// edge specified by `track_right_edge_idx` ended up. - pub fn steal_left( + pub(super) fn steal_left( mut self, track_right_edge_idx: usize, ) -> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { @@ -1491,7 +1506,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { /// of the parent, while pushing the old parent key-value pair onto the left child. /// Returns a handle to the edge in the left child specified by `track_left_edge_idx`, /// which didn't move. - pub fn steal_right( + pub(super) fn steal_right( mut self, track_left_edge_idx: usize, ) -> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { @@ -1500,7 +1515,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { } /// This does stealing similar to `steal_left` but steals multiple elements at once. - pub fn bulk_steal_left(&mut self, count: usize) { + pub(super) fn bulk_steal_left(&mut self, count: usize) { assert!(count > 0); unsafe { let left_node = &mut self.left_child; @@ -1563,7 +1578,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { } /// The symmetric clone of `bulk_steal_left`. - pub fn bulk_steal_right(&mut self, count: usize) { + pub(super) fn bulk_steal_right(&mut self, count: usize) { assert!(count > 0); unsafe { let left_node = &mut self.left_child; @@ -1628,7 +1643,7 @@ impl<'a, K: 'a, V: 'a> BalancingContext<'a, K, V> { } impl Handle, marker::Edge> { - pub fn forget_node_type( + pub(super) fn forget_node_type( self, ) -> Handle, marker::Edge> { unsafe { Handle::new_edge(self.node.forget_type(), self.idx) } @@ -1636,7 +1651,7 @@ impl Handle, marker::E } impl Handle, marker::Edge> { - pub fn forget_node_type( + pub(super) fn forget_node_type( self, ) -> Handle, marker::Edge> { unsafe { Handle::new_edge(self.node.forget_type(), self.idx) } @@ -1644,7 +1659,7 @@ impl Handle, marke } impl Handle, marker::KV> { - pub fn forget_node_type( + pub(super) fn forget_node_type( self, ) -> Handle, marker::KV> { unsafe { Handle::new_kv(self.node.forget_type(), self.idx) } @@ -1653,7 +1668,7 @@ impl Handle, marker::K impl Handle, Type> { /// Checks whether the underlying node is an `Internal` node or a `Leaf` node. - pub fn force( + pub(super) fn force( self, ) -> ForceResult< Handle, Type>, @@ -1672,7 +1687,7 @@ impl Handle Handle, K, V, marker::LeafOrInternal>, Type> { /// Unsafely asserts to the compiler the static information that the handle's node is a `Leaf`. - pub unsafe fn cast_to_leaf_unchecked( + pub(super) unsafe fn cast_to_leaf_unchecked( self, ) -> Handle, K, V, marker::Leaf>, Type> { let node = unsafe { self.node.cast_to_leaf_unchecked() }; @@ -1683,7 +1698,7 @@ impl<'a, K, V, Type> Handle, K, V, marker::LeafOrInterna impl<'a, K, V> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { /// Move the suffix after `self` from one node to another one. `right` must be empty. /// The first edge of `right` remains unchanged. - pub fn move_suffix( + pub(super) fn move_suffix( &mut self, right: &mut NodeRef, K, V, marker::LeafOrInternal>, ) { @@ -1726,13 +1741,13 @@ impl<'a, K, V> Handle, K, V, marker::LeafOrInternal>, ma } } -pub enum ForceResult { +pub(super) enum ForceResult { Leaf(Leaf), Internal(Internal), } /// Result of insertion, when a node needed to expand beyond its capacity. -pub struct SplitResult<'a, K, V, NodeType> { +pub(super) struct SplitResult<'a, K, V, NodeType> { // Altered node in existing tree with elements and edges that belong to the left of `kv`. pub left: NodeRef, K, V, NodeType>, // Some key and value that existed before and were split off, to be inserted elsewhere. @@ -1742,32 +1757,32 @@ pub struct SplitResult<'a, K, V, NodeType> { } impl<'a, K, V> SplitResult<'a, K, V, marker::Leaf> { - pub fn forget_node_type(self) -> SplitResult<'a, K, V, marker::LeafOrInternal> { + pub(super) fn forget_node_type(self) -> SplitResult<'a, K, V, marker::LeafOrInternal> { SplitResult { left: self.left.forget_type(), kv: self.kv, right: self.right.forget_type() } } } impl<'a, K, V> SplitResult<'a, K, V, marker::Internal> { - pub fn forget_node_type(self) -> SplitResult<'a, K, V, marker::LeafOrInternal> { + pub(super) fn forget_node_type(self) -> SplitResult<'a, K, V, marker::LeafOrInternal> { SplitResult { left: self.left.forget_type(), kv: self.kv, right: self.right.forget_type() } } } -pub mod marker { +pub(super) mod marker { use core::marker::PhantomData; - pub enum Leaf {} - pub enum Internal {} - pub enum LeafOrInternal {} + pub(crate) enum Leaf {} + pub(crate) enum Internal {} + pub(crate) enum LeafOrInternal {} - pub enum Owned {} - pub enum Dying {} - pub enum DormantMut {} - pub struct Immut<'a>(PhantomData<&'a ()>); - pub struct Mut<'a>(PhantomData<&'a mut ()>); - pub struct ValMut<'a>(PhantomData<&'a mut ()>); + pub(crate) enum Owned {} + pub(crate) enum Dying {} + pub(crate) enum DormantMut {} + pub(crate) struct Immut<'a>(PhantomData<&'a ()>); + pub(crate) struct Mut<'a>(PhantomData<&'a mut ()>); + pub(crate) struct ValMut<'a>(PhantomData<&'a mut ()>); - pub trait BorrowType { + pub(crate) trait BorrowType { /// If node references of this borrow type allow traversing to other /// nodes in the tree, this constant is set to `true`. It can be used /// for a compile-time assertion. @@ -1786,8 +1801,8 @@ pub mod marker { impl<'a> BorrowType for ValMut<'a> {} impl BorrowType for DormantMut {} - pub enum KV {} - pub enum Edge {} + pub(crate) enum KV {} + pub(crate) enum Edge {} } /// Inserts a value into a slice of initialized elements followed by one uninitialized element. diff --git a/library/alloc/src/collections/btree/node/tests.rs b/library/alloc/src/collections/btree/node/tests.rs index 4d2fa0f094171..ecd009f11c71a 100644 --- a/library/alloc/src/collections/btree/node/tests.rs +++ b/library/alloc/src/collections/btree/node/tests.rs @@ -6,7 +6,7 @@ use crate::string::String; impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> { // Asserts that the back pointer in each reachable node points to its parent. - pub fn assert_back_pointers(self) { + pub(crate) fn assert_back_pointers(self) { if let ForceResult::Internal(node) = self.force() { for idx in 0..=node.len() { let edge = unsafe { Handle::new_edge(node, idx) }; @@ -20,7 +20,7 @@ impl<'a, K: 'a, V: 'a> NodeRef, K, V, marker::LeafOrInternal> // Renders a multi-line display of the keys in order and in tree hierarchy, // picturing the tree growing sideways from its root on the left to its // leaves on the right. - pub fn dump_keys(self) -> String + pub(crate) fn dump_keys(self) -> String where K: Debug, { diff --git a/library/alloc/src/collections/btree/remove.rs b/library/alloc/src/collections/btree/remove.rs index 56f2824b782bd..9d870b86f34a0 100644 --- a/library/alloc/src/collections/btree/remove.rs +++ b/library/alloc/src/collections/btree/remove.rs @@ -10,7 +10,7 @@ impl<'a, K: 'a, V: 'a> Handle, K, V, marker::LeafOrInter /// the leaf edge corresponding to that former pair. It's possible this empties /// a root node that is internal, which the caller should pop from the map /// holding the tree. The caller should also decrement the map's length. - pub fn remove_kv_tracking( + pub(super) fn remove_kv_tracking( self, handle_emptied_internal_root: F, alloc: A, diff --git a/library/alloc/src/collections/btree/search.rs b/library/alloc/src/collections/btree/search.rs index 22e015edac3d2..96e5bf108024b 100644 --- a/library/alloc/src/collections/btree/search.rs +++ b/library/alloc/src/collections/btree/search.rs @@ -8,7 +8,7 @@ use SearchResult::*; use super::node::ForceResult::*; use super::node::{Handle, NodeRef, marker}; -pub enum SearchBound { +pub(super) enum SearchBound { /// An inclusive bound to look for, just like `Bound::Included(T)`. Included(T), /// An exclusive bound to look for, just like `Bound::Excluded(T)`. @@ -20,7 +20,7 @@ pub enum SearchBound { } impl SearchBound { - pub fn from_range(range_bound: Bound) -> Self { + pub(super) fn from_range(range_bound: Bound) -> Self { match range_bound { Bound::Included(t) => Included(t), Bound::Excluded(t) => Excluded(t), @@ -29,12 +29,12 @@ impl SearchBound { } } -pub enum SearchResult { +pub(super) enum SearchResult { Found(Handle, marker::KV>), GoDown(Handle, marker::Edge>), } -pub enum IndexResult { +pub(super) enum IndexResult { KV(usize), Edge(usize), } @@ -46,7 +46,7 @@ impl NodeRef( + pub(super) fn search_tree( mut self, key: &Q, ) -> SearchResult @@ -80,7 +80,7 @@ impl NodeRef( + pub(super) fn search_tree_for_bifurcation<'r, Q: ?Sized, R>( mut self, range: &'r R, ) -> Result< @@ -156,7 +156,7 @@ impl NodeRef( + pub(super) fn find_lower_bound_edge<'r, Q>( self, bound: SearchBound<&'r Q>, ) -> (Handle, SearchBound<&'r Q>) @@ -170,7 +170,7 @@ impl NodeRef( + pub(super) fn find_upper_bound_edge<'r, Q>( self, bound: SearchBound<&'r Q>, ) -> (Handle, SearchBound<&'r Q>) @@ -192,7 +192,10 @@ impl NodeRef { /// /// The result is meaningful only if the tree is ordered by key, like the tree /// in a `BTreeMap` is. - pub fn search_node(self, key: &Q) -> SearchResult + pub(super) fn search_node( + self, + key: &Q, + ) -> SearchResult where Q: Ord, K: Borrow, diff --git a/library/alloc/src/collections/btree/split.rs b/library/alloc/src/collections/btree/split.rs index c188ed1da6113..87a79e6cf3f93 100644 --- a/library/alloc/src/collections/btree/split.rs +++ b/library/alloc/src/collections/btree/split.rs @@ -8,7 +8,7 @@ use super::search::SearchResult::*; impl Root { /// Calculates the length of both trees that result from splitting up /// a given number of distinct key-value pairs. - pub fn calc_split_length( + pub(super) fn calc_split_length( total_num: usize, root_a: &Root, root_b: &Root, @@ -31,7 +31,11 @@ impl Root { /// and if the ordering of `Q` corresponds to that of `K`. /// If `self` respects all `BTreeMap` tree invariants, then both /// `self` and the returned tree will respect those invariants. - pub fn split_off(&mut self, key: &Q, alloc: A) -> Self + pub(super) fn split_off( + &mut self, + key: &Q, + alloc: A, + ) -> Self where K: Borrow, { diff --git a/library/alloc/src/collections/linked_list/tests.rs b/library/alloc/src/collections/linked_list/tests.rs index b7d4f8512a0f2..aa19239f6c55d 100644 --- a/library/alloc/src/collections/linked_list/tests.rs +++ b/library/alloc/src/collections/linked_list/tests.rs @@ -58,7 +58,7 @@ fn list_from(v: &[T]) -> LinkedList { v.iter().cloned().collect() } -pub fn check_links(list: &LinkedList) { +fn check_links(list: &LinkedList) { unsafe { let mut len = 0; let mut last_ptr: Option<&Node> = None; diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 1c33f8f60d824..299c8b8679e3d 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -823,6 +823,7 @@ impl VecDeque { /// assert!(buf.capacity() >= 11); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "vecdeque_reserve")] #[track_caller] pub fn reserve(&mut self, additional: usize) { let new_cap = self.len.checked_add(additional).expect("capacity overflow"); diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 28e4217e30394..0bb7c432cc35f 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -88,6 +88,7 @@ #![allow(rustdoc::redundant_explicit_links)] #![warn(rustdoc::unescaped_backticks)] #![deny(ffi_unwind_calls)] +#![warn(unreachable_pub)] // // Library features: // tidy-alphabetical-start @@ -227,7 +228,7 @@ pub mod alloc; pub mod boxed; #[cfg(test)] mod boxed { - pub use std::boxed::Box; + pub(crate) use std::boxed::Box; } pub mod borrow; #[unstable(feature = "bstr", issue = "134915")] diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs index ad86bf4bf072f..b80d1fc788947 100644 --- a/library/alloc/src/raw_vec.rs +++ b/library/alloc/src/raw_vec.rs @@ -97,7 +97,7 @@ impl RawVec { /// `RawVec` with capacity `usize::MAX`. Useful for implementing /// delayed allocation. #[must_use] - pub const fn new() -> Self { + pub(crate) const fn new() -> Self { Self::new_in(Global) } @@ -120,7 +120,7 @@ impl RawVec { #[must_use] #[inline] #[track_caller] - pub fn with_capacity(capacity: usize) -> Self { + pub(crate) fn with_capacity(capacity: usize) -> Self { Self { inner: RawVecInner::with_capacity(capacity, T::LAYOUT), _marker: PhantomData } } @@ -129,7 +129,7 @@ impl RawVec { #[must_use] #[inline] #[track_caller] - pub fn with_capacity_zeroed(capacity: usize) -> Self { + pub(crate) fn with_capacity_zeroed(capacity: usize) -> Self { Self { inner: RawVecInner::with_capacity_zeroed_in(capacity, Global, T::LAYOUT), _marker: PhantomData, @@ -172,7 +172,7 @@ impl RawVec { /// Like `new`, but parameterized over the choice of allocator for /// the returned `RawVec`. #[inline] - pub const fn new_in(alloc: A) -> Self { + pub(crate) const fn new_in(alloc: A) -> Self { Self { inner: RawVecInner::new_in(alloc, align_of::()), _marker: PhantomData } } @@ -181,7 +181,7 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] #[track_caller] - pub fn with_capacity_in(capacity: usize, alloc: A) -> Self { + pub(crate) fn with_capacity_in(capacity: usize, alloc: A) -> Self { Self { inner: RawVecInner::with_capacity_in(capacity, alloc, T::LAYOUT), _marker: PhantomData, @@ -191,7 +191,7 @@ impl RawVec { /// Like `try_with_capacity`, but parameterized over the choice of /// allocator for the returned `RawVec`. #[inline] - pub fn try_with_capacity_in(capacity: usize, alloc: A) -> Result { + pub(crate) fn try_with_capacity_in(capacity: usize, alloc: A) -> Result { match RawVecInner::try_with_capacity_in(capacity, alloc, T::LAYOUT) { Ok(inner) => Ok(Self { inner, _marker: PhantomData }), Err(e) => Err(e), @@ -203,7 +203,7 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] #[track_caller] - pub fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self { + pub(crate) fn with_capacity_zeroed_in(capacity: usize, alloc: A) -> Self { Self { inner: RawVecInner::with_capacity_zeroed_in(capacity, alloc, T::LAYOUT), _marker: PhantomData, @@ -222,7 +222,7 @@ impl RawVec { /// /// Note, that the requested capacity and `self.capacity()` could differ, as /// an allocator could overallocate and return a greater memory block than requested. - pub unsafe fn into_box(self, len: usize) -> Box<[MaybeUninit], A> { + pub(crate) unsafe fn into_box(self, len: usize) -> Box<[MaybeUninit], A> { // Sanity-check one half of the safety requirement (we cannot check the other half). debug_assert!( len <= self.capacity(), @@ -247,7 +247,7 @@ impl RawVec { /// If the `ptr` and `capacity` come from a `RawVec` created via `alloc`, then this is /// guaranteed. #[inline] - pub unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, alloc: A) -> Self { + pub(crate) unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, alloc: A) -> Self { // SAFETY: Precondition passed to the caller unsafe { let ptr = ptr.cast(); @@ -265,7 +265,7 @@ impl RawVec { /// /// See [`RawVec::from_raw_parts_in`]. #[inline] - pub unsafe fn from_nonnull_in(ptr: NonNull, capacity: usize, alloc: A) -> Self { + pub(crate) unsafe fn from_nonnull_in(ptr: NonNull, capacity: usize, alloc: A) -> Self { // SAFETY: Precondition passed to the caller unsafe { let ptr = ptr.cast(); @@ -278,12 +278,12 @@ impl RawVec { /// `Unique::dangling()` if `capacity == 0` or `T` is zero-sized. In the former case, you must /// be careful. #[inline] - pub const fn ptr(&self) -> *mut T { + pub(crate) const fn ptr(&self) -> *mut T { self.inner.ptr() } #[inline] - pub fn non_null(&self) -> NonNull { + pub(crate) fn non_null(&self) -> NonNull { self.inner.non_null() } @@ -291,13 +291,13 @@ impl RawVec { /// /// This will always be `usize::MAX` if `T` is zero-sized. #[inline] - pub const fn capacity(&self) -> usize { + pub(crate) const fn capacity(&self) -> usize { self.inner.capacity(size_of::()) } /// Returns a shared reference to the allocator backing this `RawVec`. #[inline] - pub fn allocator(&self) -> &A { + pub(crate) fn allocator(&self) -> &A { self.inner.allocator() } @@ -323,7 +323,7 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline] #[track_caller] - pub fn reserve(&mut self, len: usize, additional: usize) { + pub(crate) fn reserve(&mut self, len: usize, additional: usize) { self.inner.reserve(len, additional, T::LAYOUT) } @@ -332,12 +332,16 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[inline(never)] #[track_caller] - pub fn grow_one(&mut self) { + pub(crate) fn grow_one(&mut self) { self.inner.grow_one(T::LAYOUT) } /// The same as `reserve`, but returns on errors instead of panicking or aborting. - pub fn try_reserve(&mut self, len: usize, additional: usize) -> Result<(), TryReserveError> { + pub(crate) fn try_reserve( + &mut self, + len: usize, + additional: usize, + ) -> Result<(), TryReserveError> { self.inner.try_reserve(len, additional, T::LAYOUT) } @@ -360,12 +364,12 @@ impl RawVec { /// Aborts on OOM. #[cfg(not(no_global_oom_handling))] #[track_caller] - pub fn reserve_exact(&mut self, len: usize, additional: usize) { + pub(crate) fn reserve_exact(&mut self, len: usize, additional: usize) { self.inner.reserve_exact(len, additional, T::LAYOUT) } /// The same as `reserve_exact`, but returns on errors instead of panicking or aborting. - pub fn try_reserve_exact( + pub(crate) fn try_reserve_exact( &mut self, len: usize, additional: usize, @@ -386,7 +390,7 @@ impl RawVec { #[cfg(not(no_global_oom_handling))] #[track_caller] #[inline] - pub fn shrink_to_fit(&mut self, cap: usize) { + pub(crate) fn shrink_to_fit(&mut self, cap: usize) { self.inner.shrink_to_fit(cap, T::LAYOUT) } } diff --git a/library/alloc/src/slice.rs b/library/alloc/src/slice.rs index edc8d99f2f990..1cedead7aa243 100644 --- a/library/alloc/src/slice.rs +++ b/library/alloc/src/slice.rs @@ -85,6 +85,7 @@ use crate::vec::Vec; // functions are actually methods that are in `impl [T]` but not in // `core::slice::SliceExt` - we need to supply these functions for the // `test_permutations` test +#[allow(unreachable_pub)] // cfg(test) pub above pub(crate) mod hack { use core::alloc::Allocator; diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 8eee7cff2080d..431e19e6ef1d7 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -2740,7 +2740,7 @@ impl Weak { /// # Safety /// /// The pointer must have originated from the [`into_raw`] and must still own its potential - /// weak reference. + /// weak reference, and must point to a block of memory allocated by global allocator. /// /// It is allowed for the strong count to be 0 at the time of calling this. Nevertheless, this /// takes ownership of one weak reference currently represented as a raw pointer (the weak diff --git a/library/alloc/src/testing/crash_test.rs b/library/alloc/src/testing/crash_test.rs index 684bac60d9a86..8e00e4f41e5d6 100644 --- a/library/alloc/src/testing/crash_test.rs +++ b/library/alloc/src/testing/crash_test.rs @@ -11,7 +11,7 @@ use crate::fmt::Debug; // the `Debug` trait is the only thing we use from `crate /// Crash test dummies are identified and ordered by an id, so they can be used /// as keys in a BTreeMap. #[derive(Debug)] -pub struct CrashTestDummy { +pub(crate) struct CrashTestDummy { pub id: usize, cloned: AtomicUsize, dropped: AtomicUsize, @@ -20,7 +20,7 @@ pub struct CrashTestDummy { impl CrashTestDummy { /// Creates a crash test dummy design. The `id` determines order and equality of instances. - pub fn new(id: usize) -> CrashTestDummy { + pub(crate) fn new(id: usize) -> CrashTestDummy { CrashTestDummy { id, cloned: AtomicUsize::new(0), @@ -31,34 +31,34 @@ impl CrashTestDummy { /// Creates an instance of a crash test dummy that records what events it experiences /// and optionally panics. - pub fn spawn(&self, panic: Panic) -> Instance<'_> { + pub(crate) fn spawn(&self, panic: Panic) -> Instance<'_> { Instance { origin: self, panic } } /// Returns how many times instances of the dummy have been cloned. - pub fn cloned(&self) -> usize { + pub(crate) fn cloned(&self) -> usize { self.cloned.load(SeqCst) } /// Returns how many times instances of the dummy have been dropped. - pub fn dropped(&self) -> usize { + pub(crate) fn dropped(&self) -> usize { self.dropped.load(SeqCst) } /// Returns how many times instances of the dummy have had their `query` member invoked. - pub fn queried(&self) -> usize { + pub(crate) fn queried(&self) -> usize { self.queried.load(SeqCst) } } #[derive(Debug)] -pub struct Instance<'a> { +pub(crate) struct Instance<'a> { origin: &'a CrashTestDummy, panic: Panic, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum Panic { +pub(crate) enum Panic { Never, InClone, InDrop, @@ -66,12 +66,12 @@ pub enum Panic { } impl Instance<'_> { - pub fn id(&self) -> usize { + pub(crate) fn id(&self) -> usize { self.origin.id } /// Some anonymous query, the result of which is already given. - pub fn query(&self, result: R) -> R { + pub(crate) fn query(&self, result: R) -> R { self.origin.queried.fetch_add(1, SeqCst); if self.panic == Panic::InQuery { panic!("panic in `query`"); diff --git a/library/alloc/src/testing/mod.rs b/library/alloc/src/testing/mod.rs index 7a094f8a59522..c8457daf93e5a 100644 --- a/library/alloc/src/testing/mod.rs +++ b/library/alloc/src/testing/mod.rs @@ -1,3 +1,3 @@ -pub mod crash_test; -pub mod ord_chaos; -pub mod rng; +pub(crate) mod crash_test; +pub(crate) mod ord_chaos; +pub(crate) mod rng; diff --git a/library/alloc/src/testing/ord_chaos.rs b/library/alloc/src/testing/ord_chaos.rs index 96ce7c1579046..55e1ae5e3deaa 100644 --- a/library/alloc/src/testing/ord_chaos.rs +++ b/library/alloc/src/testing/ord_chaos.rs @@ -4,7 +4,7 @@ use std::ptr; // Minimal type with an `Ord` implementation violating transitivity. #[derive(Debug)] -pub enum Cyclic3 { +pub(crate) enum Cyclic3 { A, B, C, @@ -37,16 +37,16 @@ impl Eq for Cyclic3 {} // Controls the ordering of values wrapped by `Governed`. #[derive(Debug)] -pub struct Governor { +pub(crate) struct Governor { flipped: Cell, } impl Governor { - pub fn new() -> Self { + pub(crate) fn new() -> Self { Governor { flipped: Cell::new(false) } } - pub fn flip(&self) { + pub(crate) fn flip(&self) { self.flipped.set(!self.flipped.get()); } } @@ -55,7 +55,7 @@ impl Governor { // (assuming that `T` respects total order), but can suddenly be made to invert // that total order. #[derive(Debug)] -pub struct Governed<'a, T>(pub T, pub &'a Governor); +pub(crate) struct Governed<'a, T>(pub T, pub &'a Governor); impl PartialOrd for Governed<'_, T> { fn partial_cmp(&self, other: &Self) -> Option { diff --git a/library/alloc/src/testing/rng.rs b/library/alloc/src/testing/rng.rs index ecf543bee035a..77d3348f38a5d 100644 --- a/library/alloc/src/testing/rng.rs +++ b/library/alloc/src/testing/rng.rs @@ -1,5 +1,5 @@ /// XorShiftRng -pub struct DeterministicRng { +pub(crate) struct DeterministicRng { count: usize, x: u32, y: u32, @@ -8,12 +8,12 @@ pub struct DeterministicRng { } impl DeterministicRng { - pub fn new() -> Self { + pub(crate) fn new() -> Self { DeterministicRng { count: 0, x: 0x193a6754, y: 0xa8a7d469, z: 0x97830e05, w: 0x113ba7bb } } /// Guarantees that each returned number is unique. - pub fn next(&mut self) -> u32 { + pub(crate) fn next(&mut self) -> u32 { self.count += 1; assert!(self.count <= 70029); let x = self.x; diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 54673ceb1da81..48afcf6e0645b 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1267,6 +1267,7 @@ impl Vec { #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] #[track_caller] + #[cfg_attr(not(test), rustc_diagnostic_item = "vec_reserve")] pub fn reserve(&mut self, additional: usize) { self.buf.reserve(self.len, additional); } diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 79d094556c45c..51687a3adcdd4 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -116,7 +116,6 @@ mod c_char_definition { // Section 2.1 "Basic Types" in MSP430 Embedded Application Binary // Interface says "The char type is unsigned by default". // https://www.ti.com/lit/an/slaa534a/slaa534a.pdf - // Note: this doesn't seem to match Clang's default (https://github.com/rust-lang/rust/issues/129945). // powerpc/powerpc64: // - PPC32 SysV: "Table 3-1 Scalar Types" in System V Application Binary Interface PowerPC // Processor Supplement says ANSI C char is unsigned byte @@ -139,8 +138,10 @@ mod c_char_definition { // https://github.com/IBM/s390x-abi/releases/tag/v1.6.1 // - z/OS: XL C/C++ Language Reference says: "By default, char behaves like an unsigned char." // https://www.ibm.com/docs/en/zos/3.1.0?topic=specifiers-character-types - // Xtensa: - // - "The char type is unsigned by default for Xtensa processors." + // xtensa: + // Section 2.17.1 "Data Types and Alignment" of Xtensa LX Microprocessor Overview handbook + // says "`char` type is unsigned by default". + // https://loboris.eu/ESP32/Xtensa_lx%20Overview%20handbook.pdf // // On the following operating systems, c_char is signed by default, regardless of architecture. // Darwin (macOS, iOS, etc.): @@ -150,11 +151,12 @@ mod c_char_definition { // Windows MSVC C++ Language Reference says "Microsoft-specific: Variables of type char // are promoted to int as if from type signed char by default, unless the /J compilation // option is used." - // https://learn.microsoft.com/en-us/cpp/cpp/fundamental-types-cpp?view=msvc-170#character-types) - // L4RE: + // https://learn.microsoft.com/en-us/cpp/cpp/fundamental-types-cpp?view=msvc-170#character-types + // L4Re: // The kernel builds with -funsigned-char on all targets (but useserspace follows the // architecture defaults). As we only have a target for userspace apps so there are no - // special cases for L4RE below. + // special cases for L4Re below. + // https://github.com/rust-lang/rust/pull/132975#issuecomment-2484645240 if #[cfg(all( not(windows), not(target_vendor = "apple"), @@ -166,8 +168,8 @@ mod c_char_definition { target_arch = "msp430", target_arch = "powerpc", target_arch = "powerpc64", - target_arch = "riscv64", target_arch = "riscv32", + target_arch = "riscv64", target_arch = "s390x", target_arch = "xtensa", ) diff --git a/library/core/src/iter/sources/from_fn.rs b/library/core/src/iter/sources/from_fn.rs index 5f3d404d7dca2..75cc0ffe3c77c 100644 --- a/library/core/src/iter/sources/from_fn.rs +++ b/library/core/src/iter/sources/from_fn.rs @@ -1,7 +1,7 @@ use crate::fmt; -/// Creates a new iterator where each iteration calls the provided closure -/// `F: FnMut() -> Option`. +/// Creates an iterator with the provided closure +/// `F: FnMut() -> Option` as its `[next](Iterator::next)` method. /// /// The iterator will yield the `T`s returned from the closure. /// diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 5c04e5a40df09..01a3c9d2ada72 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -313,6 +313,17 @@ pub macro cfg_match { /// } /// } /// ``` +/// +/// If desired, it is possible to return expressions through the use of surrounding braces: +/// +/// ``` +/// #![feature(cfg_match)] +/// +/// let _some_string = cfg_match! {{ +/// unix => { "With great power comes great electricity bills" } +/// _ => { "Behind every successful diet is an unwatched pizza" } +/// }}; +/// ``` #[cfg(not(bootstrap))] #[unstable(feature = "cfg_match", issue = "115585")] #[rustc_diagnostic_item = "cfg_match"] diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 01af964a83e26..a793fc2aa2e55 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -6,6 +6,13 @@ #![stable(feature = "rust1", since = "1.0.0")] +mod variance; + +#[unstable(feature = "phantom_variance_markers", issue = "135806")] +pub use self::variance::{ + PhantomContravariant, PhantomContravariantLifetime, PhantomCovariant, PhantomCovariantLifetime, + PhantomInvariant, PhantomInvariantLifetime, Variance, variance, +}; use crate::cell::UnsafeCell; use crate::cmp; use crate::fmt::Debug; diff --git a/library/core/src/marker/variance.rs b/library/core/src/marker/variance.rs new file mode 100644 index 0000000000000..23334e6575ddf --- /dev/null +++ b/library/core/src/marker/variance.rs @@ -0,0 +1,260 @@ +#![unstable(feature = "phantom_variance_markers", issue = "135806")] + +use super::PhantomData; +use crate::any::type_name; +use crate::cmp::Ordering; +use crate::fmt; +use crate::hash::{Hash, Hasher}; + +macro_rules! first_token { + ($first:tt $($rest:tt)*) => { + $first + }; +} + +macro_rules! phantom_type { + ($( + $(#[$attr:meta])* + pub struct $name:ident <$t:ident> ($($inner:tt)*); + )*) => {$( + $(#[$attr])* + pub struct $name<$t>($($inner)*) where T: ?Sized; + + impl $name + where T: ?Sized + { + /// Constructs a new instance of the variance marker. + pub const fn new() -> Self { + Self(PhantomData) + } + } + + impl self::sealed::Sealed for $name where T: ?Sized { + const VALUE: Self = Self::new(); + } + impl Variance for $name where T: ?Sized {} + + impl Default for $name + where T: ?Sized + { + fn default() -> Self { + Self(PhantomData) + } + } + + impl fmt::Debug for $name + where T: ?Sized + { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}<{}>", stringify!($name), type_name::()) + } + } + + impl Clone for $name + where T: ?Sized + { + fn clone(&self) -> Self { + *self + } + } + + impl Copy for $name where T: ?Sized {} + + impl PartialEq for $name + where T: ?Sized + { + fn eq(&self, _: &Self) -> bool { + true + } + } + + impl Eq for $name where T: ?Sized {} + + impl PartialOrd for $name + where T: ?Sized + { + fn partial_cmp(&self, _: &Self) -> Option { + Some(Ordering::Equal) + } + } + + impl Ord for $name + where T: ?Sized + { + fn cmp(&self, _: &Self) -> Ordering { + Ordering::Equal + } + } + + impl Hash for $name + where T: ?Sized + { + fn hash(&self, _: &mut H) {} + } + )*}; +} + +macro_rules! phantom_lifetime { + ($( + $(#[$attr:meta])* + pub struct $name:ident <$lt:lifetime> ($($inner:tt)*); + )*) => {$( + $(#[$attr])* + #[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] + pub struct $name<$lt>($($inner)*); + + impl $name<'_> { + /// Constructs a new instance of the variance marker. + pub const fn new() -> Self { + Self(first_token!($($inner)*)(PhantomData)) + } + } + + impl self::sealed::Sealed for $name<'_> { + const VALUE: Self = Self::new(); + } + impl Variance for $name<'_> {} + + impl fmt::Debug for $name<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", stringify!($name)) + } + } + )*}; +} + +phantom_lifetime! { + /// Zero-sized type used to mark a lifetime as covariant. + /// + /// Covariant lifetimes must live at least as long as declared. See [the reference][1] for more + /// information. + /// + /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance + /// + /// ## Layout + /// + /// For all `'a`, the following are guaranteed: + /// * `size_of::>() == 0` + /// * `align_of::>() == 1` + pub struct PhantomCovariantLifetime<'a>(PhantomCovariant<&'a ()>); + /// Zero-sized type used to mark a lifetime as contravariant. + /// + /// Contravariant lifetimes must live at most as long as declared. See [the reference][1] for + /// more information. + /// + /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance + /// + /// ## Layout + /// + /// For all `'a`, the following are guaranteed: + /// * `size_of::>() == 0` + /// * `align_of::>() == 1` + pub struct PhantomContravariantLifetime<'a>(PhantomContravariant<&'a ()>); + /// Zero-sized type used to mark a lifetime as invariant. + /// + /// Invariant lifetimes must be live for the exact length declared, neither shorter nor longer. + /// See [the reference][1] for more information. + /// + /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance + /// + /// ## Layout + /// + /// For all `'a`, the following are guaranteed: + /// * `size_of::>() == 0` + /// * `align_of::>() == 1` + pub struct PhantomInvariantLifetime<'a>(PhantomInvariant<&'a ()>); +} + +phantom_type! { + /// Zero-sized type used to mark a type parameter as covariant. + /// + /// Types used as part of the return value from a function are covariant. If the type is _also_ + /// passed as a parameter then it is [invariant][PhantomInvariant]. See [the reference][1] for + /// more information. + /// + /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance + /// + /// ## Layout + /// + /// For all `T`, the following are guaranteed: + /// * `size_of::>() == 0` + /// * `align_of::>() == 1` + pub struct PhantomCovariant(PhantomData T>); + /// Zero-sized type used to mark a type parameter as contravariant. + /// + /// Types passed as arguments to a function are contravariant. If the type is _also_ part of the + /// return value from a function then it is [invariant][PhantomInvariant]. See [the + /// reference][1] for more information. + /// + /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance + /// + /// ## Layout + /// + /// For all `T`, the following are guaranteed: + /// * `size_of::>() == 0` + /// * `align_of::>() == 1` + pub struct PhantomContravariant(PhantomData); + /// Zero-sized type used to mark a type parameter as invariant. + /// + /// Types that are both passed as an argument _and_ used as part of the return value from a + /// function are invariant. See [the reference][1] for more information. + /// + /// [1]: https://doc.rust-lang.org/stable/reference/subtyping.html#variance + /// + /// ## Layout + /// + /// For all `T`, the following are guaranteed: + /// * `size_of::>() == 0` + /// * `align_of::>() == 1` + pub struct PhantomInvariant(PhantomData T>); +} + +mod sealed { + pub trait Sealed { + const VALUE: Self; + } +} + +/// A marker trait for phantom variance types. +pub trait Variance: sealed::Sealed + Default {} + +/// Construct a variance marker; equivalent to [`Default::default`]. +/// +/// This type can be any of the following. You generally should not need to explicitly name the +/// type, however. +/// +/// - [`PhantomCovariant`] +/// - [`PhantomContravariant`] +/// - [`PhantomInvariant`] +/// - [`PhantomCovariantLifetime`] +/// - [`PhantomContravariantLifetime`] +/// - [`PhantomInvariantLifetime`] +/// +/// # Example +/// +/// ```rust +/// #![feature(phantom_variance_markers)] +/// +/// use core::marker::{PhantomCovariant, variance}; +/// +/// struct BoundFn +/// where +/// F: Fn(P) -> R, +/// { +/// function: F, +/// parameter: P, +/// return_value: PhantomCovariant, +/// } +/// +/// let bound_fn = BoundFn { +/// function: core::convert::identity, +/// parameter: 5u8, +/// return_value: variance(), +/// }; +/// ``` +pub const fn variance() -> T +where + T: Variance, +{ + T::VALUE +} diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index ba5746d0adea1..1993a7491e108 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -1099,10 +1099,15 @@ impl [T] { /// assert!(iter.next().is_none()); /// ``` /// - /// There's no `windows_mut`, as that existing would let safe code violate the - /// "only one `&mut` at a time to the same thing" rule. However, you can sometimes - /// use [`Cell::as_slice_of_cells`](crate::cell::Cell::as_slice_of_cells) in - /// conjunction with `windows` to accomplish something similar: + /// Because the [Iterator] trait cannot represent the required lifetimes, + /// there is no `windows_mut` analog to `windows`; + /// `[0,1,2].windows_mut(2).collect()` would violate [the rules of references] + /// (though a [LendingIterator] analog is possible). You can sometimes use + /// [`Cell::as_slice_of_cells`](crate::cell::Cell::as_slice_of_cells) in + /// conjunction with `windows` instead: + /// + /// [the rules of references]: https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#the-rules-of-references + /// [LendingIterator]: https://blog.rust-lang.org/2022/10/28/gats-stabilization.html /// ``` /// use std::cell::Cell; /// diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 859ac1632305b..0061e33f98647 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -1164,7 +1164,7 @@ impl AtomicBool { /// /// # Considerations /// - /// This method is not magic; it is not provided by the hardware. + /// This method is not magic; it is not provided by the hardware. /// It is implemented in terms of [`AtomicBool::compare_exchange_weak`], and suffers from the same drawbacks. /// In particular, this method will not circumvent the [ABA Problem]. /// @@ -1203,6 +1203,125 @@ impl AtomicBool { } Err(prev) } + + /// Fetches the value, and applies a function to it that returns an optional + /// new value. Returns a `Result` of `Ok(previous_value)` if the function + /// returned `Some(_)`, else `Err(previous_value)`. + /// + /// See also: [`update`](`AtomicBool::update`). + /// + /// Note: This may call the function multiple times if the value has been + /// changed from other threads in the meantime, as long as the function + /// returns `Some(_)`, but the function will have been applied only once to + /// the stored value. + /// + /// `try_update` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. The first describes the required ordering for + /// when the operation finally succeeds while the second describes the + /// required ordering for loads. These correspond to the success and failure + /// orderings of [`AtomicBool::compare_exchange`] respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part of this + /// operation [`Relaxed`], and using [`Release`] makes the final successful + /// load [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], + /// [`Acquire`] or [`Relaxed`]. + /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on `u8`. + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of [`AtomicBool::compare_exchange_weak`], and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + /// use std::sync::atomic::{AtomicBool, Ordering}; + /// + /// let x = AtomicBool::new(false); + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(false)); + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(!x)), Ok(false)); + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(!x)), Ok(true)); + /// assert_eq!(x.load(Ordering::SeqCst), false); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[cfg(target_has_atomic = "8")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn try_update( + &self, + set_order: Ordering, + fetch_order: Ordering, + f: impl FnMut(bool) -> Option, + ) -> Result { + // FIXME(atomic_try_update): this is currently an unstable alias to `fetch_update`; + // when stabilizing, turn `fetch_update` into a deprecated alias to `try_update`. + self.fetch_update(set_order, fetch_order, f) + } + + /// Fetches the value, applies a function to it that it return a new value. + /// The new value is stored and the old value is returned. + /// + /// See also: [`try_update`](`AtomicBool::try_update`). + /// + /// Note: This may call the function multiple times if the value has been changed from other threads in + /// the meantime, but the function will have been applied only once to the stored value. + /// + /// `update` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. The first describes the required ordering for + /// when the operation finally succeeds while the second describes the + /// required ordering for loads. These correspond to the success and failure + /// orderings of [`AtomicBool::compare_exchange`] respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the final successful load + /// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`]. + /// + /// **Note:** This method is only available on platforms that support atomic operations on `u8`. + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of [`AtomicBool::compare_exchange_weak`], and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + /// + /// use std::sync::atomic::{AtomicBool, Ordering}; + /// + /// let x = AtomicBool::new(false); + /// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |x| !x), false); + /// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |x| !x), true); + /// assert_eq!(x.load(Ordering::SeqCst), false); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[cfg(target_has_atomic = "8")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn update( + &self, + set_order: Ordering, + fetch_order: Ordering, + mut f: impl FnMut(bool) -> bool, + ) -> bool { + let mut prev = self.load(fetch_order); + loop { + match self.compare_exchange_weak(prev, f(prev), set_order, fetch_order) { + Ok(x) => break x, + Err(next_prev) => prev = next_prev, + } + } + } } #[cfg(target_has_atomic_load_store = "ptr")] @@ -1684,7 +1803,7 @@ impl AtomicPtr { /// /// # Considerations /// - /// This method is not magic; it is not provided by the hardware. + /// This method is not magic; it is not provided by the hardware. /// It is implemented in terms of [`AtomicPtr::compare_exchange_weak`], and suffers from the same drawbacks. /// In particular, this method will not circumvent the [ABA Problem]. /// @@ -1732,6 +1851,137 @@ impl AtomicPtr { } Err(prev) } + /// Fetches the value, and applies a function to it that returns an optional + /// new value. Returns a `Result` of `Ok(previous_value)` if the function + /// returned `Some(_)`, else `Err(previous_value)`. + /// + /// See also: [`update`](`AtomicPtr::update`). + /// + /// Note: This may call the function multiple times if the value has been + /// changed from other threads in the meantime, as long as the function + /// returns `Some(_)`, but the function will have been applied only once to + /// the stored value. + /// + /// `try_update` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. The first describes the required ordering for + /// when the operation finally succeeds while the second describes the + /// required ordering for loads. These correspond to the success and failure + /// orderings of [`AtomicPtr::compare_exchange`] respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part of this + /// operation [`Relaxed`], and using [`Release`] makes the final successful + /// load [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], + /// [`Acquire`] or [`Relaxed`]. + /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on pointers. + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of [`AtomicPtr::compare_exchange_weak`], and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + /// use std::sync::atomic::{AtomicPtr, Ordering}; + /// + /// let ptr: *mut _ = &mut 5; + /// let some_ptr = AtomicPtr::new(ptr); + /// + /// let new: *mut _ = &mut 10; + /// assert_eq!(some_ptr.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(ptr)); + /// let result = some_ptr.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| { + /// if x == ptr { + /// Some(new) + /// } else { + /// None + /// } + /// }); + /// assert_eq!(result, Ok(ptr)); + /// assert_eq!(some_ptr.load(Ordering::SeqCst), new); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[cfg(target_has_atomic = "ptr")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn try_update( + &self, + set_order: Ordering, + fetch_order: Ordering, + f: impl FnMut(*mut T) -> Option<*mut T>, + ) -> Result<*mut T, *mut T> { + // FIXME(atomic_try_update): this is currently an unstable alias to `fetch_update`; + // when stabilizing, turn `fetch_update` into a deprecated alias to `try_update`. + self.fetch_update(set_order, fetch_order, f) + } + + /// Fetches the value, applies a function to it that it return a new value. + /// The new value is stored and the old value is returned. + /// + /// See also: [`try_update`](`AtomicPtr::try_update`). + /// + /// Note: This may call the function multiple times if the value has been changed from other threads in + /// the meantime, but the function will have been applied only once to the stored value. + /// + /// `update` takes two [`Ordering`] arguments to describe the memory + /// ordering of this operation. The first describes the required ordering for + /// when the operation finally succeeds while the second describes the + /// required ordering for loads. These correspond to the success and failure + /// orderings of [`AtomicPtr::compare_exchange`] respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the final successful load + /// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`]. + /// + /// **Note:** This method is only available on platforms that support atomic + /// operations on pointers. + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of [`AtomicPtr::compare_exchange_weak`], and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + /// + /// use std::sync::atomic::{AtomicPtr, Ordering}; + /// + /// let ptr: *mut _ = &mut 5; + /// let some_ptr = AtomicPtr::new(ptr); + /// + /// let new: *mut _ = &mut 10; + /// let result = some_ptr.update(Ordering::SeqCst, Ordering::SeqCst, |_| new); + /// assert_eq!(result, ptr); + /// assert_eq!(some_ptr.load(Ordering::SeqCst), new); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[cfg(target_has_atomic = "8")] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn update( + &self, + set_order: Ordering, + fetch_order: Ordering, + mut f: impl FnMut(*mut T) -> *mut T, + ) -> *mut T { + let mut prev = self.load(fetch_order); + loop { + match self.compare_exchange_weak(prev, f(prev), set_order, fetch_order) { + Ok(x) => break x, + Err(next_prev) => prev = next_prev, + } + } + } /// Offsets the pointer's address by adding `val` (in units of `T`), /// returning the previous pointer. @@ -2297,7 +2547,7 @@ macro_rules! atomic_int { $int_type, no = [ "**Note:** This function is only available on targets where `", - stringify!($int_type), "` has an alignment of ", $align, " bytes." + stringify!($atomic_type), "` has the same alignment as `", stringify!($int_type), "`." ], }] /// @@ -2875,7 +3125,7 @@ macro_rules! atomic_int { /// /// # Considerations /// - /// This method is not magic; it is not provided by the hardware. + /// This method is not magic; it is not provided by the hardware. /// It is implemented in terms of #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange_weak`],")] /// and suffers from the same drawbacks. @@ -2913,6 +3163,127 @@ macro_rules! atomic_int { Err(prev) } + /// Fetches the value, and applies a function to it that returns an optional + /// new value. Returns a `Result` of `Ok(previous_value)` if the function returned `Some(_)`, else + /// `Err(previous_value)`. + /// + #[doc = concat!("See also: [`update`](`", stringify!($atomic_type), "::update`).")] + /// + /// Note: This may call the function multiple times if the value has been changed from other threads in + /// the meantime, as long as the function returns `Some(_)`, but the function will have been applied + /// only once to the stored value. + /// + /// `try_update` takes two [`Ordering`] arguments to describe the memory ordering of this operation. + /// The first describes the required ordering for when the operation finally succeeds while the second + /// describes the required ordering for loads. These correspond to the success and failure orderings of + #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange`]")] + /// respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the final successful load + /// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`].")] + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of + #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange_weak`],")] + /// and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let x = ", stringify!($atomic_type), "::new(7);")] + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(7)); + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(7)); + /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + 1)), Ok(8)); + /// assert_eq!(x.load(Ordering::SeqCst), 9); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[$cfg_cas] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn try_update( + &self, + set_order: Ordering, + fetch_order: Ordering, + f: impl FnMut($int_type) -> Option<$int_type>, + ) -> Result<$int_type, $int_type> { + // FIXME(atomic_try_update): this is currently an unstable alias to `fetch_update`; + // when stabilizing, turn `fetch_update` into a deprecated alias to `try_update`. + self.fetch_update(set_order, fetch_order, f) + } + + /// Fetches the value, applies a function to it that it return a new value. + /// The new value is stored and the old value is returned. + /// + #[doc = concat!("See also: [`try_update`](`", stringify!($atomic_type), "::try_update`).")] + /// + /// Note: This may call the function multiple times if the value has been changed from other threads in + /// the meantime, but the function will have been applied only once to the stored value. + /// + /// `update` takes two [`Ordering`] arguments to describe the memory ordering of this operation. + /// The first describes the required ordering for when the operation finally succeeds while the second + /// describes the required ordering for loads. These correspond to the success and failure orderings of + #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange`]")] + /// respectively. + /// + /// Using [`Acquire`] as success ordering makes the store part + /// of this operation [`Relaxed`], and using [`Release`] makes the final successful load + /// [`Relaxed`]. The (failed) load ordering can only be [`SeqCst`], [`Acquire`] or [`Relaxed`]. + /// + /// **Note**: This method is only available on platforms that support atomic operations on + #[doc = concat!("[`", $s_int_type, "`].")] + /// + /// # Considerations + /// + /// This method is not magic; it is not provided by the hardware. + /// It is implemented in terms of + #[doc = concat!("[`", stringify!($atomic_type), "::compare_exchange_weak`],")] + /// and suffers from the same drawbacks. + /// In particular, this method will not circumvent the [ABA Problem]. + /// + /// [ABA Problem]: https://en.wikipedia.org/wiki/ABA_problem + /// + /// # Examples + /// + /// ```rust + /// #![feature(atomic_try_update)] + #[doc = concat!($extra_feature, "use std::sync::atomic::{", stringify!($atomic_type), ", Ordering};")] + /// + #[doc = concat!("let x = ", stringify!($atomic_type), "::new(7);")] + /// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |x| x + 1), 7); + /// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |x| x + 1), 8); + /// assert_eq!(x.load(Ordering::SeqCst), 9); + /// ``` + #[inline] + #[unstable(feature = "atomic_try_update", issue = "135894")] + #[$cfg_cas] + #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces + pub fn update( + &self, + set_order: Ordering, + fetch_order: Ordering, + mut f: impl FnMut($int_type) -> $int_type, + ) -> $int_type { + let mut prev = self.load(fetch_order); + loop { + match self.compare_exchange_weak(prev, f(prev), set_order, fetch_order) { + Ok(x) => break x, + Err(next_prev) => prev = next_prev, + } + } + } + /// Maximum with the current value. /// /// Finds the maximum of the current value and the argument `val`, and diff --git a/library/std/src/f128.rs b/library/std/src/f128.rs index 4f37e18a8cd76..d65f5ed61cfbc 100644 --- a/library/std/src/f128.rs +++ b/library/std/src/f128.rs @@ -324,6 +324,20 @@ impl f128 { /// /// The precision of this function is non-deterministic. This means it varies by platform, /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// # #[cfg(reliable_f128_math)] { + /// + /// let x = 2.0_f128; + /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f128::EPSILON); + /// + /// assert_eq!(f128::powi(f128::NAN, 0), 1.0); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] @@ -347,8 +361,10 @@ impl f128 { /// /// let x = 2.0_f128; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// /// assert!(abs_difference <= f128::EPSILON); + /// + /// assert_eq!(f128::powf(1.0, f128::NAN), 1.0); + /// assert_eq!(f128::powf(f128::NAN, 0.0), 1.0); /// # } /// ``` #[inline] diff --git a/library/std/src/f16.rs b/library/std/src/f16.rs index 42cd6e3fe2a5f..5b0903bceabb4 100644 --- a/library/std/src/f16.rs +++ b/library/std/src/f16.rs @@ -324,6 +324,20 @@ impl f16 { /// /// The precision of this function is non-deterministic. This means it varies by platform, /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// # #[cfg(reliable_f16_math)] { + /// + /// let x = 2.0_f16; + /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f16::EPSILON); + /// + /// assert_eq!(f16::powi(f16::NAN, 0), 1.0); + /// # } + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] @@ -347,8 +361,10 @@ impl f16 { /// /// let x = 2.0_f16; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// /// assert!(abs_difference <= f16::EPSILON); + /// + /// assert_eq!(f16::powf(1.0, f16::NAN), 1.0); + /// assert_eq!(f16::powf(f16::NAN, 0.0), 1.0); /// # } /// ``` #[inline] diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index 438d77b1626be..f9b6723788ae3 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -306,8 +306,9 @@ impl f32 { /// ``` /// let x = 2.0_f32; /// let abs_difference = (x.powi(2) - (x * x)).abs(); - /// /// assert!(abs_difference <= f32::EPSILON); + /// + /// assert_eq!(f32::powi(f32::NAN, 0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -329,8 +330,10 @@ impl f32 { /// ``` /// let x = 2.0_f32; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// /// assert!(abs_difference <= f32::EPSILON); + /// + /// assert_eq!(f32::powf(1.0, f32::NAN), 1.0); + /// assert_eq!(f32::powf(f32::NAN, 0.0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index 9bb4bfbab2a0f..0de55a15d48e8 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -306,8 +306,9 @@ impl f64 { /// ``` /// let x = 2.0_f64; /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f64::EPSILON); /// - /// assert!(abs_difference < 1e-10); + /// assert_eq!(f64::powi(f64::NAN, 0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -329,8 +330,10 @@ impl f64 { /// ``` /// let x = 2.0_f64; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); + /// assert!(abs_difference <= f64::EPSILON); /// - /// assert!(abs_difference < 1e-10); + /// assert_eq!(f64::powf(1.0, f64::NAN), 1.0); + /// assert_eq!(f64::powf(f64::NAN, 0.0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] diff --git a/library/std/src/io/pipe/tests.rs b/library/std/src/io/pipe/tests.rs index c1f3f192ca2d7..f113b157459d3 100644 --- a/library/std/src/io/pipe/tests.rs +++ b/library/std/src/io/pipe/tests.rs @@ -1,7 +1,7 @@ use crate::io::{Read, Write, pipe}; #[test] -#[cfg(all(windows, unix, not(miri)))] +#[cfg(all(any(unix, windows), not(miri)))] fn pipe_creation_clone_and_rw() { let (rx, tx) = pipe().unwrap(); diff --git a/library/std/src/sync/mpmc/mod.rs b/library/std/src/sync/mpmc/mod.rs index 0cf4902d6d59b..00966ee3ecffd 100644 --- a/library/std/src/sync/mpmc/mod.rs +++ b/library/std/src/sync/mpmc/mod.rs @@ -18,7 +18,7 @@ //! infinite buffer. //! //! 2. A synchronous, bounded channel. The [`sync_channel`] function will -//! return a `(SyncSender, Receiver)` tuple where the storage for pending +//! return a `(Sender, Receiver)` tuple where the storage for pending //! messages is a pre-allocated buffer of a fixed size. All sends will be //! **synchronous** by blocking until there is buffer space available. Note //! that a bound of 0 is allowed, causing the channel to become a "rendezvous" @@ -360,9 +360,17 @@ impl Sender { /// that a return value of [`Err`] means that the data will never be /// received, but a return value of [`Ok`] does *not* mean that the data /// will be received. It is possible for the corresponding receiver to - /// hang up immediately after this function returns [`Ok`]. + /// hang up immediately after this function returns [`Ok`]. However, if + /// the channel is zero-capacity, it acts as a rendezvous channel and a + /// return value of [`Ok`] means that the data has been received. /// - /// This method will never block the current thread. + /// If the channel is full and not disconnected, this call will block until + /// the send operation can proceed. If the channel becomes disconnected, + /// this call will wake up and return an error. The returned error contains + /// the original message. + /// + /// If called on a zero-capacity channel, this method will wait for a receive + /// operation to appear on the other side of the channel. /// /// # Examples /// @@ -650,7 +658,7 @@ impl fmt::Debug for Sender { } /// The receiving half of Rust's [`channel`] (or [`sync_channel`]) type. -/// Different threads can share this [`Sender`] by cloning it. +/// Different threads can share this [`Receiver`] by cloning it. /// /// Messages sent to the channel can be retrieved using [`recv`]. /// diff --git a/library/std/src/sys/pal/uefi/process.rs b/library/std/src/sys/pal/uefi/process.rs index 1a0754134dfb8..3077a72eac661 100644 --- a/library/std/src/sys/pal/uefi/process.rs +++ b/library/std/src/sys/pal/uefi/process.rs @@ -460,7 +460,7 @@ mod uefi_command_internal { helpers::open_protocol(self.handle, loaded_image::PROTOCOL_GUID).unwrap(); let len = args.len(); - let args_size: u32 = crate::mem::size_of_val(&args).try_into().unwrap(); + let args_size: u32 = (len * crate::mem::size_of::()).try_into().unwrap(); let ptr = Box::into_raw(args).as_mut_ptr(); unsafe { @@ -706,9 +706,10 @@ mod uefi_command_internal { res.push(QUOTE); res.extend(prog.encode_wide()); res.push(QUOTE); - res.push(SPACE); for arg in args { + res.push(SPACE); + // Wrap the argument in quotes to be treat as single arg res.push(QUOTE); for c in arg.encode_wide() { @@ -719,8 +720,6 @@ mod uefi_command_internal { res.push(c); } res.push(QUOTE); - - res.push(SPACE); } res.into_boxed_slice() diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index 9ae03ac7fe075..a0abd439de020 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -4,8 +4,10 @@ use std::{env, fs}; use crate::core::build_steps::toolstate::ToolState; use crate::core::build_steps::{compile, llvm}; use crate::core::builder; -use crate::core::builder::{Builder, Cargo as CargoCommand, RunConfig, ShouldRun, Step}; -use crate::core::config::{DebuginfoLevel, TargetSelection}; +use crate::core::builder::{ + Builder, Cargo as CargoCommand, RunConfig, ShouldRun, Step, cargo_profile_var, +}; +use crate::core::config::{DebuginfoLevel, RustcLto, TargetSelection}; use crate::utils::channel::GitInfo; use crate::utils::exec::{BootstrapCommand, command}; use crate::utils::helpers::{add_dylib_path, exe, t}; @@ -645,7 +647,7 @@ impl Step for Rustdoc { } // NOTE: Never modify the rustflags here, it breaks the build cache for other tools! - let cargo = prepare_tool_cargo( + let mut cargo = prepare_tool_cargo( builder, build_compiler, Mode::ToolRustc, @@ -656,6 +658,17 @@ impl Step for Rustdoc { features.as_slice(), ); + // rustdoc is performance sensitive, so apply LTO to it. + let lto = match builder.config.rust_lto { + RustcLto::Off => Some("off"), + RustcLto::Thin => Some("thin"), + RustcLto::Fat => Some("fat"), + RustcLto::ThinLocal => None, + }; + if let Some(lto) = lto { + cargo.env(cargo_profile_var("LTO", &builder.config), lto); + } + let _guard = builder.msg_tool( Kind::Build, Mode::ToolRustc, diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index d418237a5689f..1b413dcb07efb 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -10,7 +10,7 @@ use crate::core::config::flags::Color; use crate::utils::build_stamp; use crate::utils::helpers::{self, LldThreads, check_cfg_arg, linker_args, linker_flags}; use crate::{ - BootstrapCommand, CLang, Compiler, DocTests, DryRun, EXTRA_CHECK_CFGS, GitRepo, Mode, + BootstrapCommand, CLang, Compiler, Config, DocTests, DryRun, EXTRA_CHECK_CFGS, GitRepo, Mode, TargetSelection, command, prepare_behaviour_dump_dir, t, }; @@ -484,10 +484,7 @@ impl Builder<'_> { build_stamp::clear_if_dirty(self, &my_out, &rustdoc); } - let profile_var = |name: &str| { - let profile = if self.config.rust_optimize.is_release() { "RELEASE" } else { "DEV" }; - format!("CARGO_PROFILE_{}_{}", profile, name) - }; + let profile_var = |name: &str| cargo_profile_var(name, &self.config); // See comment in rustc_llvm/build.rs for why this is necessary, largely llvm-config // needs to not accidentally link to libLLVM in stage0/lib. @@ -1232,3 +1229,8 @@ impl Builder<'_> { } } } + +pub fn cargo_profile_var(name: &str, config: &Config) -> String { + let profile = if config.rust_optimize.is_release() { "RELEASE" } else { "DEV" }; + format!("CARGO_PROFILE_{}_{}", profile, name) +} diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index b293ac4f35154..04a00fde3ab45 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -11,7 +11,7 @@ use std::{env, fs}; use clap::ValueEnum; -pub use self::cargo::Cargo; +pub use self::cargo::{Cargo, cargo_profile_var}; pub use crate::Compiler; use crate::core::build_steps::{ check, clean, clippy, compile, dist, doc, gcc, install, llvm, run, setup, test, tool, vendor, diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index a3eb781f1476a..6f62df28e4949 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -340,4 +340,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "Change the compiler profile to default to rust.debug-assertions = true", }, + ChangeInfo { + change_id: 135832, + severity: ChangeSeverity::Info, + summary: "Rustdoc now respects the value of rust.lto.", + }, ]; diff --git a/src/build_helper/src/git.rs b/src/build_helper/src/git.rs index 01bac1498c24a..3ef9c7ac35e9d 100644 --- a/src/build_helper/src/git.rs +++ b/src/build_helper/src/git.rs @@ -30,8 +30,8 @@ pub fn output_result(cmd: &mut Command) -> Result { /// Finds the remote for rust-lang/rust. /// For example for these remotes it will return `upstream`. /// ```text -/// origin https://github.com/Nilstrieb/rust.git (fetch) -/// origin https://github.com/Nilstrieb/rust.git (push) +/// origin https://github.com/pietroalbani/rust.git (fetch) +/// origin https://github.com/pietroalbani/rust.git (push) /// upstream https://github.com/rust-lang/rust (fetch) /// upstream https://github.com/rust-lang/rust (push) /// ``` diff --git a/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile b/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile index 6f33c63218182..2b8a3f829c608 100644 --- a/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile +++ b/src/ci/docker/host-aarch64/dist-aarch64-linux/Dockerfile @@ -20,6 +20,7 @@ RUN yum upgrade -y && \ gcc-c++ \ git \ glibc-devel \ + glibc-static \ libedit-devel \ libstdc++-devel \ make \ diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile index 3a39623058255..0b4682ac32ba0 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile @@ -21,6 +21,8 @@ RUN yum upgrade -y && \ git \ glibc-devel.i686 \ glibc-devel.x86_64 \ + glibc-static.i686 \ + glibc-static.x86_64 \ libedit-devel \ libstdc++-devel.i686 \ libstdc++-devel.x86_64 \ diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index d408cd518a00b..9234c6dc921ee 100644 --- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile +++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile @@ -29,7 +29,7 @@ ENV PATH="/node/bin:${PATH}" # Install es-check # Pin its version to prevent unrelated CI failures due to future es-check versions. -RUN npm install es-check@6.1.1 eslint@8.6.0 -g +RUN npm install es-check@6.1.1 eslint@8.6.0 typescript@5.7.3 -g COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh @@ -68,4 +68,5 @@ ENV SCRIPT \ es-check es2019 ../src/librustdoc/html/static/js/*.js && \ eslint -c ../src/librustdoc/html/static/.eslintrc.js ../src/librustdoc/html/static/js/*.js && \ eslint -c ../src/tools/rustdoc-js/.eslintrc.js ../src/tools/rustdoc-js/tester.js && \ - eslint -c ../src/tools/rustdoc-gui/.eslintrc.js ../src/tools/rustdoc-gui/tester.js + eslint -c ../src/tools/rustdoc-gui/.eslintrc.js ../src/tools/rustdoc-gui/tester.js && \ + tsc --project ../src/librustdoc/html/static/js/tsconfig.json diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index c9368130205f6..9af887096251c 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -47,10 +47,6 @@ runners: os: windows-2025-8core-32gb <<: *base-job - - &job-windows-16c - os: windows-2022-16core-64gb - <<: *base-job - - &job-aarch64-linux # Free some disk space to avoid running out of space during the build. free_disk: true @@ -90,7 +86,7 @@ envs: # builds) # - not running `opt-dist`'s post-optimization smoke tests on the resulting toolchain # - # If you *want* these to happen however, temporarily uncomment it before triggering a try build. + # If you *want* these to happen however, temporarily comment it before triggering a try build. DIST_TRY_BUILD: 1 auto: diff --git a/src/doc/book b/src/doc/book index 82a4a49789bc9..fa312a343fbff 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 82a4a49789bc96db1a1b2a210b4c5ed7c9ef0c0d +Subproject commit fa312a343fbff01bc6cef393e326817f70719813 diff --git a/src/doc/edition-guide b/src/doc/edition-guide index d56e0f3a0656b..4ed5a1a4a2a7e 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit d56e0f3a0656b7702ca466d4b191e16c28262b82 +Subproject commit 4ed5a1a4a2a7ecc2e529a5baaef04f7bc7917eda diff --git a/src/doc/nomicon b/src/doc/nomicon index 625b200e5b33a..bc22988655446 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 625b200e5b33a5af35589db0bc454203a3d46d20 +Subproject commit bc2298865544695c63454fc1f9f98a3dc22e9948 diff --git a/src/doc/reference b/src/doc/reference index 293af99100377..93b921c7d3213 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 293af991003772bdccf2d6b980182d84dd055942 +Subproject commit 93b921c7d3213d38d920f7f905a3bec093d2217d diff --git a/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs b/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs index 8983915d78a9c..b0f9af1b8d103 100644 --- a/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs +++ b/src/doc/rustc-dev-guide/examples/rustc-driver-example.rs @@ -58,7 +58,7 @@ impl rustc_driver::Callbacks for MyCallbacks { fn after_crate_root_parsing( &mut self, _compiler: &Compiler, - krate: &rustc_ast::Crate, + krate: &mut rustc_ast::Crate, ) -> Compilation { for item in &krate.items { println!("{}", item_to_string(&item)); diff --git a/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs b/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs index c894b60444ac4..8766a81734464 100644 --- a/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs +++ b/src/doc/rustc-dev-guide/examples/rustc-driver-interacting-with-the-ast.rs @@ -58,7 +58,7 @@ impl rustc_driver::Callbacks for MyCallbacks { fn after_crate_root_parsing( &mut self, _compiler: &Compiler, - krate: &rustc_ast::Crate, + krate: &mut rustc_ast::Crate, ) -> Compilation { for item in &krate.items { println!("{}", item_to_string(&item)); diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index c964c29768f9c..8227dfa043e3a 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -262,7 +262,7 @@ target | std | host | notes [`aarch64-unknown-nto-qnx710`](platform-support/nto-qnx.md) | ✓ | | ARM64 QNX Neutrino 7.1 RTOS with default network stack (io-pkt) | [`aarch64-unknown-nto-qnx710_iosock`](platform-support/nto-qnx.md) | ✓ | | ARM64 QNX Neutrino 7.1 RTOS with new network stack (io-sock) | [`aarch64-unknown-nto-qnx800`](platform-support/nto-qnx.md) | ✓ | | ARM64 QNX Neutrino 8.0 RTOS | -[`aarch64-unknown-nuttx`](platform-support/nuttx.md) | * | | ARM64 with NuttX +[`aarch64-unknown-nuttx`](platform-support/nuttx.md) | ✓ | | ARM64 with NuttX [`aarch64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | ARM64 OpenBSD [`aarch64-unknown-redox`](platform-support/redox.md) | ✓ | | ARM64 Redox OS [`aarch64-unknown-teeos`](platform-support/aarch64-unknown-teeos.md) | ? | | ARM64 TEEOS | @@ -298,8 +298,8 @@ target | std | host | notes [`armv7k-apple-watchos`](platform-support/apple-watchos.md) | ✓ | | Armv7-A Apple WatchOS [`armv7s-apple-ios`](platform-support/apple-ios.md) | ✓ | | Armv7-A Apple-A6 Apple iOS [`armv8r-none-eabihf`](platform-support/armv8r-none-eabihf.md) | * | | Bare Armv8-R, hardfloat -[`armv7a-nuttx-eabi`](platform-support/nuttx.md) | * | | ARMv7-A with NuttX -[`armv7a-nuttx-eabihf`](platform-support/nuttx.md) | * | | ARMv7-A with NuttX, hardfloat +[`armv7a-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv7-A with NuttX +[`armv7a-nuttx-eabihf`](platform-support/nuttx.md) | ✓ | | ARMv7-A with NuttX, hardfloat `avr-unknown-gnu-atmega328` | * | | AVR. Requires `-Z build-std=core` `bpfeb-unknown-none` | * | | BPF (big endian) `bpfel-unknown-none` | * | | BPF (little endian) @@ -369,21 +369,21 @@ target | std | host | notes [`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 -[`riscv32imac-unknown-nuttx-elf`](platform-support/nuttx.md) | * | | RISC-V 32bit with NuttX +[`riscv32imac-unknown-nuttx-elf`](platform-support/nuttx.md) | ✓ | | RISC-V 32bit with NuttX [`riscv32imac-unknown-xous-elf`](platform-support/riscv32imac-unknown-xous-elf.md) | ? | | RISC-V Xous (RV32IMAC ISA) [`riscv32imafc-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF -[`riscv32imafc-unknown-nuttx-elf`](platform-support/nuttx.md) | * | | RISC-V 32bit with NuttX +[`riscv32imafc-unknown-nuttx-elf`](platform-support/nuttx.md) | ✓ | | RISC-V 32bit with NuttX [`riscv32imc-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF -[`riscv32imc-unknown-nuttx-elf`](platform-support/nuttx.md) | * | | RISC-V 32bit with NuttX +[`riscv32imc-unknown-nuttx-elf`](platform-support/nuttx.md) | ✓ | | RISC-V 32bit with NuttX [`riscv64-linux-android`](platform-support/android.md) | ? | | RISC-V 64-bit Android [`riscv64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | `riscv64gc-unknown-freebsd` | ? | | RISC-V FreeBSD `riscv64gc-unknown-fuchsia` | ? | | RISC-V Fuchsia [`riscv64gc-unknown-hermit`](platform-support/hermit.md) | ✓ | | RISC-V Hermit [`riscv64gc-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | RISC-V NetBSD -[`riscv64gc-unknown-nuttx-elf`](platform-support/nuttx.md) | * | | RISC-V 64bit with NuttX +[`riscv64gc-unknown-nuttx-elf`](platform-support/nuttx.md) | ✓ | | RISC-V 64bit with NuttX [`riscv64gc-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/riscv64 -[`riscv64imac-unknown-nuttx-elf`](platform-support/nuttx.md) | * | | RISC-V 64bit with NuttX +[`riscv64imac-unknown-nuttx-elf`](platform-support/nuttx.md) | ✓ | | RISC-V 64bit with NuttX [`s390x-unknown-linux-musl`](platform-support/s390x-unknown-linux-musl.md) | ✓ | | S390x Linux (kernel 3.2, musl 1.2.3) `sparc-unknown-linux-gnu` | ✓ | | 32-bit SPARC Linux [`sparc-unknown-none-elf`](./platform-support/sparc-unknown-none-elf.md) | * | | Bare 32-bit SPARC V7+ @@ -391,18 +391,18 @@ target | std | host | notes [`sparc64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/sparc64 [`thumbv4t-none-eabi`](platform-support/armv4t-none-eabi.md) | * | | Thumb-mode Bare Armv4T [`thumbv5te-none-eabi`](platform-support/armv5te-none-eabi.md) | * | | Thumb-mode Bare Armv5TE -[`thumbv6m-nuttx-eabi`](platform-support/nuttx.md) | * | | ARMv6M with NuttX +[`thumbv6m-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv6M with NuttX `thumbv7a-pc-windows-msvc` | | | [`thumbv7a-uwp-windows-msvc`](platform-support/uwp-windows-msvc.md) | | | -[`thumbv7a-nuttx-eabi`](platform-support/nuttx.md) | * | | ARMv7-A with NuttX -[`thumbv7a-nuttx-eabihf`](platform-support/nuttx.md) | * | | ARMv7-A with NuttX, hardfloat -[`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 +[`thumbv7a-nuttx-eabi`](platform-support/nuttx.md) | ✓ | | ARMv7-A with NuttX +[`thumbv7a-nuttx-eabihf`](platform-support/nuttx.md) | ✓ | | ARMv7-A with NuttX, hardfloat +[`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 -[`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 +[`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 [`wasm64-unknown-unknown`](platform-support/wasm64-unknown-unknown.md) | ? | | WebAssembly [`x86_64-apple-tvos`](platform-support/apple-tvos.md) | ✓ | | x86 64-bit tvOS [`x86_64-apple-watchos-sim`](platform-support/apple-watchos.md) | ✓ | | x86 64-bit Apple WatchOS simulator diff --git a/src/librustdoc/html/static/js/README.md b/src/librustdoc/html/static/js/README.md index 1fd859ad7cf49..e99d7330f0ed8 100644 --- a/src/librustdoc/html/static/js/README.md +++ b/src/librustdoc/html/static/js/README.md @@ -3,13 +3,9 @@ These JavaScript files are incorporated into the rustdoc binary at build time, and are minified and written to the filesystem as part of the doc build process. -We use the [Closure Compiler](https://github.com/google/closure-compiler/wiki/Annotating-JavaScript-for-the-Closure-Compiler) +We use the [TypeScript Compiler](https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html) dialect of JSDoc to comment our code and annotate params and return types. To run a check: - ./x.py doc library/std - npm i -g google-closure-compiler - google-closure-compiler -W VERBOSE \ - build//doc/{search-index*.js,crates*.js} \ - src/librustdoc/html/static/js/{search.js,main.js,storage.js} \ - --externs src/librustdoc/html/static/js/externs.js >/dev/null + npm i -g typescript + tsc --project tsconfig.json diff --git a/src/librustdoc/html/static/js/externs.js b/src/librustdoc/html/static/js/externs.js deleted file mode 100644 index c4faca1c0c3bc..0000000000000 --- a/src/librustdoc/html/static/js/externs.js +++ /dev/null @@ -1,270 +0,0 @@ -// This file contains type definitions that are processed by the Closure Compiler but are -// not put into the JavaScript we include as part of the documentation. It is used for -// type checking. See README.md in this directory for more info. - -/* eslint-disable */ -let searchState; -function initSearch(searchIndex){} - -/** - * @typedef {{ - * name: string, - * id: number|null, - * fullPath: Array, - * pathWithoutLast: Array, - * pathLast: string, - * generics: Array, - * bindings: Map>, - * }} - */ -let QueryElement; - -/** - * @typedef {{ - * pos: number, - * totalElems: number, - * typeFilter: (null|string), - * userQuery: string, - * isInBinding: (null|string), - * }} - */ -let ParserState; - -/** - * @typedef {{ - * original: string, - * userQuery: string, - * typeFilter: number, - * elems: Array, - * args: Array, - * returned: Array, - * foundElems: number, - * totalElems: number, - * literalSearch: boolean, - * hasReturnArrow: boolean, - * corrections: Array<{from: string, to: integer}> | null, - * typeFingerprint: Uint32Array, - * error: Array | null, - * }} - */ -let ParsedQuery; - -/** - * @typedef {{ - * crate: string, - * desc: string, - * id: number, - * name: string, - * normalizedName: string, - * parent: (Object|null|undefined), - * path: string, - * ty: (Number|null|number), - * type: FunctionSearchType? - * }} - */ -let Row; - -/** - * @typedef {{ - * in_args: Array, - * returned: Array, - * others: Array, - * query: ParsedQuery, - * }} - */ -let ResultsTable; - -/** - * @typedef {Map} - */ -let Results; - -/** - * @typedef {{ - * desc: string, - * displayPath: string, - * fullPath: string, - * href: string, - * id: number, - * lev: number, - * name: string, - * normalizedName: string, - * parent: (Object|undefined), - * path: string, - * ty: number, - * type: FunctionSearchType?, - * displayType: Promise>>|null, - * displayTypeMappedNames: Promise]>>|null, - * }} - */ -let ResultObject; - -/** - * A pair of [inputs, outputs], or 0 for null. This is stored in the search index. - * The JavaScript deserializes this into FunctionSearchType. - * - * Numeric IDs are *ONE-indexed* into the paths array (`p`). Zero is used as a sentinel for `null` - * because `null` is four bytes while `0` is one byte. - * - * An input or output can be encoded as just a number if there is only one of them, AND - * it has no generics. The no generics rule exists to avoid ambiguity: imagine if you had - * a function with a single output, and that output had a single generic: - * - * fn something() -> Result - * - * If output was allowed to be any RawFunctionType, it would look like thi - * - * [[], [50, [3, 3]]] - * - * The problem is that the above output could be interpreted as either a type with ID 50 and two - * generics, or it could be interpreted as a pair of types, the first one with ID 50 and the second - * with ID 3 and a single generic parameter that is also ID 3. We avoid this ambiguity by choosing - * in favor of the pair of types interpretation. This is why the `(number|Array)` - * is used instead of `(RawFunctionType|Array)`. - * - * The output can be skipped if it's actually unit and there's no type constraints. If thi - * function accepts constrained generics, then the output will be unconditionally emitted, and - * after it will come a list of trait constraints. The position of the item in the list will - * determine which type parameter it is. For example: - * - * [1, 2, 3, 4, 5] - * ^ ^ ^ ^ ^ - * | | | | - generic parameter (-3) of trait 5 - * | | | - generic parameter (-2) of trait 4 - * | | - generic parameter (-1) of trait 3 - * | - this function returns a single value (type 2) - * - this function takes a single input parameter (type 1) - * - * Or, for a less contrived version: - * - * [[[4, -1], 3], [[5, -1]], 11] - * -^^^^^^^---- ^^^^^^^ ^^ - * | | | - generic parameter, roughly `where -1: 11` - * | | | since -1 is the type parameter and 11 the trait - * | | - function output 5<-1> - * | - the overall function signature is something like - * | `fn(4<-1>, 3) -> 5<-1> where -1: 11` - * - function input, corresponds roughly to 4<-1> - * 4 is an index into the `p` array for a type - * -1 is the generic parameter, given by 11 - * - * If a generic parameter has multiple trait constraints, it gets wrapped in an array, just like - * function inputs and outputs: - * - * [-1, -1, [4, 3]] - * ^^^^^^ where -1: 4 + 3 - * - * If a generic parameter's trait constraint has generic parameters, it gets wrapped in the array - * even if only one exists. In other words, the ambiguity of `4<3>` and `4 + 3` is resolved in - * favor of `4 + 3`: - * - * [-1, -1, [[4, 3]]] - * ^^^^^^^^ where -1: 4 + 3 - * - * [-1, -1, [5, [4, 3]]] - * ^^^^^^^^^^^ where -1: 5, -2: 4 + 3 - * - * If a generic parameter has no trait constraints (like in Rust, the `Sized` constraint i - * implied and a fake `?Sized` constraint used to note its absence), it will be filled in with 0. - * - * @typedef {( - * 0 | - * [(number|Array)] | - * [(number|Array), (number|Array)] | - * Array<(number|Array)> - * )} - */ -let RawFunctionSearchType; - -/** - * A single function input or output type. This is either a single path ID, or a pair of - * [path ID, generics]. - * - * Numeric IDs are *ONE-indexed* into the paths array (`p`). Zero is used as a sentinel for `null` - * because `null` is four bytes while `0` is one byte. - * - * @typedef {number | [number, Array]} - */ -let RawFunctionType; - -/** - * @typedef {{ - * inputs: Array, - * output: Array, - * where_clause: Array>, - * }} - */ -let FunctionSearchType; - -/** - * @typedef {{ - * id: (null|number), - * ty: number, - * generics: Array, - * bindings: Map>, - * }} - */ -let FunctionType; - -/** - * The raw search data for a given crate. `n`, `t`, `d`, `i`, and `f` - * are arrays with the same length. `q`, `a`, and `c` use a sparse - * representation for compactness. - * - * `n[i]` contains the name of an item. - * - * `t[i]` contains the type of that item - * (as a string of characters that represent an offset in `itemTypes`). - * - * `d[i]` contains the description of that item. - * - * `q` contains the full paths of the items. For compactness, it is a set of - * (index, path) pairs used to create a map. If a given index `i` is - * not present, this indicates "same as the last index present". - * - * `i[i]` contains an item's parent, usually a module. For compactness, - * it is a set of indexes into the `p` array. - * - * `f` contains function signatures, or `0` if the item isn't a function. - * More information on how they're encoded can be found in rustc-dev-guide - * - * Functions are themselves encoded as arrays. The first item is a list of - * types representing the function's inputs, and the second list item is a list - * of types representing the function's output. Tuples are flattened. - * Types are also represented as arrays; the first item is an index into the `p` - * array, while the second is a list of types representing any generic parameters. - * - * b[i] contains an item's impl disambiguator. This is only present if an item - * is defined in an impl block and, the impl block's type has more than one associated - * item with the same name. - * - * `a` defines aliases with an Array of pairs: [name, offset], where `offset` - * points into the n/t/d/q/i/f arrays. - * - * `doc` contains the description of the crate. - * - * `p` is a list of path/type pairs. It is used for parents and function parameters. - * The first item is the type, the second is the name, the third is the visible path (if any) and - * the fourth is the canonical path used for deduplication (if any). - * - * `r` is the canonical path used for deduplication of re-exported items. - * It is not used for associated items like methods (that's the fourth element - * of `p`) but is used for modules items like free functions. - * - * `c` is an array of item indices that are deprecated. - * @typedef {{ - * doc: string, - * a: Object, - * n: Array, - * t: string, - * d: Array, - * q: Array<[number, string]>, - * i: Array, - * f: string, - * p: Array<[number, string] | [number, string, number] | [number, string, number, number]>, - * b: Array<[number, String]>, - * c: Array, - * r: Array<[number, number]>, - * }} - */ -let RawSearchIndexCrate; diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 984b0877d8de2..ccf4002bb300d 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -11,8 +11,13 @@ window.RUSTDOC_TOOLTIP_HOVER_MS = 300; window.RUSTDOC_TOOLTIP_HOVER_EXIT_MS = 450; -// Given a basename (e.g. "storage") and an extension (e.g. ".js"), return a URL -// for a resource under the root-path, with the resource-suffix. +/** + * Given a basename (e.g. "storage") and an extension (e.g. ".js"), return a URL + * for a resource under the root-path, with the resource-suffix. + * + * @param {string} basename + * @param {string} extension + */ function resourcePath(basename, extension) { return getVar("root-path") + basename + getVar("resource-suffix") + extension; } @@ -27,13 +32,18 @@ function hideMain() { function showMain() { const main = document.getElementById(MAIN_ID); + if (!main) { + return; + } removeClass(main, "hidden"); const mainHeading = main.querySelector(".main-heading"); - if (mainHeading && searchState.rustdocToolbar) { - if (searchState.rustdocToolbar.parentElement) { - searchState.rustdocToolbar.parentElement.removeChild(searchState.rustdocToolbar); + if (mainHeading && window.searchState.rustdocToolbar) { + if (window.searchState.rustdocToolbar.parentElement) { + window.searchState.rustdocToolbar.parentElement.removeChild( + window.searchState.rustdocToolbar, + ); } - mainHeading.appendChild(searchState.rustdocToolbar); + mainHeading.appendChild(window.searchState.rustdocToolbar); } const toggle = document.getElementById("toggle-all-docs"); if (toggle) { @@ -61,16 +71,20 @@ function setMobileTopbar() { } } -// Gets the human-readable string for the virtual-key code of the -// given KeyboardEvent, ev. -// -// This function is meant as a polyfill for KeyboardEvent#key, -// since it is not supported in IE 11 or Chrome for Android. We also test for -// KeyboardEvent#keyCode because the handleShortcut handler is -// also registered for the keydown event, because Blink doesn't fire -// keypress on hitting the Escape key. -// -// So I guess you could say things are getting pretty interoperable. +/** + * Gets the human-readable string for the virtual-key code of the + * given KeyboardEvent, ev. + * + * This function is meant as a polyfill for KeyboardEvent#key, + * since it is not supported in IE 11 or Chrome for Android. We also test for + * KeyboardEvent#keyCode because the handleShortcut handler is + * also registered for the keydown event, because Blink doesn't fire + * keypress on hitting the Escape key. + * + * So I guess you could say things are getting pretty interoperable. + * + * @param {KeyboardEvent} ev + */ function getVirtualKey(ev) { if ("key" in ev && typeof ev.key !== "undefined") { return ev.key; @@ -110,6 +124,9 @@ function getNakedUrl() { * @param {HTMLElement} referenceNode */ function insertAfter(newNode, referenceNode) { + // You're not allowed to pass an element with no parent. + // I dunno how to make TS's typechecker see that. + // @ts-expect-error referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); } @@ -129,6 +146,7 @@ function getOrCreateSection(id, classes) { el = document.createElement("section"); el.id = id; el.className = classes; + // @ts-expect-error insertAfter(el, document.getElementById(MAIN_ID)); } return el; @@ -159,12 +177,13 @@ function getNotDisplayedElem() { * contains the displayed element (there can be only one at the same time!). So basically, we switch * elements between the two `
` elements. * - * @param {HTMLElement} elemToDisplay + * @param {HTMLElement|null} elemToDisplay */ function switchDisplayedElement(elemToDisplay) { const el = getAlternativeDisplayElem(); if (el.children.length > 0) { + // @ts-expect-error getNotDisplayedElem().appendChild(el.firstElementChild); } if (elemToDisplay === null) { @@ -177,10 +196,14 @@ function switchDisplayedElement(elemToDisplay) { removeClass(el, "hidden"); const mainHeading = elemToDisplay.querySelector(".main-heading"); + // @ts-expect-error if (mainHeading && searchState.rustdocToolbar) { + // @ts-expect-error if (searchState.rustdocToolbar.parentElement) { + // @ts-expect-error searchState.rustdocToolbar.parentElement.removeChild(searchState.rustdocToolbar); } + // @ts-expect-error mainHeading.appendChild(searchState.rustdocToolbar); } } @@ -189,6 +212,7 @@ function browserSupportsHistoryApi() { return window.history && typeof window.history.pushState === "function"; } +// @ts-expect-error function preLoadCss(cssUrl) { // https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload const link = document.createElement("link"); @@ -201,6 +225,7 @@ function preLoadCss(cssUrl) { (function() { const isHelpPage = window.location.pathname.endsWith("/help.html"); + // @ts-expect-error function loadScript(url, errorCallback) { const script = document.createElement("script"); script.src = url; @@ -211,21 +236,25 @@ function preLoadCss(cssUrl) { } if (getSettingsButton()) { + // @ts-expect-error getSettingsButton().onclick = event => { if (event.ctrlKey || event.altKey || event.metaKey) { return; } + // @ts-expect-error window.hideAllModals(false); addClass(getSettingsButton(), "rotate"); event.preventDefault(); // Sending request for the CSS and the JS files at the same time so it will // hopefully be loaded when the JS will generate the settings content. + // @ts-expect-error loadScript(getVar("static-root-path") + getVar("settings-js")); // Pre-load all theme CSS files, so that switching feels seamless. // // When loading settings.html as a standalone page, the equivalent HTML is // generated in context.rs. setTimeout(() => { + // @ts-expect-error const themes = getVar("themes").split(","); for (const theme of themes) { // if there are no themes, do nothing @@ -241,6 +270,8 @@ function preLoadCss(cssUrl) { window.searchState = { rustdocToolbar: document.querySelector("rustdoc-toolbar"), loadingText: "Loading search results...", + // This will always be an HTMLInputElement, but tsc can't see that + // @ts-expect-error input: document.getElementsByClassName("search-input")[0], outputElement: () => { let el = document.getElementById("search"); @@ -263,31 +294,38 @@ function preLoadCss(cssUrl) { // tab and back preserves the element that was focused. focusedByTab: [null, null, null], clearInputTimeout: () => { - if (searchState.timeout !== null) { - clearTimeout(searchState.timeout); - searchState.timeout = null; + if (window.searchState.timeout !== null) { + clearTimeout(window.searchState.timeout); + window.searchState.timeout = null; } }, - isDisplayed: () => searchState.outputElement().parentElement.id === ALTERNATIVE_DISPLAY_ID, + // @ts-expect-error + isDisplayed: () => { + const outputElement = window.searchState.outputElement(); + return outputElement && + outputElement.parentElement && + outputElement.parentElement.id === ALTERNATIVE_DISPLAY_ID; + }, // Sets the focus on the search bar at the top of the page focus: () => { - searchState.input.focus(); + window.searchState.input && window.searchState.input.focus(); }, // Removes the focus from the search bar. defocus: () => { - searchState.input.blur(); + window.searchState.input && window.searchState.input.blur(); }, showResults: search => { if (search === null || typeof search === "undefined") { - search = searchState.outputElement(); + search = window.searchState.outputElement(); } switchDisplayedElement(search); - searchState.mouseMovedAfterSearch = false; - document.title = searchState.title; + // @ts-expect-error + window.searchState.mouseMovedAfterSearch = false; + document.title = window.searchState.title; }, removeQueryParameters: () => { // We change the document title. - document.title = searchState.titleBeforeSearch; + document.title = window.searchState.titleBeforeSearch; if (browserSupportsHistoryApi()) { history.replaceState(null, "", getNakedUrl() + window.location.hash); } @@ -295,9 +333,10 @@ function preLoadCss(cssUrl) { hideResults: () => { switchDisplayedElement(null); // We also remove the query parameter from the URL. - searchState.removeQueryParameters(); + window.searchState.removeQueryParameters(); }, getQueryStringParams: () => { + /** @type {Object.} */ const params = {}; window.location.search.substring(1).split("&"). map(s => { @@ -309,26 +348,28 @@ function preLoadCss(cssUrl) { return params; }, setup: () => { - const search_input = searchState.input; - if (!searchState.input) { + const search_input = window.searchState.input; + if (!search_input) { return; } let searchLoaded = false; // If you're browsing the nightly docs, the page might need to be refreshed for the // search to work because the hash of the JS scripts might have changed. function sendSearchForm() { + // @ts-expect-error document.getElementsByClassName("search-form")[0].submit(); } function loadSearch() { if (!searchLoaded) { searchLoaded = true; + // @ts-expect-error loadScript(getVar("static-root-path") + getVar("search-js"), sendSearchForm); loadScript(resourcePath("search-index", ".js"), sendSearchForm); } } search_input.addEventListener("focus", () => { - search_input.origPlaceholder = search_input.placeholder; + window.searchState.origPlaceholder = search_input.placeholder; search_input.placeholder = "Type your search here."; loadSearch(); }); @@ -337,16 +378,21 @@ function preLoadCss(cssUrl) { loadSearch(); } - const params = searchState.getQueryStringParams(); + const params = window.searchState.getQueryStringParams(); if (params.search !== undefined) { - searchState.setLoadingSearch(); + window.searchState.setLoadingSearch(); loadSearch(); } }, setLoadingSearch: () => { - const search = searchState.outputElement(); - search.innerHTML = "

" + searchState.loadingText + "

"; - searchState.showResults(search); + const search = window.searchState.outputElement(); + if (!search) { + return; + } + search.innerHTML = "

" + + window.searchState.loadingText + + "

"; + window.searchState.showResults(search); }, descShards: new Map(), loadDesc: async function({descShard, descIndex}) { @@ -370,6 +416,8 @@ function preLoadCss(cssUrl) { return list[descIndex]; }, loadedDescShard: function(crate, shard, data) { + // If loadedDescShard gets called, then the library must have been declared. + // @ts-expect-error this.descShards.get(crate)[shard].resolve(data.split("\n")); }, }; @@ -377,8 +425,11 @@ function preLoadCss(cssUrl) { const toggleAllDocsId = "toggle-all-docs"; let savedHash = ""; + /** + * @param {HashChangeEvent|null} ev + */ function handleHashes(ev) { - if (ev !== null && searchState.isDisplayed() && ev.newURL) { + if (ev !== null && window.searchState.isDisplayed() && ev.newURL) { // This block occurs when clicking on an element in the navbar while // in a search. switchDisplayedElement(null); @@ -419,6 +470,7 @@ function preLoadCss(cssUrl) { } return onEachLazy(implElem.parentElement.parentElement.querySelectorAll( `[id^="${assocId}"]`), + // @ts-expect-error item => { const numbered = /^(.+?)-([0-9]+)$/.exec(item.id); if (item.id === assocId || (numbered && numbered[1] === assocId)) { @@ -437,12 +489,16 @@ function preLoadCss(cssUrl) { } } + /** + * @param {HashChangeEvent|null} ev + */ function onHashChange(ev) { // If we're in mobile mode, we should hide the sidebar in any case. hideSidebar(); handleHashes(ev); } + // @ts-expect-error function openParentDetails(elem) { while (elem) { if (elem.tagName === "DETAILS") { @@ -452,18 +508,25 @@ function preLoadCss(cssUrl) { } } + // @ts-expect-error function expandSection(id) { openParentDetails(document.getElementById(id)); } + // @ts-expect-error function handleEscape(ev) { + // @ts-expect-error searchState.clearInputTimeout(); + // @ts-expect-error searchState.hideResults(); ev.preventDefault(); + // @ts-expect-error searchState.defocus(); + // @ts-expect-error window.hideAllModals(true); // true = reset focus for tooltips } + // @ts-expect-error function handleShortcut(ev) { // Don't interfere with browser shortcuts const disableShortcuts = getSettingValue("disable-shortcuts") === "true"; @@ -471,8 +534,11 @@ function preLoadCss(cssUrl) { return; } + // @ts-expect-error if (document.activeElement.tagName === "INPUT" && + // @ts-expect-error document.activeElement.type !== "checkbox" && + // @ts-expect-error document.activeElement.type !== "radio") { switch (getVirtualKey(ev)) { case "Escape": @@ -489,6 +555,7 @@ function preLoadCss(cssUrl) { case "S": case "/": ev.preventDefault(); + // @ts-expect-error searchState.focus(); break; @@ -515,6 +582,7 @@ function preLoadCss(cssUrl) { document.addEventListener("keydown", handleShortcut); function addSidebarItems() { + // @ts-expect-error if (!window.SIDEBAR_ITEMS) { return; } @@ -529,6 +597,7 @@ function preLoadCss(cssUrl) { * "Modules", or "Macros". */ function block(shortty, id, longty) { + // @ts-expect-error const filtered = window.SIDEBAR_ITEMS[shortty]; if (!filtered) { return; @@ -564,7 +633,9 @@ function preLoadCss(cssUrl) { li.appendChild(link); ul.appendChild(li); } + // @ts-expect-error sidebar.appendChild(h3); + // @ts-expect-error sidebar.appendChild(ul); } @@ -600,6 +671,7 @@ function preLoadCss(cssUrl) { } // + // @ts-expect-error window.register_implementors = imp => { const implementors = document.getElementById("implementors-list"); const synthetic_implementors = document.getElementById("synthetic-implementors-list"); @@ -615,18 +687,22 @@ function preLoadCss(cssUrl) { // // By the way, this is only used by and useful for traits implemented automatically // (like "Send" and "Sync"). + // @ts-expect-error onEachLazy(synthetic_implementors.getElementsByClassName("impl"), el => { const aliases = el.getAttribute("data-aliases"); if (!aliases) { return; } + // @ts-expect-error aliases.split(",").forEach(alias => { inlined_types.add(alias); }); }); } + // @ts-expect-error let currentNbImpls = implementors.getElementsByClassName("impl").length; + // @ts-expect-error const traitName = document.querySelector(".main-heading h1 > .trait").textContent; const baseIdName = "impl-" + traitName + "-"; const libs = Object.getOwnPropertyNames(imp); @@ -636,6 +712,7 @@ function preLoadCss(cssUrl) { const script = document .querySelector("script[data-ignore-extern-crates]"); const ignoreExternCrates = new Set( + // @ts-expect-error (script ? script.getAttribute("data-ignore-extern-crates") : "").split(","), ); for (const lib of libs) { @@ -663,6 +740,7 @@ function preLoadCss(cssUrl) { code.innerHTML = struct[TEXT_IDX]; addClass(code, "code-header"); + // @ts-expect-error onEachLazy(code.getElementsByTagName("a"), elem => { const href = elem.getAttribute("href"); @@ -681,12 +759,15 @@ function preLoadCss(cssUrl) { addClass(display, "impl"); display.appendChild(anchor); display.appendChild(code); + // @ts-expect-error list.appendChild(display); currentNbImpls += 1; } } }; + // @ts-expect-error if (window.pending_implementors) { + // @ts-expect-error window.register_implementors(window.pending_implementors); } @@ -719,12 +800,15 @@ function preLoadCss(cssUrl) { * * - After processing all of the impls, it sorts the sidebar items by name. * - * @param {{[cratename: string]: Array>}} impl + * @param {{[cratename: string]: Array>}} imp */ + // @ts-expect-error window.register_type_impls = imp => { + // @ts-expect-error if (!imp || !imp[window.currentCrate]) { return; } + // @ts-expect-error window.pending_type_impls = null; const idMap = new Map(); @@ -744,6 +828,7 @@ function preLoadCss(cssUrl) { let associatedConstants = document.querySelector(".sidebar .block.associatedconstant"); let sidebarTraitList = document.querySelector(".sidebar .block.trait-implementation"); + // @ts-expect-error for (const impList of imp[window.currentCrate]) { const types = impList.slice(2); const text = impList[0]; @@ -772,20 +857,28 @@ function preLoadCss(cssUrl) { h.appendChild(link); trait_implementations = outputList; trait_implementations_header = outputListHeader; + // @ts-expect-error sidebarSection.appendChild(h); sidebarTraitList = document.createElement("ul"); sidebarTraitList.className = "block trait-implementation"; + // @ts-expect-error sidebarSection.appendChild(sidebarTraitList); + // @ts-expect-error mainContent.appendChild(outputListHeader); + // @ts-expect-error mainContent.appendChild(outputList); } else { implementations = outputList; if (trait_implementations) { + // @ts-expect-error mainContent.insertBefore(outputListHeader, trait_implementations_header); + // @ts-expect-error mainContent.insertBefore(outputList, trait_implementations_header); } else { const mainContent = document.querySelector("#main-content"); + // @ts-expect-error mainContent.appendChild(outputListHeader); + // @ts-expect-error mainContent.appendChild(outputList); } } @@ -793,6 +886,7 @@ function preLoadCss(cssUrl) { const template = document.createElement("template"); template.innerHTML = text; + // @ts-expect-error onEachLazy(template.content.querySelectorAll("a"), elem => { const href = elem.getAttribute("href"); @@ -800,6 +894,7 @@ function preLoadCss(cssUrl) { elem.setAttribute("href", window.rootPath + href); } }); + // @ts-expect-error onEachLazy(template.content.querySelectorAll("[id]"), el => { let i = 0; if (idMap.has(el.id)) { @@ -817,6 +912,7 @@ function preLoadCss(cssUrl) { const oldHref = `#${el.id}`; const newHref = `#${el.id}-${i}`; el.id = `${el.id}-${i}`; + // @ts-expect-error onEachLazy(template.content.querySelectorAll("a[href]"), link => { if (link.getAttribute("href") === oldHref) { link.href = newHref; @@ -830,11 +926,14 @@ function preLoadCss(cssUrl) { if (isTrait) { const li = document.createElement("li"); const a = document.createElement("a"); + // @ts-expect-error a.href = `#${template.content.querySelector(".impl").id}`; a.textContent = traitName; li.appendChild(a); + // @ts-expect-error sidebarTraitList.append(li); } else { + // @ts-expect-error onEachLazy(templateAssocItems, item => { let block = hasClass(item, "associatedtype") ? associatedTypes : ( hasClass(item, "associatedconstant") ? associatedConstants : ( @@ -856,10 +955,14 @@ function preLoadCss(cssUrl) { const insertionReference = methods || sidebarTraitList; if (insertionReference) { const insertionReferenceH = insertionReference.previousElementSibling; + // @ts-expect-error sidebarSection.insertBefore(blockHeader, insertionReferenceH); + // @ts-expect-error sidebarSection.insertBefore(block, insertionReferenceH); } else { + // @ts-expect-error sidebarSection.appendChild(blockHeader); + // @ts-expect-error sidebarSection.appendChild(block); } if (hasClass(item, "associatedtype")) { @@ -896,11 +999,14 @@ function preLoadCss(cssUrl) { list.replaceChildren(...newChildren); } }; + // @ts-expect-error if (window.pending_type_impls) { + // @ts-expect-error window.register_type_impls(window.pending_type_impls); } function addSidebarCrates() { + // @ts-expect-error if (!window.ALL_CRATES) { return; } @@ -914,6 +1020,7 @@ function preLoadCss(cssUrl) { const ul = document.createElement("ul"); ul.className = "block crate"; + // @ts-expect-error for (const crate of window.ALL_CRATES) { const link = document.createElement("a"); link.href = window.rootPath + crate + "/index.html"; @@ -933,17 +1040,20 @@ function preLoadCss(cssUrl) { function expandAllDocs() { const innerToggle = document.getElementById(toggleAllDocsId); removeClass(innerToggle, "will-expand"); + // @ts-expect-error onEachLazy(document.getElementsByClassName("toggle"), e => { if (!hasClass(e, "type-contents-toggle") && !hasClass(e, "more-examples-toggle")) { e.open = true; } }); + // @ts-expect-error innerToggle.children[0].innerText = "Summary"; } function collapseAllDocs() { const innerToggle = document.getElementById(toggleAllDocsId); addClass(innerToggle, "will-expand"); + // @ts-expect-error onEachLazy(document.getElementsByClassName("toggle"), e => { if (e.parentNode.id !== "implementations-list" || (!hasClass(e, "implementors-toggle") && @@ -952,6 +1062,7 @@ function preLoadCss(cssUrl) { e.open = false; } }); + // @ts-expect-error innerToggle.children[0].innerText = "Show all"; } @@ -977,9 +1088,11 @@ function preLoadCss(cssUrl) { const hideImplementations = getSettingValue("auto-hide-trait-implementations") === "true"; const hideLargeItemContents = getSettingValue("auto-hide-large-items") !== "false"; + // @ts-expect-error function setImplementorsTogglesOpen(id, open) { const list = document.getElementById(id); if (list !== null) { + // @ts-expect-error onEachLazy(list.getElementsByClassName("implementors-toggle"), e => { e.open = open; }); @@ -991,6 +1104,7 @@ function preLoadCss(cssUrl) { setImplementorsTogglesOpen("blanket-implementations-list", false); } + // @ts-expect-error onEachLazy(document.getElementsByClassName("toggle"), e => { if (!hideLargeItemContents && hasClass(e, "type-contents-toggle")) { e.open = true; @@ -1002,6 +1116,7 @@ function preLoadCss(cssUrl) { }); }()); + // @ts-expect-error window.rustdoc_add_line_numbers_to_examples = () => { if (document.querySelector(".rustdoc.src")) { // We are in the source code page, nothing to be done here! @@ -1009,6 +1124,7 @@ function preLoadCss(cssUrl) { } onEachLazy(document.querySelectorAll( ":not(.scraped-example) > .example-wrap > pre:not(.example-line-numbers)", + // @ts-expect-error ), x => { const parent = x.parentNode; const line_numbers = parent.querySelectorAll(".example-line-numbers"); @@ -1027,33 +1143,41 @@ function preLoadCss(cssUrl) { }); }; + // @ts-expect-error window.rustdoc_remove_line_numbers_from_examples = () => { + // @ts-expect-error onEachLazy(document.querySelectorAll(".example-wrap > .example-line-numbers"), x => { x.parentNode.removeChild(x); }); }; if (getSettingValue("line-numbers") === "true") { + // @ts-expect-error window.rustdoc_add_line_numbers_to_examples(); } function showSidebar() { + // @ts-expect-error window.hideAllModals(false); const sidebar = document.getElementsByClassName("sidebar")[0]; + // @ts-expect-error addClass(sidebar, "shown"); } function hideSidebar() { const sidebar = document.getElementsByClassName("sidebar")[0]; + // @ts-expect-error removeClass(sidebar, "shown"); } window.addEventListener("resize", () => { + // @ts-expect-error if (window.CURRENT_TOOLTIP_ELEMENT) { // As a workaround to the behavior of `contains: layout` used in doc togglers, // tooltip popovers are positioned using javascript. // // This means when the window is resized, we need to redo the layout. + // @ts-expect-error const base = window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE; const force_visible = base.TOOLTIP_FORCE_VISIBLE; hideTooltip(false); @@ -1069,6 +1193,7 @@ function preLoadCss(cssUrl) { mainElem.addEventListener("click", hideSidebar); } + // @ts-expect-error onEachLazy(document.querySelectorAll("a[href^='#']"), el => { // For clicks on internal links ( tags with a hash property), we expand the section we're // jumping to *before* jumping there. We can't do this in onHashChange, because it changes @@ -1079,7 +1204,9 @@ function preLoadCss(cssUrl) { }); }); + // @ts-expect-error onEachLazy(document.querySelectorAll(".toggle > summary:not(.hideme)"), el => { + // @ts-expect-error el.addEventListener("click", e => { if (e.target.tagName !== "SUMMARY" && e.target.tagName !== "A") { e.preventDefault(); @@ -1090,15 +1217,17 @@ function preLoadCss(cssUrl) { /** * Show a tooltip immediately. * - * @param {DOMElement} e - The tooltip's anchor point. The DOM is consulted to figure - * out what the tooltip should contain, and where it should be - * positioned. + * @param {HTMLElement} e - The tooltip's anchor point. The DOM is consulted to figure + * out what the tooltip should contain, and where it should be + * positioned. */ function showTooltip(e) { const notable_ty = e.getAttribute("data-notable-ty"); + // @ts-expect-error if (!window.NOTABLE_TRAITS && notable_ty) { const data = document.getElementById("notable-traits-data"); if (data) { + // @ts-expect-error window.NOTABLE_TRAITS = JSON.parse(data.innerText); } else { throw new Error("showTooltip() called with notable without any notable traits!"); @@ -1106,36 +1235,44 @@ function preLoadCss(cssUrl) { } // Make this function idempotent. If the tooltip is already shown, avoid doing extra work // and leave it alone. + // @ts-expect-error if (window.CURRENT_TOOLTIP_ELEMENT && window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE === e) { + // @ts-expect-error clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT); return; } + // @ts-expect-error window.hideAllModals(false); const wrapper = document.createElement("div"); if (notable_ty) { wrapper.innerHTML = "
" + + // @ts-expect-error window.NOTABLE_TRAITS[notable_ty] + "
"; } else { // Replace any `title` attribute with `data-title` to avoid double tooltips. - if (e.getAttribute("title") !== null) { - e.setAttribute("data-title", e.getAttribute("title")); + const ttl = e.getAttribute("title"); + if (ttl !== null) { + e.setAttribute("data-title", ttl); e.removeAttribute("title"); } - if (e.getAttribute("data-title") !== null) { + const dttl = e.getAttribute("data-title"); + if (dttl !== null) { const titleContent = document.createElement("div"); titleContent.className = "content"; - titleContent.appendChild(document.createTextNode(e.getAttribute("data-title"))); + titleContent.appendChild(document.createTextNode(dttl)); wrapper.appendChild(titleContent); } } wrapper.className = "tooltip popover"; const focusCatcher = document.createElement("div"); focusCatcher.setAttribute("tabindex", "0"); + // @ts-expect-error focusCatcher.onfocus = hideTooltip; wrapper.appendChild(focusCatcher); const pos = e.getBoundingClientRect(); // 5px overlap so that the mouse can easily travel from place to place wrapper.style.top = (pos.top + window.scrollY + pos.height) + "px"; + // @ts-expect-error wrapper.style.left = 0; wrapper.style.right = "auto"; wrapper.style.visibility = "hidden"; @@ -1152,8 +1289,11 @@ function preLoadCss(cssUrl) { ); } wrapper.style.visibility = ""; + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT = wrapper; + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE = e; + // @ts-expect-error clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT); wrapper.onpointerenter = ev => { // If this is a synthetic touch event, ignore it. A click event will be along shortly. @@ -1164,7 +1304,7 @@ function preLoadCss(cssUrl) { }; wrapper.onpointerleave = ev => { // If this is a synthetic touch event, ignore it. A click event will be along shortly. - if (ev.pointerType !== "mouse") { + if (ev.pointerType !== "mouse" || !(ev.relatedTarget instanceof HTMLElement)) { return; } if (!e.TOOLTIP_FORCE_VISIBLE && !e.contains(ev.relatedTarget)) { @@ -1180,23 +1320,27 @@ function preLoadCss(cssUrl) { * was called, that timeout gets cleared. If the tooltip is already in the requested state, * this function will still clear any pending timeout, but otherwise do nothing. * - * @param {DOMElement} element - The tooltip's anchor point. The DOM is consulted to figure - * out what the tooltip should contain, and where it should be - * positioned. + * @param {HTMLElement} element - The tooltip's anchor point. The DOM is consulted to figure + * out what the tooltip should contain, and where it should be + * positioned. * @param {boolean} show - If true, the tooltip will be made visible. If false, it will * be hidden. */ function setTooltipHoverTimeout(element, show) { clearTooltipHoverTimeout(element); + // @ts-expect-error if (!show && !window.CURRENT_TOOLTIP_ELEMENT) { // To "hide" an already hidden element, just cancel its timeout. return; } + // @ts-expect-error if (show && window.CURRENT_TOOLTIP_ELEMENT) { // To "show" an already visible element, just cancel its timeout. return; } + // @ts-expect-error if (window.CURRENT_TOOLTIP_ELEMENT && + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE !== element) { // Don't do anything if another tooltip is already visible. return; @@ -1214,22 +1358,29 @@ function preLoadCss(cssUrl) { * If a show/hide timeout was set by `setTooltipHoverTimeout`, cancel it. If none exists, * do nothing. * - * @param {DOMElement} element - The tooltip's anchor point, - * as passed to `setTooltipHoverTimeout`. + * @param {HTMLElement} element - The tooltip's anchor point, + * as passed to `setTooltipHoverTimeout`. */ function clearTooltipHoverTimeout(element) { if (element.TOOLTIP_HOVER_TIMEOUT !== undefined) { + // @ts-expect-error removeClass(window.CURRENT_TOOLTIP_ELEMENT, "fade-out"); clearTimeout(element.TOOLTIP_HOVER_TIMEOUT); delete element.TOOLTIP_HOVER_TIMEOUT; } } + // @ts-expect-error function tooltipBlurHandler(event) { + // @ts-expect-error if (window.CURRENT_TOOLTIP_ELEMENT && + // @ts-expect-error !window.CURRENT_TOOLTIP_ELEMENT.contains(document.activeElement) && + // @ts-expect-error !window.CURRENT_TOOLTIP_ELEMENT.contains(event.relatedTarget) && + // @ts-expect-error !window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.contains(document.activeElement) && + // @ts-expect-error !window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.contains(event.relatedTarget) ) { // Work around a difference in the focus behaviour between Firefox, Chrome, and Safari. @@ -1251,32 +1402,45 @@ function preLoadCss(cssUrl) { * If set to `false`, leave keyboard focus alone. */ function hideTooltip(focus) { + // @ts-expect-error if (window.CURRENT_TOOLTIP_ELEMENT) { + // @ts-expect-error if (window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE) { if (focus) { + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.focus(); } + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT.TOOLTIP_BASE.TOOLTIP_FORCE_VISIBLE = false; } + // @ts-expect-error document.body.removeChild(window.CURRENT_TOOLTIP_ELEMENT); + // @ts-expect-error clearTooltipHoverTimeout(window.CURRENT_TOOLTIP_ELEMENT); + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT = null; } } + // @ts-expect-error onEachLazy(document.getElementsByClassName("tooltip"), e => { e.onclick = () => { e.TOOLTIP_FORCE_VISIBLE = e.TOOLTIP_FORCE_VISIBLE ? false : true; + // @ts-expect-error if (window.CURRENT_TOOLTIP_ELEMENT && !e.TOOLTIP_FORCE_VISIBLE) { hideTooltip(true); } else { showTooltip(e); + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT.setAttribute("tabindex", "0"); + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT.focus(); + // @ts-expect-error window.CURRENT_TOOLTIP_ELEMENT.onblur = tooltipBlurHandler; } return false; }; + // @ts-expect-error e.onpointerenter = ev => { // If this is a synthetic touch event, ignore it. A click event will be along shortly. if (ev.pointerType !== "mouse") { @@ -1284,6 +1448,7 @@ function preLoadCss(cssUrl) { } setTooltipHoverTimeout(e, true); }; + // @ts-expect-error e.onpointermove = ev => { // If this is a synthetic touch event, ignore it. A click event will be along shortly. if (ev.pointerType !== "mouse") { @@ -1291,12 +1456,15 @@ function preLoadCss(cssUrl) { } setTooltipHoverTimeout(e, true); }; + // @ts-expect-error e.onpointerleave = ev => { // If this is a synthetic touch event, ignore it. A click event will be along shortly. if (ev.pointerType !== "mouse") { return; } + // @ts-expect-error if (!e.TOOLTIP_FORCE_VISIBLE && window.CURRENT_TOOLTIP_ELEMENT && + // @ts-expect-error !window.CURRENT_TOOLTIP_ELEMENT.contains(ev.relatedTarget)) { // Tooltip pointer leave gesture: // @@ -1329,6 +1497,7 @@ function preLoadCss(cssUrl) { // * https://www.nngroup.com/articles/tooltip-guidelines/ // * https://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown setTooltipHoverTimeout(e, false); + // @ts-expect-error addClass(window.CURRENT_TOOLTIP_ELEMENT, "fade-out"); } }; @@ -1338,6 +1507,7 @@ function preLoadCss(cssUrl) { if (sidebar_menu_toggle) { sidebar_menu_toggle.addEventListener("click", () => { const sidebar = document.getElementsByClassName("sidebar")[0]; + // @ts-expect-error if (!hasClass(sidebar, "shown")) { showSidebar(); } else { @@ -1346,12 +1516,18 @@ function preLoadCss(cssUrl) { }); } + // @ts-expect-error function helpBlurHandler(event) { + // @ts-expect-error if (!getHelpButton().contains(document.activeElement) && + // @ts-expect-error !getHelpButton().contains(event.relatedTarget) && + // @ts-expect-error !getSettingsButton().contains(document.activeElement) && + // @ts-expect-error !getSettingsButton().contains(event.relatedTarget) ) { + // @ts-expect-error window.hidePopoverMenus(); } } @@ -1427,14 +1603,18 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm if (isHelpPage) { const help_section = document.createElement("section"); help_section.appendChild(container); + // @ts-expect-error document.getElementById("main-content").appendChild(help_section); container.style.display = "block"; } else { const help_button = getHelpButton(); + // @ts-expect-error help_button.appendChild(container); container.onblur = helpBlurHandler; + // @ts-expect-error help_button.onblur = helpBlurHandler; + // @ts-expect-error help_button.children[0].onblur = helpBlurHandler; } @@ -1446,8 +1626,10 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm * * Pass "true" to reset focus for tooltip popovers. */ + // @ts-expect-error window.hideAllModals = switchFocus => { hideSidebar(); + // @ts-expect-error window.hidePopoverMenus(); hideTooltip(switchFocus); }; @@ -1455,7 +1637,9 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm /** * Hide all the popover menus. */ + // @ts-expect-error window.hidePopoverMenus = () => { + // @ts-expect-error onEachLazy(document.querySelectorAll("rustdoc-toolbar .popover"), elem => { elem.style.display = "none"; }); @@ -1474,10 +1658,12 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm * @return {HTMLElement} */ function getHelpMenu(buildNeeded) { + // @ts-expect-error let menu = getHelpButton().querySelector(".popover"); if (!menu && buildNeeded) { menu = buildHelpMenu(); } + // @ts-expect-error return menu; } @@ -1489,9 +1675,11 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // other modals. const button = getHelpButton(); addClass(button, "help-open"); + // @ts-expect-error button.querySelector("a").focus(); const menu = getHelpMenu(true); if (menu.style.display === "none") { + // @ts-expect-error window.hideAllModals(); menu.style.display = ""; } @@ -1506,8 +1694,11 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // If user clicks with a moderator, though, use default browser behavior, // probably opening in a new window or tab. if (!helpLink.contains(helpLink) || + // @ts-expect-error event.ctrlKey || + // @ts-expect-error event.altKey || + // @ts-expect-error event.metaKey) { return; } @@ -1517,6 +1708,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm if (shouldShowHelp) { showHelp(); } else { + // @ts-expect-error window.hidePopoverMenus(); } }); @@ -1527,6 +1719,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm addSidebarCrates(); onHashChange(null); window.addEventListener("hashchange", onHashChange); + // @ts-expect-error searchState.setup(); }()); @@ -1580,6 +1773,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm removeClass(document.documentElement, "hide-sidebar"); updateLocalStorage("hide-sidebar", "false"); if (document.querySelector(".rustdoc.src")) { + // @ts-expect-error window.rustdocToggleSrcSidebar(); } e.preventDefault(); @@ -1589,6 +1783,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // Pointer capture. // // Resizing is a single-pointer gesture. Any secondary pointer is ignored + // @ts-expect-error let currentPointerId = null; // "Desired" sidebar size. @@ -1596,6 +1791,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // This is stashed here for window resizing. If the sidebar gets // shrunk to maintain BODY_MIN, and then the user grows the window again, // it gets the sidebar to restore its size. + // @ts-expect-error let desiredSidebarSize = null; // Sidebar resize debouncer. @@ -1626,7 +1822,9 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // through that size when using the shrink-to-nothing gesture. function hideSidebar() { if (isSrcPage) { + // @ts-expect-error window.rustdocCloseSourceSidebar(); + // @ts-expect-error updateLocalStorage("src-sidebar-width", null); // [RUSTDOCIMPL] CSS variable fast path // @@ -1639,14 +1837,19 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // // So, to clear it, we need to clear all three. document.documentElement.style.removeProperty("--src-sidebar-width"); + // @ts-expect-error sidebar.style.removeProperty("--src-sidebar-width"); + // @ts-expect-error resizer.style.removeProperty("--src-sidebar-width"); } else { addClass(document.documentElement, "hide-sidebar"); updateLocalStorage("hide-sidebar", "true"); + // @ts-expect-error updateLocalStorage("desktop-sidebar-width", null); document.documentElement.style.removeProperty("--desktop-sidebar-width"); + // @ts-expect-error sidebar.style.removeProperty("--desktop-sidebar-width"); + // @ts-expect-error resizer.style.removeProperty("--desktop-sidebar-width"); } } @@ -1659,6 +1862,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // remains visible all the time on there. function showSidebar() { if (isSrcPage) { + // @ts-expect-error window.rustdocShowSourceSidebar(); } else { removeClass(document.documentElement, "hide-sidebar"); @@ -1674,6 +1878,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm */ function changeSidebarSize(size) { if (isSrcPage) { + // @ts-expect-error updateLocalStorage("src-sidebar-width", size); // [RUSTDOCIMPL] CSS variable fast path // @@ -1681,11 +1886,16 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // because the sidebar isn't actually loaded yet, // we scope this update to the sidebar to avoid hitting a slow // path in WebKit. + // @ts-expect-error sidebar.style.setProperty("--src-sidebar-width", size + "px"); + // @ts-expect-error resizer.style.setProperty("--src-sidebar-width", size + "px"); } else { + // @ts-expect-error updateLocalStorage("desktop-sidebar-width", size); + // @ts-expect-error sidebar.style.setProperty("--desktop-sidebar-width", size + "px"); + // @ts-expect-error resizer.style.setProperty("--desktop-sidebar-width", size + "px"); } } @@ -1701,7 +1911,9 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // Respond to the resize handle event. // This function enforces size constraints, and implements the // shrink-to-nothing gesture based on thresholds defined above. + // @ts-expect-error function resize(e) { + // @ts-expect-error if (currentPointerId === null || currentPointerId !== e.pointerId) { return; } @@ -1719,15 +1931,19 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm changeSidebarSize(constrainedPos); desiredSidebarSize = constrainedPos; if (pendingSidebarResizingFrame !== false) { + // @ts-expect-error clearTimeout(pendingSidebarResizingFrame); } + // @ts-expect-error pendingSidebarResizingFrame = setTimeout(() => { + // @ts-expect-error if (currentPointerId === null || pendingSidebarResizingFrame === false) { return; } pendingSidebarResizingFrame = false; document.documentElement.style.setProperty( "--resizing-sidebar-width", + // @ts-expect-error desiredSidebarSize + "px", ); }, 100); @@ -1739,51 +1955,69 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm return; } stopResize(); + // @ts-expect-error if (desiredSidebarSize >= (window.innerWidth - BODY_MIN)) { changeSidebarSize(window.innerWidth - BODY_MIN); + // @ts-expect-error } else if (desiredSidebarSize !== null && desiredSidebarSize > SIDEBAR_MIN) { + // @ts-expect-error changeSidebarSize(desiredSidebarSize); } }); + // @ts-expect-error function stopResize(e) { + // @ts-expect-error if (currentPointerId === null) { return; } if (e) { e.preventDefault(); } + // @ts-expect-error desiredSidebarSize = sidebar.getBoundingClientRect().width; + // @ts-expect-error removeClass(resizer, "active"); window.removeEventListener("pointermove", resize, false); window.removeEventListener("pointerup", stopResize, false); removeClass(document.documentElement, "sidebar-resizing"); document.documentElement.style.removeProperty( "--resizing-sidebar-width"); + // @ts-expect-error if (resizer.releasePointerCapture) { + // @ts-expect-error resizer.releasePointerCapture(currentPointerId); currentPointerId = null; } } + // @ts-expect-error function initResize(e) { + // @ts-expect-error if (currentPointerId !== null || e.altKey || e.ctrlKey || e.metaKey || e.button !== 0) { return; } + // @ts-expect-error if (resizer.setPointerCapture) { + // @ts-expect-error resizer.setPointerCapture(e.pointerId); + // @ts-expect-error if (!resizer.hasPointerCapture(e.pointerId)) { // unable to capture pointer; something else has it // on iOS, this usually means you long-clicked a link instead + // @ts-expect-error resizer.releasePointerCapture(e.pointerId); return; } currentPointerId = e.pointerId; } + // @ts-expect-error window.hideAllModals(false); e.preventDefault(); window.addEventListener("pointermove", resize, false); window.addEventListener("pointercancel", stopResize, false); window.addEventListener("pointerup", stopResize, false); + // @ts-expect-error addClass(resizer, "active"); addClass(document.documentElement, "sidebar-resizing"); + // @ts-expect-error const pos = e.clientX - sidebar.offsetLeft - 3; document.documentElement.style.setProperty( "--resizing-sidebar-width", pos + "px"); desiredSidebarSize = null; @@ -1795,6 +2029,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // and the copy buttons on the code examples. (function() { // Common functions to copy buttons. + // @ts-expect-error function copyContentToClipboard(content) { const el = document.createElement("textarea"); el.value = content; @@ -1809,6 +2044,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm document.body.removeChild(el); } + // @ts-expect-error function copyButtonAnimation(button) { button.classList.add("clicked"); @@ -1831,6 +2067,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm // Most page titles are ' in - Rust', except // modules (which don't have the first part) and keywords/primitives // (which don't have a module path) + // @ts-expect-error const title = document.querySelector("title").textContent.replace(" - Rust", ""); const [item, module] = title.split(" in "); const path = [item]; @@ -1843,6 +2080,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm }; // Copy buttons on code examples. + // @ts-expect-error function copyCode(codeElem) { if (!codeElem) { // Should never happen, but the world is a dark and dangerous place. @@ -1851,6 +2089,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm copyContentToClipboard(codeElem.textContent); } + // @ts-expect-error function getExampleWrap(event) { let elem = event.target; while (!hasClass(elem, "example-wrap")) { @@ -1866,6 +2105,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm return elem; } + // @ts-expect-error function addCopyButton(event) { const elem = getExampleWrap(event); if (elem === null) { @@ -1896,9 +2136,11 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm return; } const scrapedWrapped = elem.parentElement; + // @ts-expect-error window.updateScrapedExample(scrapedWrapped, parent); } + // @ts-expect-error function showHideCodeExampleButtons(event) { const elem = getExampleWrap(event); if (elem === null) { @@ -1917,6 +2159,7 @@ href="https://doc.rust-lang.org/${channel}/rustdoc/read-documentation/search.htm buttons.classList.toggle("keep-visible"); } + // @ts-expect-error onEachLazy(document.querySelectorAll(".docblock .example-wrap"), elem => { elem.addEventListener("mouseover", addCopyButton); elem.addEventListener("click", showHideCodeExampleButtons); diff --git a/src/librustdoc/html/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts new file mode 100644 index 0000000000000..18a3e22113b8b --- /dev/null +++ b/src/librustdoc/html/static/js/rustdoc.d.ts @@ -0,0 +1,387 @@ +// This file contains type definitions that are processed by the TypeScript Compiler but are +// not put into the JavaScript we include as part of the documentation. It is used for +// type checking. See README.md in this directory for more info. + +/* eslint-disable */ +declare global { + interface Window { + /** Make the current theme easy to find */ + currentTheme: HTMLLinkElement|null; + /** Used by the popover tooltip code. */ + RUSTDOC_TOOLTIP_HOVER_MS: number; + /** Used by the popover tooltip code. */ + RUSTDOC_TOOLTIP_HOVER_EXIT_MS: number; + /** Search engine data used by main.js and search.js */ + searchState: rustdoc.SearchState; + /** Global option, with a long list of "../"'s */ + rootPath: string|null; + /** + * Currently opened crate. + * As a multi-page application, we know this never changes once set. + */ + currentCrate: string|null; + } + interface HTMLElement { + /** Used by the popover tooltip code. */ + TOOLTIP_FORCE_VISIBLE: boolean|undefined, + /** Used by the popover tooltip code */ + TOOLTIP_HOVER_TIMEOUT: Timeout|undefined, + } +} + +export = rustdoc; + +declare namespace rustdoc { + interface SearchState { + rustdocToolbar: HTMLElement|null; + loadingText: string; + input: HTMLInputElement|null; + title: string; + titleBeforeSearch: string; + timeout: number|null; + currentTab: number; + focusedByTab: [number|null, number|null, number|null]; + clearInputTimeout: function; + outputElement: function(): HTMLElement|null; + focus: function(); + defocus: function(); + showResults: function(HTMLElement|null|undefined); + removeQueryParameters: function(); + hideResults: function(); + getQueryStringParams: function(): Object.; + origPlaceholder: string; + setup: function(); + setLoadingSearch: function(); + descShards: Map; + loadDesc: function({descShard: SearchDescShard, descIndex: number}): Promise; + loadedDescShard: function(string, number, string); + isDisplayed: function(): boolean, + } + + interface SearchDescShard { + crate: string; + promise: Promise|null; + resolve: function(string[])|null; + shard: number; + } + + /** + * A single parsed "atom" in a search query. For example, + * + * std::fmt::Formatter, Write -> Result<()> + * ┏━━━━━━━━━━━━━━━━━━ ┌──── ┏━━━━━┅┅┅┅┄┄┄┄┄┄┄┄┄┄┄┄┄┄┐ + * ┃ │ ┗ QueryElement { ┊ + * ┃ │ name: Result ┊ + * ┃ │ generics: [ ┊ + * ┃ │ QueryElement ┘ + * ┃ │ name: () + * ┃ │ ] + * ┃ │ } + * ┃ └ QueryElement { + * ┃ name: Write + * ┃ } + * ┗ QueryElement { + * name: Formatter + * pathWithoutLast: std::fmt + * } + */ + interface QueryElement { + name: string, + id: number|null, + fullPath: Array, + pathWithoutLast: Array, + pathLast: string, + normalizedPathLast: string, + generics: Array, + bindings: Map>, + typeFilter: number|null, + } + + /** + * Same as QueryElement, but bindings and typeFilter support strings + */ + interface ParserQueryElement { + name: string, + id: number|null, + fullPath: Array, + pathWithoutLast: Array, + pathLast: string, + normalizedPathLast: string, + generics: Array, + bindings: Map>, + bindingName: {name: string, generics: ParserQueryElement[]}|null, + typeFilter: string|null, + } + + /** + * Intermediate parser state. Discarded when parsing is done. + */ + interface ParserState { + pos: number; + length: number; + totalElems: number; + genericsElems: number; + typeFilter: (null|string); + userQuery: string; + isInBinding: (null|{name: string, generics: ParserQueryElement[]}); + } + + /** + * A complete parsed query. + */ + interface ParsedQuery { + userQuery: string, + elems: Array, + returned: Array, + foundElems: number, + totalElems: number, + literalSearch: boolean, + hasReturnArrow: boolean, + correction: string|null, + proposeCorrectionFrom: string|null, + proposeCorrectionTo: string|null, + typeFingerprint: Uint32Array, + error: Array | null, + } + + /** + * An entry in the search index database. + */ + interface Row { + crate: string, + descShard: SearchDescShard, + id: number, + name: string, + normalizedName: string, + word: string, + parent: ({ty: number, name: string, path: string, exactPath: string}|null|undefined), + path: string, + ty: number, + type?: FunctionSearchType + } + + /** + * The viewmodel for the search engine results page. + */ + interface ResultsTable { + in_args: Array, + returned: Array, + others: Array, + query: ParsedQuery, + } + + type Results = Map; + + /** + * An annotated `Row`, used in the viewmodel. + */ + interface ResultObject { + desc: string, + displayPath: string, + fullPath: string, + href: string, + id: number, + dist: number, + path_dist: number, + name: string, + normalizedName: string, + word: string, + index: number, + parent: (Object|undefined), + path: string, + ty: number, + type?: FunctionSearchType, + paramNames?: string[], + displayType: Promise>>|null, + displayTypeMappedNames: Promise]>>|null, + item: Row, + dontValidate?: boolean, + } + + /** + * A pair of [inputs, outputs], or 0 for null. This is stored in the search index. + * The JavaScript deserializes this into FunctionSearchType. + * + * Numeric IDs are *ONE-indexed* into the paths array (`p`). Zero is used as a sentinel for `null` + * because `null` is four bytes while `0` is one byte. + * + * An input or output can be encoded as just a number if there is only one of them, AND + * it has no generics. The no generics rule exists to avoid ambiguity: imagine if you had + * a function with a single output, and that output had a single generic: + * + * fn something() -> Result + * + * If output was allowed to be any RawFunctionType, it would look like thi + * + * [[], [50, [3, 3]]] + * + * The problem is that the above output could be interpreted as either a type with ID 50 and two + * generics, or it could be interpreted as a pair of types, the first one with ID 50 and the second + * with ID 3 and a single generic parameter that is also ID 3. We avoid this ambiguity by choosing + * in favor of the pair of types interpretation. This is why the `(number|Array)` + * is used instead of `(RawFunctionType|Array)`. + * + * The output can be skipped if it's actually unit and there's no type constraints. If thi + * function accepts constrained generics, then the output will be unconditionally emitted, and + * after it will come a list of trait constraints. The position of the item in the list will + * determine which type parameter it is. For example: + * + * [1, 2, 3, 4, 5] + * ^ ^ ^ ^ ^ + * | | | | - generic parameter (-3) of trait 5 + * | | | - generic parameter (-2) of trait 4 + * | | - generic parameter (-1) of trait 3 + * | - this function returns a single value (type 2) + * - this function takes a single input parameter (type 1) + * + * Or, for a less contrived version: + * + * [[[4, -1], 3], [[5, -1]], 11] + * -^^^^^^^---- ^^^^^^^ ^^ + * | | | - generic parameter, roughly `where -1: 11` + * | | | since -1 is the type parameter and 11 the trait + * | | - function output 5<-1> + * | - the overall function signature is something like + * | `fn(4<-1>, 3) -> 5<-1> where -1: 11` + * - function input, corresponds roughly to 4<-1> + * 4 is an index into the `p` array for a type + * -1 is the generic parameter, given by 11 + * + * If a generic parameter has multiple trait constraints, it gets wrapped in an array, just like + * function inputs and outputs: + * + * [-1, -1, [4, 3]] + * ^^^^^^ where -1: 4 + 3 + * + * If a generic parameter's trait constraint has generic parameters, it gets wrapped in the array + * even if only one exists. In other words, the ambiguity of `4<3>` and `4 + 3` is resolved in + * favor of `4 + 3`: + * + * [-1, -1, [[4, 3]]] + * ^^^^^^^^ where -1: 4 + 3 + * + * [-1, -1, [5, [4, 3]]] + * ^^^^^^^^^^^ where -1: 5, -2: 4 + 3 + * + * If a generic parameter has no trait constraints (like in Rust, the `Sized` constraint i + * implied and a fake `?Sized` constraint used to note its absence), it will be filled in with 0. + */ + type RawFunctionSearchType = + 0 | + [(number|Array)] | + [(number|Array), (number|Array)] | + Array<(number|Array)> + ; + + /** + * A single function input or output type. This is either a single path ID, or a pair of + * [path ID, generics]. + * + * Numeric IDs are *ONE-indexed* into the paths array (`p`). Zero is used as a sentinel for `null` + * because `null` is four bytes while `0` is one byte. + */ + type RawFunctionType = number | [number, Array]; + + /** + * The type signature entry in the decoded search index. + * (The "Raw" objects are encoded differently to save space in the JSON). + */ + interface FunctionSearchType { + inputs: Array, + output: Array, + where_clause: Array>, + } + + /** + * A decoded function type, made from real objects. + * `ty` will be negative for generics, positive for types, and 0 for placeholders. + */ + interface FunctionType { + id: null|number, + ty: number|null, + name?: string, + path: string|null, + exactPath: string|null, + unboxFlag: boolean, + generics: Array, + bindings: Map>, + }; + + interface HighlightedFunctionType extends FunctionType { + generics: HighlightedFunctionType[], + bindings: Map, + highlighted?: boolean; + } + + interface FingerprintableType { + id: number|null; + generics: FingerprintableType[]; + bindings: Map; + }; + + /** + * The raw search data for a given crate. `n`, `t`, `d`, `i`, and `f` + * are arrays with the same length. `q`, `a`, and `c` use a sparse + * representation for compactness. + * + * `n[i]` contains the name of an item. + * + * `t[i]` contains the type of that item + * (as a string of characters that represent an offset in `itemTypes`). + * + * `d[i]` contains the description of that item. + * + * `q` contains the full paths of the items. For compactness, it is a set of + * (index, path) pairs used to create a map. If a given index `i` is + * not present, this indicates "same as the last index present". + * + * `i[i]` contains an item's parent, usually a module. For compactness, + * it is a set of indexes into the `p` array. + * + * `f` contains function signatures, or `0` if the item isn't a function. + * More information on how they're encoded can be found in rustc-dev-guide + * + * Functions are themselves encoded as arrays. The first item is a list of + * types representing the function's inputs, and the second list item is a list + * of types representing the function's output. Tuples are flattened. + * Types are also represented as arrays; the first item is an index into the `p` + * array, while the second is a list of types representing any generic parameters. + * + * b[i] contains an item's impl disambiguator. This is only present if an item + * is defined in an impl block and, the impl block's type has more than one associated + * item with the same name. + * + * `a` defines aliases with an Array of pairs: [name, offset], where `offset` + * points into the n/t/d/q/i/f arrays. + * + * `doc` contains the description of the crate. + * + * `p` is a list of path/type pairs. It is used for parents and function parameters. + * The first item is the type, the second is the name, the third is the visible path (if any) and + * the fourth is the canonical path used for deduplication (if any). + * + * `r` is the canonical path used for deduplication of re-exported items. + * It is not used for associated items like methods (that's the fourth element + * of `p`) but is used for modules items like free functions. + * + * `c` is an array of item indices that are deprecated. + */ + type RawSearchIndexCrate = { + doc: string, + a: Object, + n: Array, + t: string, + D: string, + e: string, + q: Array<[number, string]>, + i: string, + f: string, + p: Array<[number, string] | [number, string, number] | [number, string, number, number] | [number, string, number, number, string]>, + b: Array<[number, String]>, + c: string, + r: Array<[number, number]>, + P: Array<[number, string]>, + }; + + type VlqData = VlqData[] | number; +} diff --git a/src/librustdoc/html/static/js/scrape-examples.js b/src/librustdoc/html/static/js/scrape-examples.js index 98c53b8656f52..d08f15a5bfa86 100644 --- a/src/librustdoc/html/static/js/scrape-examples.js +++ b/src/librustdoc/html/static/js/scrape-examples.js @@ -1,5 +1,8 @@ /* global addClass, hasClass, removeClass, onEachLazy */ +// Eventually fix this. +// @ts-nocheck + "use strict"; (function() { diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index 660484c133c36..1ad32721e0687 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -10,11 +10,19 @@ if (!Array.prototype.toSpliced) { // Can't use arrow functions, because we want `this` Array.prototype.toSpliced = function() { const me = this.slice(); + // @ts-expect-error Array.prototype.splice.apply(me, arguments); return me; }; } +/** + * + * @template T + * @param {Iterable} arr + * @param {function(T): any} func + * @param {function(T): boolean} funcBtwn + */ function onEachBtwn(arr, func, funcBtwn) { let skipped = true; for (const value of arr) { @@ -98,9 +106,24 @@ const NO_TYPE_FILTER = -1; * documentation. */ const editDistanceState = { + /** + * @type {number[]} + */ current: [], + /** + * @type {number[]} + */ prev: [], + /** + * @type {number[]} + */ prevPrev: [], + /** + * @param {string} a + * @param {string} b + * @param {number} limit + * @returns + */ calculate: function calculate(a, b, limit) { // Ensure that `b` is the shorter string, minimizing memory use. if (a.length < b.length) { @@ -186,14 +209,28 @@ const editDistanceState = { }, }; +/** + * @param {string} a + * @param {string} b + * @param {number} limit + * @returns + */ function editDistance(a, b, limit) { return editDistanceState.calculate(a, b, limit); } +/** + * @param {string} c + * @returns {boolean} + */ function isEndCharacter(c) { return "=,>-])".indexOf(c) !== -1; } +/** + * @param {number} ty + * @returns + */ function isFnLikeTy(ty) { return ty === TY_FN || ty === TY_METHOD || ty === TY_TYMETHOD; } @@ -212,7 +249,7 @@ function isSeparatorCharacter(c) { /** * Returns `true` if the current parser position is starting with "->". * - * @param {ParserState} parserState + * @param {rustdoc.ParserState} parserState * * @return {boolean} */ @@ -223,7 +260,7 @@ function isReturnArrow(parserState) { /** * Increase current parser position until it doesn't find a whitespace anymore. * - * @param {ParserState} parserState + * @param {rustdoc.ParserState} parserState */ function skipWhitespace(parserState) { while (parserState.pos < parserState.userQuery.length) { @@ -238,7 +275,7 @@ function skipWhitespace(parserState) { /** * Returns `true` if the previous character is `lookingFor`. * - * @param {ParserState} parserState + * @param {rustdoc.ParserState} parserState * @param {String} lookingFor * * @return {boolean} @@ -260,8 +297,8 @@ function prevIs(parserState, lookingFor) { /** * Returns `true` if the last element in the `elems` argument has generics. * - * @param {Array} elems - * @param {ParserState} parserState + * @param {Array} elems + * @param {rustdoc.ParserState} parserState * * @return {boolean} */ @@ -270,6 +307,13 @@ function isLastElemGeneric(elems, parserState) { prevIs(parserState, ">"); } +/** + * + * @param {rustdoc.ParsedQuery} query + * @param {rustdoc.ParserState} parserState + * @param {rustdoc.ParserQueryElement[]} elems + * @param {boolean} isInGenerics + */ function getFilteredNextElem(query, parserState, elems, isInGenerics) { const start = parserState.pos; if (parserState.userQuery[parserState.pos] === ":" && !isPathStart(parserState)) { @@ -294,6 +338,8 @@ function getFilteredNextElem(query, parserState, elems, isInGenerics) { // The type filter doesn't count as an element since it's a modifier. const typeFilterElem = elems.pop(); checkExtraTypeFilterCharacters(start, parserState); + // typeFilterElem is not null. If it was, the elems.length check would have fired. + // @ts-expect-error parserState.typeFilter = typeFilterElem.normalizedPathLast; parserState.pos += 1; parserState.totalElems -= 1; @@ -309,12 +355,13 @@ function getFilteredNextElem(query, parserState, elems, isInGenerics) { * If there is no `endChar`, this function will implicitly stop at the end * without raising an error. * - * @param {ParsedQuery} query - * @param {ParserState} parserState - * @param {Array} elems - This is where the new {QueryElement} will be added. - * @param {string} endChar - This function will stop when it'll encounter this - * character. - * @returns {{foundSeparator: bool}} + * @param {rustdoc.ParsedQuery} query + * @param {rustdoc.ParserState} parserState + * @param {Array} elems + * - This is where the new {QueryElement} will be added. + * @param {string} endChar - This function will stop when it'll encounter this + * character. + * @returns {{foundSeparator: boolean}} */ function getItemsBefore(query, parserState, elems, endChar) { let foundStopChar = true; @@ -385,6 +432,7 @@ function getItemsBefore(query, parserState, elems, endChar) { throw ["Unexpected ", c, " after ", extra]; } if (!foundStopChar) { + /** @type {string[]} */ let extra = []; if (isLastElemGeneric(query.elems, parserState)) { extra = [" after ", ">"]; @@ -463,12 +511,14 @@ function getItemsBefore(query, parserState, elems, endChar) { } /** - * @param {ParsedQuery} query - * @param {ParserState} parserState - * @param {Array} elems - This is where the new {QueryElement} will be added. + * @param {rustdoc.ParsedQuery} query + * @param {rustdoc.ParserState} parserState + * @param {Array} elems + * - This is where the new {QueryElement} will be added. * @param {boolean} isInGenerics */ function getNextElem(query, parserState, elems, isInGenerics) { + /** @type {rustdoc.ParserQueryElement[]} */ const generics = []; skipWhitespace(parserState); @@ -588,6 +638,7 @@ function getNextElem(query, parserState, elems, isInGenerics) { getFilteredNextElem(query, parserState, generics, isInGenerics); generics[generics.length - 1].bindingName = makePrimitiveElement("output"); } else { + // @ts-expect-error generics.push(makePrimitiveElement(null, { bindingName: makePrimitiveElement("output"), typeFilter: null, @@ -640,7 +691,8 @@ function getNextElem(query, parserState, elems, isInGenerics) { * Checks that the type filter doesn't have unwanted characters like `<>` (which are ignored * if empty). * - * @param {ParserState} parserState + * @param {number} start + * @param {rustdoc.ParserState} parserState */ function checkExtraTypeFilterCharacters(start, parserState) { const query = parserState.userQuery.slice(start, parserState.pos).trim(); @@ -658,12 +710,13 @@ function checkExtraTypeFilterCharacters(start, parserState) { } /** - * @param {ParsedQuery} query - * @param {ParserState} parserState - * @param {string} name - Name of the query element. - * @param {Array} generics - List of generics of this query element. + * @param {rustdoc.ParsedQuery} query + * @param {rustdoc.ParserState} parserState + * @param {string} name - Name of the query element. + * @param {Array} generics - List of generics of this query element. + * @param {boolean} isInGenerics * - * @return {QueryElement} - The newly created `QueryElement`. + * @return {rustdoc.ParserQueryElement} - The newly created `QueryElement`. */ function createQueryElement(query, parserState, name, generics, isInGenerics) { const path = name.trim(); @@ -756,9 +809,15 @@ function createQueryElement(query, parserState, name, generics, isInGenerics) { }; } +/** + * + * @param {string} name + * @param {Object=} extra + * @returns {rustdoc.ParserQueryElement} + */ function makePrimitiveElement(name, extra) { return Object.assign({ - name, + name: name, id: null, fullPath: [name], pathWithoutLast: [], @@ -781,8 +840,8 @@ function makePrimitiveElement(name, extra) { * * There is more than one element. * * There is no closing `"`. * - * @param {ParsedQuery} query - * @param {ParserState} parserState + * @param {rustdoc.ParsedQuery} query + * @param {rustdoc.ParserState} parserState * @param {boolean} isInGenerics */ function getStringElem(query, parserState, isInGenerics) { @@ -813,9 +872,9 @@ function getStringElem(query, parserState, isInGenerics) { * character or the end of the query. It returns the position of the last * character of the ident. * - * @param {ParserState} parserState + * @param {rustdoc.ParserState} parserState * - * @return {integer} + * @return {number} */ function getIdentEndPosition(parserState) { let afterIdent = consumeIdent(parserState); @@ -890,6 +949,10 @@ function getIdentEndPosition(parserState) { return end; } +/** + * @param {string} c + * @returns + */ function isSpecialStartCharacter(c) { return "<\"".indexOf(c) !== -1; } @@ -897,7 +960,7 @@ function isSpecialStartCharacter(c) { /** * Returns `true` if the current parser position is starting with "::". * - * @param {ParserState} parserState + * @param {rustdoc.ParserState} parserState * * @return {boolean} */ @@ -909,7 +972,7 @@ function isPathStart(parserState) { * If the current parser position is at the beginning of an identifier, * move the position to the end of it and return `true`. Otherwise, return `false`. * - * @param {ParserState} parserState + * @param {rustdoc.ParserState} parserState * * @return {boolean} */ @@ -935,14 +998,25 @@ function isPathSeparator(c) { return c === ":" || c === " "; } +/** + * @template T + */ class VlqHexDecoder { + /** + * @param {string} string + * @param {function(rustdoc.VlqData): T} cons + */ constructor(string, cons) { this.string = string; this.cons = cons; this.offset = 0; + /** @type {T[]} */ this.backrefQueue = []; } - // call after consuming `{` + /** + * call after consuming `{` + * @returns {rustdoc.VlqData[]} + */ decodeList() { let c = this.string.charCodeAt(this.offset); const ret = []; @@ -953,7 +1027,10 @@ class VlqHexDecoder { this.offset += 1; // eat cb return ret; } - // consumes and returns a list or integer + /** + * consumes and returns a list or integer + * @returns {rustdoc.VlqData} + */ decode() { let n = 0; let c = this.string.charCodeAt(this.offset); @@ -972,6 +1049,9 @@ class VlqHexDecoder { this.offset += 1; return sign ? -value : value; } + /** + * @returns {T} + */ next() { const c = this.string.charCodeAt(this.offset); // sixteen characters after "0" are backref @@ -994,6 +1074,7 @@ class VlqHexDecoder { } } class RoaringBitmap { + /** @param {string} str */ constructor(str) { // https://github.com/RoaringBitmap/RoaringFormatSpec // @@ -1063,6 +1144,7 @@ class RoaringBitmap { } } } + /** @param {number} keyvalue */ contains(keyvalue) { const key = keyvalue >> 16; const value = keyvalue & 0xFFFF; @@ -1091,10 +1173,15 @@ class RoaringBitmap { } class RoaringBitmapRun { + /** + * @param {number} runcount + * @param {Uint8Array} array + */ constructor(runcount, array) { this.runcount = runcount; this.array = array; } + /** @param {number} value */ contains(value) { // Binary search algorithm copied from // https://en.wikipedia.org/wiki/Binary_search#Procedure @@ -1120,10 +1207,15 @@ class RoaringBitmapRun { } } class RoaringBitmapArray { + /** + * @param {number} cardinality + * @param {Uint8Array} array + */ constructor(cardinality, array) { this.cardinality = cardinality; this.array = array; } + /** @param {number} value */ contains(value) { // Binary search algorithm copied from // https://en.wikipedia.org/wiki/Binary_search#Procedure @@ -1148,9 +1240,13 @@ class RoaringBitmapArray { } } class RoaringBitmapBits { + /** + * @param {Uint8Array} array + */ constructor(array) { this.array = array; } + /** @param {number} value */ contains(value) { return !!(this.array[value >> 3] & (1 << (value & 7))); } @@ -1176,9 +1272,9 @@ class RoaringBitmapBits { * matches * : A list of search index IDs for this node. * - * @typedef {{ - * children: [NameTrie], - * matches: [number], + * @type {{ + * children: NameTrie[], + * matches: number[], * }} */ class NameTrie { @@ -1186,9 +1282,20 @@ class NameTrie { this.children = []; this.matches = []; } + /** + * @param {string} name + * @param {number} id + * @param {Map} tailTable + */ insert(name, id, tailTable) { this.insertSubstring(name, 0, id, tailTable); } + /** + * @param {string} name + * @param {number} substart + * @param {number} id + * @param {Map} tailTable + */ insertSubstring(name, substart, id, tailTable) { const l = name.length; if (substart === l) { @@ -1201,10 +1308,13 @@ class NameTrie { } else { child = new NameTrie(); this.children[sb] = child; + /** @type {NameTrie[]} */ let sste; if (substart >= 2) { const tail = name.substring(substart - 2, substart + 1); if (tailTable.has(tail)) { + // it's not undefined + // @ts-expect-error sste = tailTable.get(tail); } else { sste = []; @@ -1216,6 +1326,10 @@ class NameTrie { child.insertSubstring(name, substart + 1, id, tailTable); } } + /** + * @param {string} name + * @param {Map} tailTable + */ search(name, tailTable) { const results = new Set(); this.searchSubstringPrefix(name, 0, results); @@ -1226,6 +1340,8 @@ class NameTrie { this.searchLev(name, 0, levParams, results); const tail = name.substring(0, 3); if (tailTable.has(tail)) { + // it's not undefined + // @ts-expect-error for (const entry of tailTable.get(tail)) { entry.searchSubstringPrefix(name, 3, results); } @@ -1233,6 +1349,11 @@ class NameTrie { } return [...results]; } + /** + * @param {string} name + * @param {number} substart + * @param {Set} results + */ searchSubstringPrefix(name, substart, results) { const l = name.length; if (substart === l) { @@ -1240,14 +1361,18 @@ class NameTrie { results.add(match); } // breadth-first traversal orders prefix matches by length + /** @type {NameTrie[]} */ let unprocessedChildren = []; for (const child of this.children) { if (child) { unprocessedChildren.push(child); } } + /** @type {NameTrie[]} */ let nextSet = []; while (unprocessedChildren.length !== 0) { + /** @type {NameTrie} */ + // @ts-expect-error const next = unprocessedChildren.pop(); for (const child of next.children) { if (child) { @@ -1270,10 +1395,18 @@ class NameTrie { } } } + /** + * @param {string} name + * @param {number} substart + * @param {Lev2TParametricDescription|Lev1TParametricDescription} levParams + * @param {Set} results + */ searchLev(name, substart, levParams, results) { const stack = [[this, 0]]; const n = levParams.n; while (stack.length !== 0) { + // It's not empty + //@ts-expect-error const [trie, levState] = stack.pop(); for (const [charCode, child] of trie.children.entries()) { if (!child) { @@ -1305,6 +1438,11 @@ class NameTrie { } class DocSearch { + /** + * @param {Map} rawSearchIndex + * @param {string} rootPath + * @param {rustdoc.SearchState} searchState + */ constructor(rawSearchIndex, rootPath, searchState) { /** * @type {Map} @@ -1317,19 +1455,19 @@ class DocSearch { /** * @type {Uint32Array} */ - this.functionTypeFingerprint = null; + this.functionTypeFingerprint = new Uint32Array(0); /** * Map from normalized type names to integers. Used to make type search * more efficient. * - * @type {Map} + * @type {Map} */ this.typeNameIdMap = new Map(); /** * Map from type ID to associated type name. Used for display, * not for search. * - * @type {Map} + * @type {Map} */ this.assocTypeIdNameMap = new Map(); this.ALIASES = new Map(); @@ -1338,64 +1476,88 @@ class DocSearch { /** * Special type name IDs for searching by array. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfArray = this.buildTypeMapIndex("array"); /** * Special type name IDs for searching by slice. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfSlice = this.buildTypeMapIndex("slice"); /** * Special type name IDs for searching by both array and slice (`[]` syntax). + * @type {number} */ + // @ts-expect-error this.typeNameIdOfArrayOrSlice = this.buildTypeMapIndex("[]"); /** * Special type name IDs for searching by tuple. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfTuple = this.buildTypeMapIndex("tuple"); /** * Special type name IDs for searching by unit. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfUnit = this.buildTypeMapIndex("unit"); /** * Special type name IDs for searching by both tuple and unit (`()` syntax). + * @type {number} */ + // @ts-expect-error this.typeNameIdOfTupleOrUnit = this.buildTypeMapIndex("()"); /** * Special type name IDs for searching `fn`. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfFn = this.buildTypeMapIndex("fn"); /** * Special type name IDs for searching `fnmut`. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfFnMut = this.buildTypeMapIndex("fnmut"); /** * Special type name IDs for searching `fnonce`. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfFnOnce = this.buildTypeMapIndex("fnonce"); /** * Special type name IDs for searching higher order functions (`->` syntax). + * @type {number} */ + // @ts-expect-error this.typeNameIdOfHof = this.buildTypeMapIndex("->"); /** * Special type name IDs the output assoc type. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfOutput = this.buildTypeMapIndex("output", true); /** * Special type name IDs for searching by reference. + * @type {number} */ + // @ts-expect-error this.typeNameIdOfReference = this.buildTypeMapIndex("reference"); /** * Empty, immutable map used in item search types with no bindings. * - * @type {Map>} + * @type {Map>} */ this.EMPTY_BINDINGS_MAP = new Map(); /** * Empty, immutable map used in item search types with no bindings. * - * @type {Array} + * @type {Array} */ this.EMPTY_GENERICS_ARRAY = []; @@ -1403,7 +1565,7 @@ class DocSearch { * Object pool for function types with no bindings or generics. * This is reset after loading the index. * - * @type {Map} + * @type {Map} */ this.TYPES_POOL = new Map(); @@ -1422,8 +1584,9 @@ class DocSearch { this.tailTable = new Map(); /** - * @type {Array} + * @type {Array} */ + // @ts-expect-error this.searchIndex = this.buildIndex(rawSearchIndex); } @@ -1436,9 +1599,9 @@ class DocSearch { * get the same ID. * * @param {string} name - * @param {boolean} isAssocType - True if this is an assoc type + * @param {boolean=} isAssocType - True if this is an assoc type * - * @returns {integer} + * @returns {number?} */ buildTypeMapIndex(name, isAssocType) { if (name === "" || name === null) { @@ -1446,12 +1609,14 @@ class DocSearch { } if (this.typeNameIdMap.has(name)) { + /** @type {{id: number, assocOnly: boolean}} */ + // @ts-expect-error const obj = this.typeNameIdMap.get(name); - obj.assocOnly = isAssocType && obj.assocOnly; + obj.assocOnly = !!(isAssocType && obj.assocOnly); return obj.id; } else { const id = this.typeNameIdMap.size; - this.typeNameIdMap.set(name, { id, assocOnly: isAssocType }); + this.typeNameIdMap.set(name, { id, assocOnly: !!isAssocType }); return id; } } @@ -1469,13 +1634,26 @@ class DocSearch { * The format for individual function types is encoded in * librustdoc/html/render/mod.rs: impl Serialize for RenderType * - * @param {null|Array} types - * @param {Array<{name: string, ty: number}>} lowercasePaths + * @param {null|Array} types + * @param {Array<{ + * name: string, + * ty: number, + * path: string|null, + * exactPath: string|null, + * unboxFlag: boolean + * }>} paths + * @param {Array<{ + * name: string, + * ty: number, + * path: string|null, + * exactPath: string|null, + * unboxFlag: boolean, + * }>} lowercasePaths * - * @return {Array} + * @return {Array} */ buildItemSearchTypeAll(types, paths, lowercasePaths) { - return types.length > 0 ? + return types && types.length > 0 ? types.map(type => this.buildItemSearchType(type, paths, lowercasePaths)) : this.EMPTY_GENERICS_ARRAY; } @@ -1483,7 +1661,22 @@ class DocSearch { /** * Converts a single type. * - * @param {RawFunctionType} type + * @param {rustdoc.RawFunctionType} type + * @param {Array<{ + * name: string, + * ty: number, + * path: string|null, + * exactPath: string|null, + * unboxFlag: boolean + * }>} paths + * @param {Array<{ + * name: string, + * ty: number, + * path: string|null, + * exactPath: string|null, + * unboxFlag: boolean, + * }>} lowercasePaths + * @param {boolean=} isAssocType */ buildItemSearchType(type, paths, lowercasePaths, isAssocType) { const PATH_INDEX_DATA = 0; @@ -1501,7 +1694,9 @@ class DocSearch { paths, lowercasePaths, ); + // @ts-expect-error if (type.length > BINDINGS_DATA && type[BINDINGS_DATA].length > 0) { + // @ts-expect-error bindings = new Map(type[BINDINGS_DATA].map(binding => { const [assocType, constraints] = binding; // Associated type constructors are represented sloppily in rustdoc's @@ -1524,7 +1719,7 @@ class DocSearch { } } /** - * @type {FunctionType} + * @type {rustdoc.FunctionType} */ let result; if (pathIndex < 0) { @@ -1555,7 +1750,7 @@ class DocSearch { } else { const item = lowercasePaths[pathIndex - 1]; const id = this.buildTypeMapIndex(item.name, isAssocType); - if (isAssocType) { + if (isAssocType && id !== null) { this.assocTypeIdNameMap.set(id, paths[pathIndex - 1].name); } result = { @@ -1585,6 +1780,7 @@ class DocSearch { if (cr.bindings.size === result.bindings.size && cr.bindings !== result.bindings) { let ok = true; for (const [k, v] of cr.bindings.entries()) { + // @ts-expect-error const v2 = result.bindings.get(v); if (!v2) { ok = false; @@ -1629,7 +1825,7 @@ class DocSearch { * [^1]: Distance is the relatively naive metric of counting the number of distinct items in * the function that are not present in the query. * - * @param {FunctionType|QueryElement} type - a single type + * @param {rustdoc.FingerprintableType} type - a single type * @param {Uint32Array} output - write the fingerprint to this data structure: uses 128 bits */ buildFunctionTypeFingerprint(type, output) { @@ -1647,9 +1843,12 @@ class DocSearch { input === this.typeNameIdOfFnOnce) { input = this.typeNameIdOfHof; } - // http://burtleburtle.net/bob/hash/integer.html - // ~~ is toInt32. It's used before adding, so - // the number stays in safe integer range. + /** + * http://burtleburtle.net/bob/hash/integer.html + * ~~ is toInt32. It's used before adding, so + * the number stays in safe integer range. + * @param {number} k + */ const hashint1 = k => { k = (~~k + 0x7ed55d16) + (k << 12); k = (k ^ 0xc761c23c) ^ (k >>> 19); @@ -1658,6 +1857,7 @@ class DocSearch { k = (~~k + 0xfd7046c5) + (k << 3); return (k ^ 0xb55a4f09) ^ (k >>> 16); }; + /** @param {number} k */ const hashint2 = k => { k = ~k + (k << 15); k ^= k >>> 12; @@ -1684,6 +1884,14 @@ class DocSearch { for (const g of type.generics) { this.buildFunctionTypeFingerprint(g, output); } + /** + * @type {{ + * id: number|null, + * ty: number, + * generics: rustdoc.FingerprintableType[], + * bindings: Map + * }} + */ const fb = { id: null, ty: 0, @@ -1700,7 +1908,7 @@ class DocSearch { /** * Convert raw search index into in-memory search index. * - * @param {[string, RawSearchIndexCrate][]} rawSearchIndex + * @param {Map} rawSearchIndex */ buildIndex(rawSearchIndex) { /** @@ -1714,19 +1922,37 @@ class DocSearch { * The raw function search type format is generated using serde in * librustdoc/html/render/mod.rs: IndexItemFunctionType::write_to_string * - * @param {Array<{name: string, ty: number}>} paths - * @param {Array<{name: string, ty: number}>} lowercasePaths + * @param {Array<{ + * name: string, + * ty: number, + * path: string|null, + * exactPath: string|null, + * unboxFlag: boolean + * }>} paths + * @param {Array<{ + * name: string, + * ty: number, + * path: string|null, + * exactPath: string|null, + * unboxFlag: boolean + * }>} lowercasePaths * - * @return {null|FunctionSearchType} + * @return {function(rustdoc.RawFunctionSearchType): null|rustdoc.FunctionSearchType} */ const buildFunctionSearchTypeCallback = (paths, lowercasePaths) => { - return functionSearchType => { + /** + * @param {rustdoc.RawFunctionSearchType} functionSearchType + */ + const cb = functionSearchType => { if (functionSearchType === 0) { return null; } const INPUTS_DATA = 0; const OUTPUT_DATA = 1; - let inputs, output; + /** @type {rustdoc.FunctionType[]} */ + let inputs; + /** @type {rustdoc.FunctionType[]} */ + let output; if (typeof functionSearchType[INPUTS_DATA] === "number") { inputs = [ this.buildItemSearchType( @@ -1753,6 +1979,7 @@ class DocSearch { ]; } else { output = this.buildItemSearchTypeAll( + // @ts-expect-error functionSearchType[OUTPUT_DATA], paths, lowercasePaths, @@ -1765,8 +1992,10 @@ class DocSearch { const l = functionSearchType.length; for (let i = 2; i < l; ++i) { where_clause.push(typeof functionSearchType[i] === "number" + // @ts-expect-error ? [this.buildItemSearchType(functionSearchType[i], paths, lowercasePaths)] : this.buildItemSearchTypeAll( + // @ts-expect-error functionSearchType[i], paths, lowercasePaths, @@ -1776,6 +2005,7 @@ class DocSearch { inputs, output, where_clause, }; }; + return cb; }; const searchIndex = []; @@ -1798,7 +2028,12 @@ class DocSearch { for (const [crate, crateCorpus] of rawSearchIndex) { // a string representing the lengths of each description shard // a string representing the list of function types - const itemDescShardDecoder = new VlqHexDecoder(crateCorpus.D, noop => noop); + const itemDescShardDecoder = new VlqHexDecoder(crateCorpus.D, noop => { + /** @type {number} */ + // @ts-expect-error + const n = noop; + return n; + }); let descShard = { crate, shard: 0, @@ -1817,7 +2052,7 @@ class DocSearch { /** * List of generic function type parameter names. * Used for display, not for searching. - * @type {[string]} + * @type {string[]} */ let lastParamNames = []; @@ -1847,6 +2082,8 @@ class DocSearch { id += 1; searchIndex.push(crateRow); currentIndex += 1; + // it's not undefined + // @ts-expect-error if (!this.searchIndexEmptyDesc.get(crate).contains(0)) { descIndex += 1; } @@ -1870,7 +2107,7 @@ class DocSearch { const implDisambiguator = new Map(crateCorpus.b); // an array of [(Number) item type, // (String) name] - const paths = crateCorpus.p; + const rawPaths = crateCorpus.p; // an array of [(String) alias name // [Number] index to items] const aliases = crateCorpus.a; @@ -1879,33 +2116,61 @@ class DocSearch { // an item whose index is not present will fall back to the previous present path const itemParamNames = new Map(crateCorpus.P); - // an array of [{name: String, ty: Number}] + /** + * @type {Array<{ + * name: string, + * ty: number, + * path: string|null, + * exactPath: string|null, + * unboxFlag: boolean + * }>} + */ const lowercasePaths = []; + /** + * @type {Array<{ + * name: string, + * ty: number, + * path: string|null, + * exactPath: string|null, + * unboxFlag: boolean + * }>} + */ + const paths = []; // a string representing the list of function types const itemFunctionDecoder = new VlqHexDecoder( crateCorpus.f, + // @ts-expect-error buildFunctionSearchTypeCallback(paths, lowercasePaths), ); // convert `rawPaths` entries into object form // generate normalizedPaths for function search mode - let len = paths.length; + let len = rawPaths.length; let lastPath = itemPaths.get(0); for (let i = 0; i < len; ++i) { - const elem = paths[i]; + const elem = rawPaths[i]; const ty = elem[0]; const name = elem[1]; let path = null; if (elem.length > 2 && elem[2] !== null) { + // @ts-expect-error path = itemPaths.has(elem[2]) ? itemPaths.get(elem[2]) : lastPath; lastPath = path; } - const exactPath = elem.length > 3 && elem[3] !== null ? + let exactPath = elem.length > 3 && elem[3] !== null ? + // @ts-expect-error itemPaths.get(elem[3]) : path; const unboxFlag = elem.length > 4 && !!elem[4]; + if (path === undefined) { + path = null; + } + if (exactPath === undefined) { + exactPath = null; + } + lowercasePaths.push({ ty, name: name.toLowerCase(), path, exactPath, unboxFlag }); paths[i] = { ty, name, path, exactPath, unboxFlag }; } @@ -1924,6 +2189,7 @@ class DocSearch { for (let i = 0; i < len; ++i) { const bitIndex = i + 1; if (descIndex >= descShard.len && + // @ts-expect-error !this.searchIndexEmptyDesc.get(crate).contains(bitIndex)) { descShard = { crate, @@ -1938,8 +2204,11 @@ class DocSearch { } const name = itemNames[i] === "" ? lastName : itemNames[i]; const word = itemNames[i] === "" ? lastWord : itemNames[i].toLowerCase(); + /** @type {string} */ + // @ts-expect-error const path = itemPaths.has(i) ? itemPaths.get(i) : lastPath; const paramNames = itemParamNames.has(i) ? + // @ts-expect-error itemParamNames.get(i).split(",") : lastParamNames; const type = itemFunctionDecoder.next(); @@ -1971,7 +2240,9 @@ class DocSearch { descShard, descIndex, exactPath: itemReexports.has(i) ? + // @ts-expect-error itemPaths.get(itemReexports.get(i)) : path, + // @ts-expect-error parent: itemParentIdx > 0 ? paths[itemParentIdx - 1] : undefined, type, paramNames, @@ -1987,6 +2258,7 @@ class DocSearch { searchIndex.push(row); lastPath = row.path; lastParamNames = row.paramNames; + // @ts-expect-error if (!this.searchIndexEmptyDesc.get(crate).contains(bitIndex)) { descIndex += 1; } @@ -2002,13 +2274,16 @@ class DocSearch { continue; } + // @ts-expect-error let currentNameAliases; if (currentCrateAliases.has(alias_name)) { currentNameAliases = currentCrateAliases.get(alias_name); } else { currentNameAliases = []; + // @ts-expect-error currentCrateAliases.set(alias_name, currentNameAliases); } + // @ts-expect-error for (const local_alias of aliases[alias_name]) { currentNameAliases.push(local_alias + currentIndex); } @@ -2030,11 +2305,15 @@ class DocSearch { * * When adding new things to the parser, add them there, too! * - * @param {string} val - The user query + * @param {string} userQuery - The user query * - * @return {ParsedQuery} - The parsed query + * @return {rustdoc.ParsedQuery} - The parsed query */ static parseQuery(userQuery) { + /** + * @param {string} typename + * @returns {number} + */ function itemTypeFromName(typename) { const index = itemTypes.findIndex(i => i === typename); if (index < 0) { @@ -2043,14 +2322,19 @@ class DocSearch { return index; } + /** + * @param {rustdoc.ParserQueryElement} elem + */ function convertTypeFilterOnElem(elem) { if (elem.typeFilter !== null) { let typeFilter = elem.typeFilter; if (typeFilter === "const") { typeFilter = "constant"; } + // @ts-expect-error elem.typeFilter = itemTypeFromName(typeFilter); } else { + // @ts-expect-error elem.typeFilter = NO_TYPE_FILTER; } for (const elem2 of elem.generics) { @@ -2068,7 +2352,7 @@ class DocSearch { * * @param {string} userQuery * - * @return {ParsedQuery} + * @return {rustdoc.ParsedQuery} */ function newParsedQuery(userQuery) { return { @@ -2094,8 +2378,8 @@ class DocSearch { * Parses the provided `query` input to fill `parserState`. If it encounters an error while * parsing `query`, it'll throw an error. * - * @param {ParsedQuery} query - * @param {ParserState} parserState + * @param {rustdoc.ParsedQuery} query + * @param {rustdoc.ParserState} parserState */ function parseInput(query, parserState) { let foundStopChar = true; @@ -2125,6 +2409,7 @@ class DocSearch { if (!foundStopChar) { let extra = ""; if (isLastElemGeneric(query.elems, parserState)) { + // @ts-expect-error extra = [" after ", ">"]; } else if (prevIs(parserState, "\"")) { throw ["Cannot have more than one element if you use quotes"]; @@ -2208,6 +2493,8 @@ class DocSearch { } } catch (err) { query = newParsedQuery(userQuery); + // is string list + // @ts-expect-error query.error = err; return query; } @@ -2224,25 +2511,167 @@ class DocSearch { /** * Executes the parsed query and builds a {ResultsTable}. * - * @param {ParsedQuery} parsedQuery - The parsed user query - * @param {Object} [filterCrates] - Crate to search in if defined - * @param {Object} [currentCrate] - Current crate, to rank results from this crate higher + * @param {rustdoc.ParsedQuery} origParsedQuery + * - The parsed user query + * @param {Object} [filterCrates] - Crate to search in if defined + * @param {Object} [currentCrate] - Current crate, to rank results from this crate higher * - * @return {ResultsTable} + * @return {Promise} */ - async execQuery(parsedQuery, filterCrates, currentCrate) { + async execQuery(origParsedQuery, filterCrates, currentCrate) { const results_others = new Map(), results_in_args = new Map(), results_returned = new Map(); + /** @type {rustdoc.ParsedQuery} */ + // @ts-expect-error + const parsedQuery = origParsedQuery; + + const queryLen = + parsedQuery.elems.reduce((acc, next) => acc + next.pathLast.length, 0) + + parsedQuery.returned.reduce((acc, next) => acc + next.pathLast.length, 0); + const maxEditDistance = Math.floor(queryLen / 3); + + /** + * @type {Map} + */ + const genericSymbols = new Map(); + + /** + * Convert names to ids in parsed query elements. + * This is not used for the "In Names" tab, but is used for the + * "In Params", "In Returns", and "In Function Signature" tabs. + * + * If there is no matching item, but a close-enough match, this + * function also that correction. + * + * See `buildTypeMapIndex` for more information. + * + * @param {rustdoc.QueryElement} elem + * @param {boolean} isAssocType + */ + const convertNameToId = (elem, isAssocType) => { + const loweredName = elem.pathLast.toLowerCase(); + if (this.typeNameIdMap.has(loweredName) && + // @ts-expect-error + (isAssocType || !this.typeNameIdMap.get(loweredName).assocOnly)) { + // @ts-expect-error + elem.id = this.typeNameIdMap.get(loweredName).id; + } else if (!parsedQuery.literalSearch) { + let match = null; + let matchDist = maxEditDistance + 1; + let matchName = ""; + for (const [name, { id, assocOnly }] of this.typeNameIdMap) { + const dist = Math.min( + editDistance(name, loweredName, maxEditDistance), + editDistance(name, elem.normalizedPathLast, maxEditDistance), + ); + if (dist <= matchDist && dist <= maxEditDistance && + (isAssocType || !assocOnly)) { + if (dist === matchDist && matchName > name) { + continue; + } + match = id; + matchDist = dist; + matchName = name; + } + } + if (match !== null) { + parsedQuery.correction = matchName; + } + elem.id = match; + } + if ((elem.id === null && parsedQuery.totalElems > 1 && elem.typeFilter === -1 + && elem.generics.length === 0 && elem.bindings.size === 0) + || elem.typeFilter === TY_GENERIC) { + if (genericSymbols.has(elem.normalizedPathLast)) { + // @ts-expect-error + elem.id = genericSymbols.get(elem.normalizedPathLast); + } else { + elem.id = -(genericSymbols.size + 1); + genericSymbols.set(elem.normalizedPathLast, elem.id); + } + if (elem.typeFilter === -1 && elem.normalizedPathLast.length >= 3) { + // Silly heuristic to catch if the user probably meant + // to not write a generic parameter. We don't use it, + // just bring it up. + const maxPartDistance = Math.floor(elem.normalizedPathLast.length / 3); + let matchDist = maxPartDistance + 1; + let matchName = ""; + for (const name of this.typeNameIdMap.keys()) { + const dist = editDistance( + name, + elem.normalizedPathLast, + maxPartDistance, + ); + if (dist <= matchDist && dist <= maxPartDistance) { + if (dist === matchDist && matchName > name) { + continue; + } + matchDist = dist; + matchName = name; + } + } + if (matchName !== "") { + parsedQuery.proposeCorrectionFrom = elem.name; + parsedQuery.proposeCorrectionTo = matchName; + } + } + elem.typeFilter = TY_GENERIC; + } + if (elem.generics.length > 0 && elem.typeFilter === TY_GENERIC) { + // Rust does not have HKT + parsedQuery.error = [ + "Generic type parameter ", + elem.name, + " does not accept generic parameters", + ]; + } + for (const elem2 of elem.generics) { + // @ts-expect-error + convertNameToId(elem2); + } + elem.bindings = new Map(Array.from(elem.bindings.entries()) + .map(entry => { + const [name, constraints] = entry; + // @ts-expect-error + if (!this.typeNameIdMap.has(name)) { + parsedQuery.error = [ + "Type parameter ", + // @ts-expect-error + name, + " does not exist", + ]; + return [0, []]; + } + for (const elem2 of constraints) { + convertNameToId(elem2, false); + } + + // @ts-expect-error + return [this.typeNameIdMap.get(name).id, constraints]; + }), + ); + }; + + for (const elem of parsedQuery.elems) { + convertNameToId(elem, false); + this.buildFunctionTypeFingerprint(elem, parsedQuery.typeFingerprint); + } + for (const elem of parsedQuery.returned) { + convertNameToId(elem, false); + this.buildFunctionTypeFingerprint(elem, parsedQuery.typeFingerprint); + } + + /** * Creates the query results. * - * @param {Array} results_in_args - * @param {Array} results_returned - * @param {Array} results_others - * @param {ParsedQuery} parsedQuery + * @param {Array} results_in_args + * @param {Array} results_returned + * @param {Array} results_others + * @param {rustdoc.ParsedQuery} parsedQuery * - * @return {ResultsTable} + * @return {rustdoc.ResultsTable} */ function createQueryResults( results_in_args, @@ -2257,6 +2686,7 @@ class DocSearch { }; } + // @ts-expect-error const buildHrefAndPath = item => { let displayPath; let href; @@ -2320,6 +2750,7 @@ class DocSearch { return [displayPath, href, `${exactPath}::${name}`]; }; + // @ts-expect-error function pathSplitter(path) { const tmp = "" + path.replace(/::/g, "::"); if (tmp.endsWith("")) { @@ -2332,10 +2763,9 @@ class DocSearch { * Add extra data to result objects, and filter items that have been * marked for removal. * - * @param {[ResultObject]} results + * @param {[rustdoc.ResultObject]} results * @param {"sig"|"elems"|"returned"|null} typeInfo - * @param {ParsedQuery} query - * @returns {[ResultObject]} + * @returns {[rustdoc.ResultObject]} */ const transformResults = (results, typeInfo) => { const duplicates = new Set(); @@ -2350,7 +2780,9 @@ class DocSearch { }, this.searchIndex[result.id]); // To be sure than it some items aren't considered as duplicate. + // @ts-expect-error obj.fullPath = res[2] + "|" + obj.ty; + // @ts-expect-error if (duplicates.has(obj.fullPath)) { continue; } @@ -2363,14 +2795,18 @@ class DocSearch { if (duplicates.has(res[2] + "|" + TY_IMPORT)) { continue; } + // @ts-expect-error duplicates.add(obj.fullPath); duplicates.add(res[2]); if (typeInfo !== null) { + // @ts-expect-error obj.displayTypeSignature = + // @ts-expect-error this.formatDisplayTypeSignature(obj, typeInfo); } + // @ts-expect-error obj.href = res[1]; out.push(obj); if (out.length >= MAX_RESULTS) { @@ -2378,6 +2814,7 @@ class DocSearch { } } } + // @ts-expect-error return out; }; @@ -2388,30 +2825,34 @@ class DocSearch { * The output is formatted as an array of hunks, where odd numbered * hunks are highlighted and even numbered ones are not. * - * @param {ResultObject} obj + * @param {rustdoc.ResultObject} obj * @param {"sig"|"elems"|"returned"|null} typeInfo - * @param {ParsedQuery} query - * @returns Promise< + * @returns {Promise<{ * "type": Array, * "mappedNames": Map, * "whereClause": Map>, - * > + * }>} */ this.formatDisplayTypeSignature = async(obj, typeInfo) => { + const objType = obj.type; + if (!objType) { + return {type: [], mappedNames: new Map(), whereClause: new Map()}; + } let fnInputs = null; let fnOutput = null; + // @ts-expect-error let mgens = null; if (typeInfo !== "elems" && typeInfo !== "returned") { fnInputs = unifyFunctionTypes( - obj.type.inputs, + objType.inputs, parsedQuery.elems, - obj.type.where_clause, + objType.where_clause, null, mgensScratch => { fnOutput = unifyFunctionTypes( - obj.type.output, + objType.output, parsedQuery.returned, - obj.type.where_clause, + objType.where_clause, mgensScratch, mgensOut => { mgens = mgensOut; @@ -2424,11 +2865,11 @@ class DocSearch { 0, ); } else { - const arr = typeInfo === "elems" ? obj.type.inputs : obj.type.output; + const arr = typeInfo === "elems" ? objType.inputs : objType.output; const highlighted = unifyFunctionTypes( arr, parsedQuery.elems, - obj.type.where_clause, + objType.where_clause, null, mgensOut => { mgens = mgensOut; @@ -2443,15 +2884,16 @@ class DocSearch { } } if (!fnInputs) { - fnInputs = obj.type.inputs; + fnInputs = objType.inputs; } if (!fnOutput) { - fnOutput = obj.type.output; + fnOutput = objType.output; } const mappedNames = new Map(); const whereClause = new Map(); - const fnParamNames = obj.paramNames; + const fnParamNames = obj.paramNames || []; + // @ts-expect-error const queryParamNames = []; /** * Recursively writes a map of IDs to query generic names, @@ -2461,10 +2903,10 @@ class DocSearch { * mapping `(-1, "X")`, and the writeFn function looks up the entry * for -1 to form the final, user-visible mapping of "X is T". * - * @param {QueryElement} queryElem + * @param {rustdoc.QueryElement} queryElem */ const remapQuery = queryElem => { - if (queryElem.id < 0) { + if (queryElem.id !== null && queryElem.id < 0) { queryParamNames[-1 - queryElem.id] = queryElem.name; } if (queryElem.generics.length > 0) { @@ -2483,7 +2925,7 @@ class DocSearch { * Index 0 is not highlighted, index 1 is highlighted, * index 2 is not highlighted, etc. * - * @param {{name: string, highlighted: bool|undefined}} fnType - input + * @param {{name?: string, highlighted?: boolean}} fnType - input * @param {[string]} result */ const pushText = (fnType, result) => { @@ -2496,6 +2938,7 @@ class DocSearch { // needs coerced to a boolean. if (!!(result.length % 2) === !!fnType.highlighted) { result.push(""); + // @ts-expect-error } else if (result.length === 0 && !!fnType.highlighted) { result.push(""); result.push(""); @@ -2508,7 +2951,7 @@ class DocSearch { * Write a higher order function type: either a function pointer * or a trait bound on Fn, FnMut, or FnOnce. * - * @param {FunctionType} fnType - input + * @param {rustdoc.HighlightedFunctionType} fnType - input * @param {[string]} result */ const writeHof = (fnType, result) => { @@ -2548,7 +2991,7 @@ class DocSearch { * Write a primitive type with special syntax, like `!` or `[T]`. * Returns `false` if the supplied type isn't special. * - * @param {FunctionType} fnType + * @param {rustdoc.HighlightedFunctionType} fnType * @param {[string]} result */ const writeSpecialPrimitive = (fnType, result) => { @@ -2563,6 +3006,7 @@ class DocSearch { onEachBtwn( fnType.generics, nested => writeFn(nested, result), + // @ts-expect-error () => pushText({ name: ", ", highlighted: false }, result), ); pushText({ name: sb, highlighted: fnType.highlighted }, result); @@ -2573,9 +3017,10 @@ class DocSearch { onEachBtwn( fnType.generics, value => { - prevHighlighted = value.highlighted; + prevHighlighted = !!value.highlighted; writeFn(value, result); }, + // @ts-expect-error value => pushText({ name: " ", highlighted: prevHighlighted && value.highlighted, @@ -2593,25 +3038,27 @@ class DocSearch { * like slices, with their own formatting. It also handles * updating the where clause and generic type param map. * - * @param {FunctionType} fnType + * @param {rustdoc.HighlightedFunctionType} fnType * @param {[string]} result */ const writeFn = (fnType, result) => { - if (fnType.id < 0) { + if (fnType.id !== null && fnType.id < 0) { if (fnParamNames[-1 - fnType.id] === "") { // Normally, there's no need to shown an unhighlighted // where clause, but if it's impl Trait, then we do. const generics = fnType.generics.length > 0 ? fnType.generics : - obj.type.where_clause[-1 - fnType.id]; + objType.where_clause[-1 - fnType.id]; for (const nested of generics) { writeFn(nested, result); } return; + // @ts-expect-error } else if (mgens) { for (const [queryId, fnId] of mgens) { if (fnId === fnType.id) { mappedNames.set( + // @ts-expect-error queryParamNames[-1 - queryId], fnParamNames[-1 - fnType.id], ); @@ -2622,13 +3069,17 @@ class DocSearch { name: fnParamNames[-1 - fnType.id], highlighted: !!fnType.highlighted, }, result); + // @ts-expect-error const where = []; onEachBtwn( fnType.generics, + // @ts-expect-error nested => writeFn(nested, where), + // @ts-expect-error () => pushText({ name: " + ", highlighted: false }, where), ); if (where.length > 0) { + // @ts-expect-error whereClause.set(fnParamNames[-1 - fnType.id], where); } } else { @@ -2650,12 +3101,15 @@ class DocSearch { fnType.bindings, ([key, values]) => { const name = this.assocTypeIdNameMap.get(key); + // @ts-expect-error if (values.length === 1 && values[0].id < 0 && + // @ts-expect-error `${fnType.name}::${name}` === fnParamNames[-1 - values[0].id]) { // the internal `Item=Iterator::Item` type variable should be // shown in the where clause and name mapping output, but is // redundant in this spot for (const value of values) { + // @ts-expect-error writeFn(value, []); } return true; @@ -2672,12 +3126,14 @@ class DocSearch { onEachBtwn( values || [], value => writeFn(value, result), + // @ts-expect-error () => pushText({ name: " + ", highlighted: false }, result), ); if (values.length !== 1) { pushText({ name: ")", highlighted: false }, result); } }, + // @ts-expect-error () => pushText({ name: ", ", highlighted: false }, result), ); } @@ -2687,6 +3143,7 @@ class DocSearch { onEachBtwn( fnType.generics, value => writeFn(value, result), + // @ts-expect-error () => pushText({ name: ", ", highlighted: false }, result), ); if (hasBindings || fnType.generics.length > 0) { @@ -2694,19 +3151,26 @@ class DocSearch { } } }; + // @ts-expect-error const type = []; onEachBtwn( fnInputs, + // @ts-expect-error fnType => writeFn(fnType, type), + // @ts-expect-error () => pushText({ name: ", ", highlighted: false }, type), ); + // @ts-expect-error pushText({ name: " -> ", highlighted: false }, type); onEachBtwn( fnOutput, + // @ts-expect-error fnType => writeFn(fnType, type), + // @ts-expect-error () => pushText({ name: ", ", highlighted: false }, type), ); + // @ts-expect-error return {type, mappedNames, whereClause}; }; @@ -2714,10 +3178,10 @@ class DocSearch { * This function takes a result map, and sorts it by various criteria, including edit * distance, substring match, and the crate it comes from. * - * @param {Results} results - * @param {boolean} isType + * @param {rustdoc.Results} results + * @param {"sig"|"elems"|"returned"|null} typeInfo * @param {string} preferredCrate - * @returns {Promise<[ResultObject]>} + * @returns {Promise<[rustdoc.ResultObject]>} */ const sortResults = async(results, typeInfo, preferredCrate) => { const userQuery = parsedQuery.userQuery; @@ -2733,12 +3197,12 @@ class DocSearch { // we are doing a return-type based search, // deprioritize "clone-like" results, // ie. functions that also take the queried type as an argument. - const hasType = result.item && result.item.type; - if (!hasType) { + const resultItemType = result.item && result.item.type; + if (!resultItemType) { continue; } - const inputs = result.item.type.inputs; - const where_clause = result.item.type.where_clause; + const inputs = resultItemType.inputs; + const where_clause = resultItemType.where_clause; if (containsTypeFromQuery(inputs, where_clause)) { result.path_dist *= 100; result.dist *= 100; @@ -2748,35 +3212,38 @@ class DocSearch { } result_list.sort((aaa, bbb) => { - let a, b; + /** @type {number} */ + let a; + /** @type {number} */ + let b; // sort by exact case-sensitive match if (isMixedCase) { - a = (aaa.item.name !== userQuery); - b = (bbb.item.name !== userQuery); + a = Number(aaa.item.name !== userQuery); + b = Number(bbb.item.name !== userQuery); if (a !== b) { return a - b; } } // sort by exact match with regard to the last word (mismatch goes later) - a = (aaa.word !== normalizedUserQuery); - b = (bbb.word !== normalizedUserQuery); + a = Number(aaa.word !== normalizedUserQuery); + b = Number(bbb.word !== normalizedUserQuery); if (a !== b) { return a - b; } // sort by index of keyword in item name (no literal occurrence goes later) - a = (aaa.index < 0); - b = (bbb.index < 0); + a = Number(aaa.index < 0); + b = Number(bbb.index < 0); if (a !== b) { return a - b; } // in type based search, put functions first if (parsedQuery.hasReturnArrow) { - a = !isFnLikeTy(aaa.item.ty); - b = !isFnLikeTy(bbb.item.ty); + a = Number(!isFnLikeTy(aaa.item.ty)); + b = Number(!isFnLikeTy(bbb.item.ty)); if (a !== b) { return a - b; } @@ -2784,80 +3251,93 @@ class DocSearch { // Sort by distance in the path part, if specified // (less changes required to match means higher rankings) - a = aaa.path_dist; - b = bbb.path_dist; + a = Number(aaa.path_dist); + b = Number(bbb.path_dist); if (a !== b) { return a - b; } // (later literal occurrence, if any, goes later) - a = aaa.index; - b = bbb.index; + a = Number(aaa.index); + b = Number(bbb.index); if (a !== b) { return a - b; } // Sort by distance in the name part, the last part of the path // (less changes required to match means higher rankings) - a = (aaa.dist); - b = (bbb.dist); + a = Number(aaa.dist); + b = Number(bbb.dist); if (a !== b) { return a - b; } // sort deprecated items later - a = this.searchIndexDeprecated.get(aaa.item.crate).contains(aaa.item.bitIndex); - b = this.searchIndexDeprecated.get(bbb.item.crate).contains(bbb.item.bitIndex); + a = Number( + // @ts-expect-error + this.searchIndexDeprecated.get(aaa.item.crate).contains(aaa.item.bitIndex), + ); + b = Number( + // @ts-expect-error + this.searchIndexDeprecated.get(bbb.item.crate).contains(bbb.item.bitIndex), + ); if (a !== b) { return a - b; } // sort by crate (current crate comes first) - a = (aaa.item.crate !== preferredCrate); - b = (bbb.item.crate !== preferredCrate); + a = Number(aaa.item.crate !== preferredCrate); + b = Number(bbb.item.crate !== preferredCrate); if (a !== b) { return a - b; } // sort by item name length (longer goes later) - a = aaa.word.length; - b = bbb.word.length; + a = Number(aaa.word.length); + b = Number(bbb.word.length); if (a !== b) { return a - b; } // sort by item name (lexicographically larger goes later) - a = aaa.word; - b = bbb.word; - if (a !== b) { - return (a > b ? +1 : -1); + let aw = aaa.word; + let bw = bbb.word; + if (aw !== bw) { + return (aw > bw ? +1 : -1); } // sort by description (no description goes later) - a = this.searchIndexEmptyDesc.get(aaa.item.crate).contains(aaa.item.bitIndex); - b = this.searchIndexEmptyDesc.get(bbb.item.crate).contains(bbb.item.bitIndex); + a = Number( + // @ts-expect-error + this.searchIndexEmptyDesc.get(aaa.item.crate).contains(aaa.item.bitIndex), + ); + b = Number( + // @ts-expect-error + this.searchIndexEmptyDesc.get(bbb.item.crate).contains(bbb.item.bitIndex), + ); if (a !== b) { return a - b; } // sort by type (later occurrence in `itemTypes` goes later) - a = aaa.item.ty; - b = bbb.item.ty; + a = Number(aaa.item.ty); + b = Number(bbb.item.ty); if (a !== b) { return a - b; } // sort by path (lexicographically larger goes later) - a = aaa.item.path; - b = bbb.item.path; - if (a !== b) { - return (a > b ? +1 : -1); + aw = aaa.item.path; + bw = bbb.item.path; + if (aw !== bw) { + return (aw > bw ? +1 : -1); } // que sera, sera return 0; }); + // @ts-expect-error return transformResults(result_list, typeInfo); }; @@ -2871,17 +3351,19 @@ class DocSearch { * then this function will try with a different solution, or bail with null if it * runs out of candidates. * - * @param {Array} fnTypesIn - The objects to check. - * @param {Array} queryElems - The elements from the parsed query. - * @param {[FunctionType]} whereClause - Trait bounds for generic items. + * @param {rustdoc.FunctionType[]} fnTypesIn - The objects to check. + * @param {rustdoc.QueryElement[]} queryElems - The elements from the parsed query. + * @param {rustdoc.FunctionType[][]} whereClause - Trait bounds for generic items. * @param {Map|null} mgensIn * - Map query generics to function generics (never modified). - * @param {Map -> bool} solutionCb - Called for each `mgens` solution. + * @param {function(Map?): boolean} solutionCb + * - Called for each `mgens` solution. * @param {number} unboxingDepth * - Limit checks that Ty matches Vec, * but not Vec>>>> * - * @return {[FunctionType]|null} - Returns highlighted results if a match, null otherwise. + * @return {rustdoc.HighlightedFunctionType[]|null} + * - Returns highlighted results if a match, null otherwise. */ function unifyFunctionTypes( fnTypesIn, @@ -2895,7 +3377,7 @@ class DocSearch { return null; } /** - * @type Map|null + * @type {Map|null} */ const mgens = mgensIn === null ? null : new Map(mgensIn); if (queryElems.length === 0) { @@ -2915,7 +3397,11 @@ class DocSearch { if (!unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgens)) { continue; } - if (fnType.id < 0 && queryElem.id < 0) { + if (fnType.id !== null && + fnType.id < 0 && + queryElem.id !== null && + queryElem.id < 0 + ) { if (mgens && mgens.has(queryElem.id) && mgens.get(queryElem.id) !== fnType.id) { continue; @@ -2959,8 +3445,10 @@ class DocSearch { )) { continue; } + // @ts-expect-error if (fnType.id < 0) { const highlightedGenerics = unifyFunctionTypes( + // @ts-expect-error whereClause[(-fnType.id) - 1], queryElems, whereClause, @@ -2998,12 +3486,13 @@ class DocSearch { } } } + // @ts-expect-error return false; } // Multiple element recursive case /** - * @type Array + * @type {Array} */ const fnTypes = fnTypesIn.slice(); /** @@ -3033,7 +3522,7 @@ class DocSearch { continue; } let mgensScratch; - if (fnType.id < 0) { + if (fnType.id !== null && queryElem.id !== null && fnType.id < 0) { mgensScratch = new Map(mgens); if (mgensScratch.has(queryElem.id) && mgensScratch.get(queryElem.id) !== fnType.id) { @@ -3052,8 +3541,11 @@ class DocSearch { if (!queryElemsTmp) { queryElemsTmp = queryElems.slice(0, qlast); } + /** @type {rustdoc.HighlightedFunctionType[]|null} */ let unifiedGenerics = []; + // @ts-expect-error let unifiedGenericsMgens = null; + /** @type {rustdoc.HighlightedFunctionType[]|null} */ const passesUnification = unifyFunctionTypes( fnTypes, queryElemsTmp, @@ -3102,11 +3594,14 @@ class DocSearch { bindings: new Map([...fnType.bindings.entries()].map(([k, v]) => { return [k, queryElem.bindings.has(k) ? unifyFunctionTypes( v, + // @ts-expect-error queryElem.bindings.get(k), whereClause, + // @ts-expect-error unifiedGenericsMgens, solutionCb, unboxingDepth, + // @ts-expect-error ) : unifiedGenerics.splice(0, v.length)]; })), }); @@ -3128,7 +3623,7 @@ class DocSearch { )) { continue; } - const generics = fnType.id < 0 ? + const generics = fnType.id !== null && fnType.id < 0 ? whereClause[(-fnType.id) - 1] : fnType.generics; const bindings = fnType.bindings ? @@ -3171,17 +3666,19 @@ class DocSearch { * `Vec` of `Allocators` and not the implicit `Allocator` parameter that every * `Vec` has. * - * @param {Array} fnTypesIn - The objects to check. - * @param {Array} queryElems - The elements from the parsed query. - * @param {[FunctionType]} whereClause - Trait bounds for generic items. + * @param {Array} fnTypesIn - The objects to check. + * @param {Array} queryElems - The elements from the parsed query. + * @param {rustdoc.FunctionType[][]} whereClause - Trait bounds for generic items. * @param {Map|null} mgensIn * - Map functions generics to query generics (never modified). - * @param {Map -> bool} solutionCb - Called for each `mgens` solution. + * @param {function(Map): boolean} solutionCb + * - Called for each `mgens` solution. * @param {number} unboxingDepth * - Limit checks that Ty matches Vec, * but not Vec>>>> * - * @return {[FunctionType]|null} - Returns highlighted results if a match, null otherwise. + * @return {rustdoc.HighlightedFunctionType[]|null} + * - Returns highlighted results if a match, null otherwise. */ function unifyGenericTypes( fnTypesIn, @@ -3195,10 +3692,11 @@ class DocSearch { return null; } /** - * @type Map|null + * @type {Map|null} */ const mgens = mgensIn === null ? null : new Map(mgensIn); if (queryElems.length === 0) { + // @ts-expect-error return solutionCb(mgens) ? fnTypesIn : null; } if (!fnTypesIn || fnTypesIn.length === 0) { @@ -3207,7 +3705,11 @@ class DocSearch { const fnType = fnTypesIn[0]; const queryElem = queryElems[0]; if (unifyFunctionTypeIsMatchCandidate(fnType, queryElem, mgens)) { - if (fnType.id < 0 && queryElem.id < 0) { + if (fnType.id !== null && + fnType.id < 0 && + queryElem.id !== null && + queryElem.id < 0 + ) { if (!mgens || !mgens.has(queryElem.id) || mgens.get(queryElem.id) === fnType.id ) { @@ -3238,6 +3740,7 @@ class DocSearch { queryElems.slice(1), whereClause, mgens, + // @ts-expect-error mgensScratch => { const solution = unifyFunctionTypeCheckBindings( fnType, @@ -3285,7 +3788,7 @@ class DocSearch { unboxingDepth + 1, )) { let highlightedRemaining; - if (fnType.id < 0) { + if (fnType.id !== null && fnType.id < 0) { // Where clause corresponds to `F: A + B` // ^^^^^ // The order of the constraints doesn't matter, so @@ -3295,6 +3798,7 @@ class DocSearch { [queryElem], whereClause, mgens, + // @ts-expect-error mgensScratch => { const hl = unifyGenericTypes( fnTypesIn.slice(1), @@ -3316,6 +3820,7 @@ class DocSearch { highlighted: true, }, fnType, { generics: highlightedGenerics, + // @ts-expect-error }), ...highlightedRemaining]; } } else { @@ -3327,6 +3832,7 @@ class DocSearch { [queryElem], whereClause, mgens, + // @ts-expect-error mgensScratch => { const hl = unifyGenericTypes( fnTypesIn.slice(1), @@ -3349,6 +3855,7 @@ class DocSearch { bindings: new Map([...fnType.bindings.entries()].map(([k, v]) => { return [k, highlightedGenerics.splice(0, v.length)]; })), + // @ts-expect-error }), ...highlightedRemaining]; } } @@ -3364,8 +3871,8 @@ class DocSearch { * or associated type bindings: that's not load-bearing, but it prevents unnecessary * backtracking later. * - * @param {FunctionType} fnType - * @param {QueryElement} queryElem + * @param {rustdoc.FunctionType} fnType + * @param {rustdoc.QueryElement} queryElem * @param {Map|null} mgensIn - Map query generics to function generics. * @returns {boolean} */ @@ -3377,7 +3884,7 @@ class DocSearch { // fnType.id < 0 means generic // queryElem.id < 0 does too // mgensIn[queryElem.id] = fnType.id - if (fnType.id < 0 && queryElem.id < 0) { + if (fnType.id !== null && fnType.id < 0 && queryElem.id !== null && queryElem.id < 0) { if ( mgensIn && mgensIn.has(queryElem.id) && mgensIn.get(queryElem.id) !== fnType.id @@ -3456,13 +3963,15 @@ class DocSearch { * ID of u32 in it, and the rest of the matching engine acts as if `Iterator` were * the type instead. * - * @param {FunctionType} fnType - * @param {QueryElement} queryElem - * @param {[FunctionType]} whereClause - Trait bounds for generic items. - * @param {Map} mgensIn - Map query generics to function generics. + * @param {rustdoc.FunctionType} fnType + * @param {rustdoc.QueryElement} queryElem + * @param {rustdoc.FunctionType[][]} whereClause - Trait bounds for generic items. + * @param {Map|null} mgensIn - Map query generics to function generics. * Never modified. * @param {number} unboxingDepth - * @returns {false|{mgens: [Map], simplifiedGenerics: [FunctionType]}} + * @returns {false|{ + * mgens: [Map|null], simplifiedGenerics: rustdoc.FunctionType[] + * }} */ function unifyFunctionTypeCheckBindings( fnType, @@ -3486,8 +3995,10 @@ class DocSearch { } const fnTypeBindings = fnType.bindings.get(name); mgensSolutionSet = mgensSolutionSet.flatMap(mgens => { + // @ts-expect-error const newSolutions = []; unifyFunctionTypes( + // @ts-expect-error fnTypeBindings, constraints, whereClause, @@ -3500,6 +4011,7 @@ class DocSearch { }, unboxingDepth, ); + // @ts-expect-error return newSolutions; }); } @@ -3519,14 +4031,15 @@ class DocSearch { } else { simplifiedGenerics = binds; } + // @ts-expect-error return { simplifiedGenerics, mgens: mgensSolutionSet }; } return { simplifiedGenerics, mgens: [mgensIn] }; } /** - * @param {FunctionType} fnType - * @param {QueryElement} queryElem - * @param {[FunctionType]} whereClause - Trait bounds for generic items. + * @param {rustdoc.FunctionType} fnType + * @param {rustdoc.QueryElement} queryElem + * @param {rustdoc.FunctionType[][]} whereClause - Trait bounds for generic items. * @param {Map|null} mgens - Map query generics to function generics. * @param {number} unboxingDepth * @returns {boolean} @@ -3541,7 +4054,7 @@ class DocSearch { if (unboxingDepth >= UNBOXING_LIMIT) { return false; } - if (fnType.id < 0) { + if (fnType.id !== null && fnType.id < 0) { if (!whereClause) { return false; } @@ -3577,14 +4090,14 @@ class DocSearch { * This function checks if the given list contains any * (non-generic) types mentioned in the query. * - * @param {Array} list - A list of function types. - * @param {[FunctionType]} where_clause - Trait bounds for generic items. + * @param {rustdoc.FunctionType[]} list - A list of function types. + * @param {rustdoc.FunctionType[][]} where_clause - Trait bounds for generic items. */ function containsTypeFromQuery(list, where_clause) { if (!list) return false; for (const ty of parsedQuery.returned) { // negative type ids are generics - if (ty.id < 0) { + if (ty.id !== null && ty.id < 0) { continue; } if (checkIfInList(list, ty, where_clause, null, 0)) { @@ -3592,7 +4105,7 @@ class DocSearch { } } for (const ty of parsedQuery.elems) { - if (ty.id < 0) { + if (ty.id !== null && ty.id < 0) { continue; } if (checkIfInList(list, ty, where_clause, null, 0)) { @@ -3606,9 +4119,9 @@ class DocSearch { * This function checks if the object (`row`) matches the given type (`elem`) and its * generics (if any). * - * @param {Array} list - * @param {QueryElement} elem - The element from the parsed query. - * @param {[FunctionType]} whereClause - Trait bounds for generic items. + * @param {rustdoc.FunctionType[]} list + * @param {rustdoc.QueryElement} elem - The element from the parsed query. + * @param {rustdoc.FunctionType[][]} whereClause - Trait bounds for generic items. * @param {Map|null} mgens - Map functions generics to query generics. * @param {number} unboxingDepth * @@ -3627,18 +4140,20 @@ class DocSearch { * This function checks if the object (`row`) matches the given type (`elem`) and its * generics (if any). * - * @param {Row} row - * @param {QueryElement} elem - The element from the parsed query. - * @param {[FunctionType]} whereClause - Trait bounds for generic items. + * @param {rustdoc.FunctionType} row + * @param {rustdoc.QueryElement} elem - The element from the parsed query. + * @param {rustdoc.FunctionType[][]} whereClause - Trait bounds for generic items. * @param {Map|null} mgens - Map query generics to function generics. * * @return {boolean} - Returns true if the type matches, false otherwise. */ + // @ts-expect-error const checkType = (row, elem, whereClause, mgens, unboxingDepth) => { if (unboxingDepth >= UNBOXING_LIMIT) { return false; } - if (row.id > 0 && elem.id > 0 && elem.pathWithoutLast.length === 0 && + if (row.id !== null && elem.id !== null && + row.id > 0 && elem.id > 0 && elem.pathWithoutLast.length === 0 && row.generics.length === 0 && elem.generics.length === 0 && row.bindings.size === 0 && elem.bindings.size === 0 && // special case @@ -3648,6 +4163,7 @@ class DocSearch { ) { return row.id === elem.id && typePassesFilter(elem.typeFilter, row.ty); } else { + // @ts-expect-error return unifyFunctionTypes( [row], [elem], @@ -3662,6 +4178,7 @@ class DocSearch { /** * Check a query solution for conflicting generics. */ + // @ts-expect-error const checkTypeMgensForConflict = mgens => { if (!mgens) { return true; @@ -3679,7 +4196,7 @@ class DocSearch { /** * Compute an "edit distance" that ignores missing path elements. * @param {string[]} contains search query path - * @param {Row} ty indexed item + * @param {rustdoc.Row} ty indexed item * @returns {null|number} edit distance */ function checkPath(contains, ty) { @@ -3720,6 +4237,7 @@ class DocSearch { return ret_dist > maxPathEditDistance ? null : ret_dist; } + // @ts-expect-error function typePassesFilter(filter, type) { // No filter or Exact mach if (filter <= NO_TYPE_FILTER || filter === type) return true; @@ -3741,6 +4259,7 @@ class DocSearch { return false; } + // @ts-expect-error function createAliasFromItem(item) { return { crate: item.crate, @@ -3758,11 +4277,14 @@ class DocSearch { }; } + // @ts-expect-error const handleAliases = async(ret, query, filterCrates, currentCrate) => { const lowerQuery = query.toLowerCase(); // We separate aliases and crate aliases because we want to have current crate // aliases to be before the others in the displayed results. + // @ts-expect-error const aliases = []; + // @ts-expect-error const crateAliases = []; if (filterCrates !== null) { if (this.ALIASES.has(filterCrates) @@ -3775,6 +4297,7 @@ class DocSearch { } else { for (const [crate, crateAliasesIndex] of this.ALIASES) { if (crateAliasesIndex.has(lowerQuery)) { + // @ts-expect-error const pushTo = crate === currentCrate ? crateAliases : aliases; const query_aliases = crateAliasesIndex.get(lowerQuery); for (const alias of query_aliases) { @@ -3784,6 +4307,7 @@ class DocSearch { } } + // @ts-expect-error const sortFunc = (aaa, bbb) => { if (aaa.path < bbb.path) { return 1; @@ -3792,18 +4316,23 @@ class DocSearch { } return -1; }; + // @ts-expect-error crateAliases.sort(sortFunc); aliases.sort(sortFunc); + // @ts-expect-error const fetchDesc = alias => { + // @ts-expect-error return this.searchIndexEmptyDesc.get(alias.crate).contains(alias.bitIndex) ? "" : this.searchState.loadDesc(alias); }; const [crateDescs, descs] = await Promise.all([ + // @ts-expect-error Promise.all(crateAliases.map(fetchDesc)), Promise.all(aliases.map(fetchDesc)), ]); + // @ts-expect-error const pushFunc = alias => { alias.alias = query; const res = buildHrefAndPath(alias); @@ -3818,12 +4347,15 @@ class DocSearch { }; aliases.forEach((alias, i) => { + // @ts-expect-error alias.desc = descs[i]; }); aliases.forEach(pushFunc); + // @ts-expect-error crateAliases.forEach((alias, i) => { alias.desc = crateDescs[i]; }); + // @ts-expect-error crateAliases.forEach(pushFunc); }; @@ -3843,21 +4375,24 @@ class DocSearch { * * `path_dist` is zero if a single-component search query is used, otherwise it's the * distance computed for everything other than the last path component. * - * @param {Results} results + * @param {rustdoc.Results} results * @param {string} fullId - * @param {integer} id - * @param {integer} index - * @param {integer} dist - * @param {integer} path_dist + * @param {number} id + * @param {number} index + * @param {number} dist + * @param {number} path_dist */ + // @ts-expect-error function addIntoResults(results, fullId, id, index, dist, path_dist, maxEditDistance) { if (dist <= maxEditDistance || index !== -1) { if (results.has(fullId)) { const result = results.get(fullId); + // @ts-expect-error if (result.dontValidate || result.dist <= dist) { return; } } + // @ts-expect-error results.set(fullId, { id: id, index: index, @@ -3873,12 +4408,16 @@ class DocSearch { * try to match the items which validates all the elements. For `aa -> bb` will look for * functions which have a parameter `aa` and has `bb` in its returned values. * - * @param {Row} row - * @param {integer} pos - Position in the `searchIndex`. - * @param {Object} results + * @param {rustdoc.Row} row + * @param {number} pos - Position in the `searchIndex`. + * @param {rustdoc.Results} results */ function handleArgs(row, pos, results) { - if (!row || (filterCrates !== null && row.crate !== filterCrates) || !row.type) { + if (!row || (filterCrates !== null && row.crate !== filterCrates)) { + return; + } + const rowType = row.type; + if (!rowType) { return; } @@ -3889,21 +4428,23 @@ class DocSearch { if (tfpDist === null) { return; } + // @ts-expect-error if (results.size >= MAX_RESULTS && tfpDist > results.max_dist) { return; } // If the result is too "bad", we return false and it ends this search. if (!unifyFunctionTypes( - row.type.inputs, + rowType.inputs, parsedQuery.elems, - row.type.where_clause, + rowType.where_clause, null, + // @ts-expect-error mgens => { return unifyFunctionTypes( - row.type.output, + rowType.output, parsedQuery.returned, - row.type.where_clause, + rowType.where_clause, mgens, checkTypeMgensForConflict, 0, // unboxing depth @@ -3914,15 +4455,16 @@ class DocSearch { return; } + // @ts-expect-error results.max_dist = Math.max(results.max_dist || 0, tfpDist); - addIntoResults(results, row.id, pos, 0, tfpDist, 0, Number.MAX_VALUE); + addIntoResults(results, row.id.toString(), pos, 0, tfpDist, 0, Number.MAX_VALUE); } /** * Compare the query fingerprint with the function fingerprint. * - * @param {{number}} fullId - The function - * @param {{Uint32Array}} queryFingerprint - The query + * @param {number} fullId - The function + * @param {Uint32Array} queryFingerprint - The query * @returns {number|null} - Null if non-match, number if distance * This function might return 0! */ @@ -3953,138 +4495,10 @@ class DocSearch { const innerRunQuery = () => { - const queryLen = - parsedQuery.elems.reduce((acc, next) => acc + next.pathLast.length, 0) + - parsedQuery.returned.reduce((acc, next) => acc + next.pathLast.length, 0); - const maxEditDistance = Math.floor(queryLen / 3); - - /** - * @type {Map} - */ - const genericSymbols = new Map(); - - /** - * Convert names to ids in parsed query elements. - * This is not used for the "In Names" tab, but is used for the - * "In Params", "In Returns", and "In Function Signature" tabs. - * - * If there is no matching item, but a close-enough match, this - * function also that correction. - * - * See `buildTypeMapIndex` for more information. - * - * @param {QueryElement} elem - * @param {boolean} isAssocType - */ - const convertNameToId = (elem, isAssocType) => { - const loweredName = elem.pathLast.toLowerCase(); - if (this.typeNameIdMap.has(loweredName) && - (isAssocType || !this.typeNameIdMap.get(loweredName).assocOnly)) { - elem.id = this.typeNameIdMap.get(loweredName).id; - } else if (!parsedQuery.literalSearch) { - let match = null; - let matchDist = maxEditDistance + 1; - let matchName = ""; - for (const [name, { id, assocOnly }] of this.typeNameIdMap) { - const dist = Math.min( - editDistance(name, loweredName, maxEditDistance), - editDistance(name, elem.normalizedPathLast, maxEditDistance), - ); - if (dist <= matchDist && dist <= maxEditDistance && - (isAssocType || !assocOnly)) { - if (dist === matchDist && matchName > name) { - continue; - } - match = id; - matchDist = dist; - matchName = name; - } - } - if (match !== null) { - parsedQuery.correction = matchName; - } - elem.id = match; - } - if ((elem.id === null && parsedQuery.totalElems > 1 && elem.typeFilter === -1 - && elem.generics.length === 0 && elem.bindings.size === 0) - || elem.typeFilter === TY_GENERIC) { - if (genericSymbols.has(elem.normalizedPathLast)) { - elem.id = genericSymbols.get(elem.normalizedPathLast); - } else { - elem.id = -(genericSymbols.size + 1); - genericSymbols.set(elem.normalizedPathLast, elem.id); - } - if (elem.typeFilter === -1 && elem.normalizedPathLast.length >= 3) { - // Silly heuristic to catch if the user probably meant - // to not write a generic parameter. We don't use it, - // just bring it up. - const maxPartDistance = Math.floor(elem.normalizedPathLast.length / 3); - let matchDist = maxPartDistance + 1; - let matchName = ""; - for (const name of this.typeNameIdMap.keys()) { - const dist = editDistance( - name, - elem.normalizedPathLast, - maxPartDistance, - ); - if (dist <= matchDist && dist <= maxPartDistance) { - if (dist === matchDist && matchName > name) { - continue; - } - matchDist = dist; - matchName = name; - } - } - if (matchName !== "") { - parsedQuery.proposeCorrectionFrom = elem.name; - parsedQuery.proposeCorrectionTo = matchName; - } - } - elem.typeFilter = TY_GENERIC; - } - if (elem.generics.length > 0 && elem.typeFilter === TY_GENERIC) { - // Rust does not have HKT - parsedQuery.error = [ - "Generic type parameter ", - elem.name, - " does not accept generic parameters", - ]; - } - for (const elem2 of elem.generics) { - convertNameToId(elem2); - } - elem.bindings = new Map(Array.from(elem.bindings.entries()) - .map(entry => { - const [name, constraints] = entry; - if (!this.typeNameIdMap.has(name)) { - parsedQuery.error = [ - "Type parameter ", - name, - " does not exist", - ]; - return [null, []]; - } - for (const elem2 of constraints) { - convertNameToId(elem2); - } - - return [this.typeNameIdMap.get(name).id, constraints]; - }), - ); - }; - - for (const elem of parsedQuery.elems) { - convertNameToId(elem); - this.buildFunctionTypeFingerprint(elem, parsedQuery.typeFingerprint); - } - for (const elem of parsedQuery.returned) { - convertNameToId(elem); - this.buildFunctionTypeFingerprint(elem, parsedQuery.typeFingerprint); - } - if (parsedQuery.foundElems === 1 && !parsedQuery.hasReturnArrow) { const elem = parsedQuery.elems[0]; // use arrow functions to preserve `this`. + // @ts-expect-error const handleNameSearch = id => { const row = this.searchIndex[id]; if (!typePassesFilter(elem.typeFilter, row.ty) || @@ -4094,6 +4508,7 @@ class DocSearch { let pathDist = 0; if (elem.fullPath.length > 1) { + // @ts-expect-error pathDist = checkPath(elem.pathWithoutLast, row); if (pathDist === null) { return; @@ -4102,11 +4517,13 @@ class DocSearch { if (parsedQuery.literalSearch) { if (row.word === elem.pathLast) { + // @ts-expect-error addIntoResults(results_others, row.id, id, 0, 0, pathDist); } } else { addIntoResults( results_others, + // @ts-expect-error row.id, id, row.normalizedName.indexOf(elem.normalizedPathLast), @@ -4147,23 +4564,31 @@ class DocSearch { const returned = row.type && row.type.output && checkIfInList(row.type.output, elem, row.type.where_clause, null, 0); if (in_args) { + // @ts-expect-error results_in_args.max_dist = Math.max( + // @ts-expect-error results_in_args.max_dist || 0, tfpDist, ); const maxDist = results_in_args.size < MAX_RESULTS ? (tfpDist + 1) : + // @ts-expect-error results_in_args.max_dist; + // @ts-expect-error addIntoResults(results_in_args, row.id, i, -1, tfpDist, 0, maxDist); } if (returned) { + // @ts-expect-error results_returned.max_dist = Math.max( + // @ts-expect-error results_returned.max_dist || 0, tfpDist, ); const maxDist = results_returned.size < MAX_RESULTS ? (tfpDist + 1) : + // @ts-expect-error results_returned.max_dist; + // @ts-expect-error addIntoResults(results_returned, row.id, i, -1, tfpDist, 0, maxDist); } } @@ -4173,14 +4598,17 @@ class DocSearch { // types with generic parameters go last. // That's because of the way unification is structured: it eats off // the end, and hits a fast path if the last item is a simple atom. + // @ts-expect-error const sortQ = (a, b) => { const ag = a.generics.length === 0 && a.bindings.size === 0; const bg = b.generics.length === 0 && b.bindings.size === 0; if (ag !== bg) { + // @ts-expect-error return ag - bg; } const ai = a.id > 0; const bi = b.id > 0; + // @ts-expect-error return ai - bi; }; parsedQuery.elems.sort(sortQ); @@ -4197,8 +4625,11 @@ class DocSearch { const isType = parsedQuery.foundElems !== 1 || parsedQuery.hasReturnArrow; const [sorted_in_args, sorted_returned, sorted_others] = await Promise.all([ + // @ts-expect-error sortResults(results_in_args, "elems", currentCrate), + // @ts-expect-error sortResults(results_returned, "returned", currentCrate), + // @ts-expect-error sortResults(results_others, (isType ? "query" : null), currentCrate), ]); const ret = createQueryResults( @@ -4210,11 +4641,14 @@ class DocSearch { filterCrates, currentCrate); await Promise.all([ret.others, ret.returned, ret.in_args].map(async list => { const descs = await Promise.all(list.map(result => { + // @ts-expect-error return this.searchIndexEmptyDesc.get(result.crate).contains(result.bitIndex) ? "" : + // @ts-expect-error this.searchState.loadDesc(result); })); for (const [i, result] of list.entries()) { + // @ts-expect-error result.desc = descs[i]; } })); @@ -4229,7 +4663,9 @@ class DocSearch { // ==================== Core search logic end ==================== +/** @type {Map} */ let rawSearchIndex; +// @ts-expect-error let docSearch; const longItemTypes = [ "keyword", @@ -4259,13 +4695,16 @@ const longItemTypes = [ "derive macro", "trait alias", ]; +// @ts-expect-error let currentResults; // In the search display, allows to switch between tabs. +// @ts-expect-error function printTab(nb) { let iter = 0; let foundCurrentTab = false; let foundCurrentResultSet = false; + // @ts-expect-error onEachLazy(document.getElementById("search-tabs").childNodes, elem => { if (nb === iter) { addClass(elem, "selected"); @@ -4277,6 +4716,7 @@ function printTab(nb) { }); const isTypeSearch = (nb > 0 || iter === 1); iter = 0; + // @ts-expect-error onEachLazy(document.getElementById("results").childNodes, elem => { if (nb === iter) { addClass(elem, "active"); @@ -4287,12 +4727,15 @@ function printTab(nb) { iter += 1; }); if (foundCurrentTab && foundCurrentResultSet) { + // @ts-expect-error searchState.currentTab = nb; // Corrections only kick in on type-based searches. const correctionsElem = document.getElementsByClassName("search-corrections"); if (isTypeSearch) { + // @ts-expect-error removeClass(correctionsElem[0], "hidden"); } else { + // @ts-expect-error addClass(correctionsElem[0], "hidden"); } } else if (nb !== 0) { @@ -4326,16 +4769,22 @@ function getFilterCrates() { const elem = document.getElementById("crate-search"); if (elem && + // @ts-expect-error elem.value !== "all crates" && + // @ts-expect-error window.searchIndex.has(elem.value) ) { + // @ts-expect-error return elem.value; } return null; } +// @ts-expect-error function nextTab(direction) { + // @ts-expect-error const next = (searchState.currentTab + direction + 3) % searchState.focusedByTab.length; + // @ts-expect-error searchState.focusedByTab[searchState.currentTab] = document.activeElement; printTab(next); focusSearchResult(); @@ -4344,9 +4793,12 @@ function nextTab(direction) { // Focus the first search result on the active tab, or the result that // was focused last time this tab was active. function focusSearchResult() { + // @ts-expect-error const target = searchState.focusedByTab[searchState.currentTab] || document.querySelectorAll(".search-results.active a").item(0) || + // @ts-expect-error document.querySelectorAll("#search-tabs button").item(searchState.currentTab); + // @ts-expect-error searchState.focusedByTab[searchState.currentTab] = null; if (target) { target.focus(); @@ -4356,9 +4808,8 @@ function focusSearchResult() { /** * Render a set of search results for a single tab. * @param {Array} array - The search results for this tab - * @param {ParsedQuery} query + * @param {rustdoc.ParsedQuery} query * @param {boolean} display - True if this is the active tab - * @param {"sig"|"elems"|"returned"|null} typeInfo */ async function addTab(array, query, display) { const extraClass = display ? " active" : ""; @@ -4405,6 +4856,7 @@ ${item.displayPath}${name}\ if (item.displayTypeSignature) { const {type, mappedNames, whereClause} = await item.displayTypeSignature; const displayType = document.createElement("div"); + // @ts-expect-error type.forEach((value, index) => { if (index % 2 !== 0) { const highlight = document.createElement("strong"); @@ -4445,6 +4897,7 @@ ${item.displayPath}${name}\ const line = document.createElement("div"); line.className = "where"; line.appendChild(document.createTextNode(` ${name}: `)); + // @ts-expect-error innerType.forEach((value, index) => { if (index % 2 !== 0) { const highlight = document.createElement("strong"); @@ -4488,6 +4941,7 @@ ${item.displayPath}${name}\ return output; } +// @ts-expect-error function makeTabHeader(tabNb, text, nbElems) { // https://blog.horizon-eda.org/misc/2020/02/19/ui.html // @@ -4497,6 +4951,7 @@ function makeTabHeader(tabNb, text, nbElems) { const fmtNbElems = nbElems < 10 ? `\u{2007}(${nbElems})\u{2007}\u{2007}` : nbElems < 100 ? `\u{2007}(${nbElems})\u{2007}` : `\u{2007}(${nbElems})`; + // @ts-expect-error if (searchState.currentTab === tabNb) { return ""; @@ -4505,11 +4960,12 @@ function makeTabHeader(tabNb, text, nbElems) { } /** - * @param {ResultsTable} results + * @param {rustdoc.ResultsTable} results * @param {boolean} go_to_first * @param {string} filterCrates */ async function showResults(results, go_to_first, filterCrates) { + // @ts-expect-error const search = searchState.outputElement(); if (go_to_first || (results.others.length === 1 && getSettingValue("go-to-only-result") === "true") @@ -4527,6 +4983,7 @@ async function showResults(results, go_to_first, filterCrates) { // will be used, starting search again since the search input is not empty, leading you // back to the previous page again. window.onunload = () => { }; + // @ts-expect-error searchState.removeQueryParameters(); const elem = document.createElement("a"); elem.href = results.others[0].href; @@ -4537,6 +4994,7 @@ async function showResults(results, go_to_first, filterCrates) { return; } if (results.query === undefined) { + // @ts-expect-error results.query = DocSearch.parseQuery(searchState.input.value); } @@ -4545,6 +5003,7 @@ async function showResults(results, go_to_first, filterCrates) { // Navigate to the relevant tab if the current tab is empty, like in case users search // for "-> String". If they had selected another tab previously, they have to click on // it again. + // @ts-expect-error let currentTab = searchState.currentTab; if ((currentTab === 0 && results.others.length === 0) || (currentTab === 1 && results.in_args.length === 0) || @@ -4572,6 +5031,7 @@ async function showResults(results, go_to_first, filterCrates) {

Results

${crates}`; if (results.query.error !== null) { const error = results.query.error; + // @ts-expect-error error.forEach((value, index) => { value = value.split("<").join("<").split(">").join(">"); if (index % 2 !== 0) { @@ -4632,7 +5092,9 @@ async function showResults(results, go_to_first, filterCrates) { resultsElem.appendChild(ret_returned); search.innerHTML = output; + // @ts-expect-error if (searchState.rustdocToolbar) { + // @ts-expect-error search.querySelector(".main-heading").appendChild(searchState.rustdocToolbar); } const crateSearch = document.getElementById("crate-search"); @@ -4641,23 +5103,30 @@ async function showResults(results, go_to_first, filterCrates) { } search.appendChild(resultsElem); // Reset focused elements. + // @ts-expect-error searchState.showResults(search); + // @ts-expect-error const elems = document.getElementById("search-tabs").childNodes; + // @ts-expect-error searchState.focusedByTab = []; let i = 0; for (const elem of elems) { const j = i; + // @ts-expect-error elem.onclick = () => printTab(j); + // @ts-expect-error searchState.focusedByTab.push(null); i += 1; } printTab(currentTab); } +// @ts-expect-error function updateSearchHistory(url) { if (!browserSupportsHistoryApi()) { return; } + // @ts-expect-error const params = searchState.getQueryStringParams(); if (!history.state && !params.search) { history.pushState(null, "", url); @@ -4672,9 +5141,11 @@ function updateSearchHistory(url) { * @param {boolean} [forced] */ async function search(forced) { + // @ts-expect-error const query = DocSearch.parseQuery(searchState.input.value.trim()); let filterCrates = getFilterCrates(); + // @ts-expect-error if (!forced && query.userQuery === currentResults) { if (query.userQuery.length > 0) { putBackSearch(); @@ -4682,8 +5153,10 @@ async function search(forced) { return; } + // @ts-expect-error searchState.setLoadingSearch(); + // @ts-expect-error const params = searchState.getQueryStringParams(); // In case we have no information about the saved crate and there is a URL query parameter, @@ -4693,6 +5166,7 @@ async function search(forced) { } // Update document title to maintain a meaningful browser history + // @ts-expect-error searchState.title = "\"" + query.userQuery + "\" Search - Rust"; // Because searching is incremental by character, only the most @@ -4700,8 +5174,10 @@ async function search(forced) { updateSearchHistory(buildUrl(query.userQuery, filterCrates)); await showResults( + // @ts-expect-error await docSearch.execQuery(query, filterCrates, window.currentCrate), params.go_to_first, + // @ts-expect-error filterCrates); } @@ -4710,62 +5186,83 @@ async function search(forced) { * @param {Event} [e] - The event that triggered this call, if any */ function onSearchSubmit(e) { + // @ts-expect-error e.preventDefault(); + // @ts-expect-error searchState.clearInputTimeout(); search(); } function putBackSearch() { + // @ts-expect-error const search_input = searchState.input; + // @ts-expect-error if (!searchState.input) { return; } + // @ts-expect-error if (search_input.value !== "" && !searchState.isDisplayed()) { + // @ts-expect-error searchState.showResults(); if (browserSupportsHistoryApi()) { history.replaceState(null, "", buildUrl(search_input.value, getFilterCrates())); } + // @ts-expect-error document.title = searchState.title; } } function registerSearchEvents() { + // @ts-expect-error const params = searchState.getQueryStringParams(); // Populate search bar with query string search term when provided, // but only if the input bar is empty. This avoid the obnoxious issue // where you start trying to do a search, and the index loads, and // suddenly your search is gone! + // @ts-expect-error if (searchState.input.value === "") { + // @ts-expect-error searchState.input.value = params.search || ""; } const searchAfter500ms = () => { + // @ts-expect-error searchState.clearInputTimeout(); + // @ts-expect-error if (searchState.input.value.length === 0) { + // @ts-expect-error searchState.hideResults(); } else { + // @ts-expect-error searchState.timeout = setTimeout(search, 500); } }; + // @ts-expect-error searchState.input.onkeyup = searchAfter500ms; + // @ts-expect-error searchState.input.oninput = searchAfter500ms; + // @ts-expect-error document.getElementsByClassName("search-form")[0].onsubmit = onSearchSubmit; + // @ts-expect-error searchState.input.onchange = e => { if (e.target !== document.activeElement) { // To prevent doing anything when it's from a blur event. return; } // Do NOT e.preventDefault() here. It will prevent pasting. + // @ts-expect-error searchState.clearInputTimeout(); // zero-timeout necessary here because at the time of event handler execution the // pasted content is not in the input field yet. Shouldn’t make any difference for // change, though. setTimeout(search, 0); }; + // @ts-expect-error searchState.input.onpaste = searchState.input.onchange; + // @ts-expect-error searchState.outputElement().addEventListener("keydown", e => { // We only handle unmodified keystrokes here. We don't want to interfere with, // for instance, alt-left and alt-right for history navigation. @@ -4775,18 +5272,24 @@ function registerSearchEvents() { // up and down arrow select next/previous search result, or the // search box if we're already at the top. if (e.which === 38) { // up + // @ts-expect-error const previous = document.activeElement.previousElementSibling; if (previous) { + // @ts-expect-error previous.focus(); } else { + // @ts-expect-error searchState.focus(); } e.preventDefault(); } else if (e.which === 40) { // down + // @ts-expect-error const next = document.activeElement.nextElementSibling; if (next) { + // @ts-expect-error next.focus(); } + // @ts-expect-error const rect = document.activeElement.getBoundingClientRect(); if (window.innerHeight - rect.bottom < rect.height) { window.scrollBy(0, rect.height); @@ -4801,6 +5304,7 @@ function registerSearchEvents() { } }); + // @ts-expect-error searchState.input.addEventListener("keydown", e => { if (e.which === 40) { // down focusSearchResult(); @@ -4808,11 +5312,14 @@ function registerSearchEvents() { } }); + // @ts-expect-error searchState.input.addEventListener("focus", () => { putBackSearch(); }); + // @ts-expect-error searchState.input.addEventListener("blur", () => { + // @ts-expect-error searchState.input.placeholder = searchState.input.origPlaceholder; }); @@ -4823,6 +5330,7 @@ function registerSearchEvents() { const previousTitle = document.title; window.addEventListener("popstate", e => { + // @ts-expect-error const params = searchState.getQueryStringParams(); // Revert to the previous title manually since the History // API ignores the title parameter. @@ -4836,6 +5344,7 @@ function registerSearchEvents() { // nothing there, which lets you really go back to a // previous state with nothing in the bar. if (params.search && params.search.length > 0) { + // @ts-expect-error searchState.input.value = params.search; // Some browsers fire "onpopstate" for every page load // (Chrome), while others fire the event only when actually @@ -4845,9 +5354,11 @@ function registerSearchEvents() { e.preventDefault(); search(); } else { + // @ts-expect-error searchState.input.value = ""; // When browsing back from search results the main page // visibility must be reset. + // @ts-expect-error searchState.hideResults(); } }); @@ -4860,17 +5371,22 @@ function registerSearchEvents() { // that try to sync state between the URL and the search input. To work around it, // do a small amount of re-init on page show. window.onpageshow = () => { + // @ts-expect-error const qSearch = searchState.getQueryStringParams().search; + // @ts-expect-error if (searchState.input.value === "" && qSearch) { + // @ts-expect-error searchState.input.value = qSearch; } search(); }; } +// @ts-expect-error function updateCrate(ev) { if (ev.target.value === "all crates") { // If we don't remove it from the URL, it'll be picked up again by the search. + // @ts-expect-error const query = searchState.input.value.trim(); updateSearchHistory(buildUrl(query, null)); } @@ -4881,9 +5397,11 @@ function updateCrate(ev) { search(true); } +// @ts-expect-error function initSearch(searchIndx) { rawSearchIndex = searchIndx; if (typeof window !== "undefined") { + // @ts-expect-error docSearch = new DocSearch(rawSearchIndex, ROOT_PATH, searchState); registerSearchEvents(); // If there's a search term in the URL, execute the search now. @@ -4891,6 +5409,7 @@ function initSearch(searchIndx) { search(); } } else if (typeof exports !== "undefined") { + // @ts-expect-error docSearch = new DocSearch(rawSearchIndex, ROOT_PATH, searchState); exports.docSearch = docSearch; exports.parseQuery = DocSearch.parseQuery; @@ -4902,8 +5421,11 @@ if (typeof exports !== "undefined") { } if (typeof window !== "undefined") { + // @ts-expect-error window.initSearch = initSearch; + // @ts-expect-error if (window.searchIndex !== undefined) { + // @ts-expect-error initSearch(window.searchIndex); } } else { @@ -4918,19 +5440,23 @@ if (typeof window !== "undefined") { // https://fossies.org/linux/lucene/lucene/core/src/java/org/apache/lucene/util/automaton/ // LevenshteinAutomata.java class ParametricDescription { + // @ts-expect-error constructor(w, n, minErrors) { this.w = w; this.n = n; this.minErrors = minErrors; } + // @ts-expect-error isAccept(absState) { const state = Math.floor(absState / (this.w + 1)); const offset = absState % (this.w + 1); return this.w - offset + this.minErrors[state] <= this.n; } + // @ts-expect-error getPosition(absState) { return absState % (this.w + 1); } + // @ts-expect-error getVector(name, charCode, pos, end) { let vector = 0; for (let i = pos; i < end; i += 1) { @@ -4941,6 +5467,7 @@ class ParametricDescription { } return vector; } + // @ts-expect-error unpack(data, index, bitsPerValue) { const bitLoc = (bitsPerValue * index); const dataLoc = bitLoc >> 5; diff --git a/src/librustdoc/html/static/js/settings.js b/src/librustdoc/html/static/js/settings.js index 183663b94fc28..d7b0e4b4f541e 100644 --- a/src/librustdoc/html/static/js/settings.js +++ b/src/librustdoc/html/static/js/settings.js @@ -3,6 +3,9 @@ /* global addClass, removeClass, onEach, onEachLazy */ /* global MAIN_ID, getVar, getSettingsButton, getHelpButton */ +// Eventually fix this. +// @ts-nocheck + "use strict"; (function() { diff --git a/src/librustdoc/html/static/js/src-script.js b/src/librustdoc/html/static/js/src-script.js index 3003f4c150338..8f712f4c20c7b 100644 --- a/src/librustdoc/html/static/js/src-script.js +++ b/src/librustdoc/html/static/js/src-script.js @@ -5,6 +5,9 @@ /* global addClass, onEachLazy, removeClass, browserSupportsHistoryApi */ /* global updateLocalStorage, getVar */ +// Eventually fix this. +// @ts-nocheck + "use strict"; (function() { diff --git a/src/librustdoc/html/static/js/storage.js b/src/librustdoc/html/static/js/storage.js index d77804d045e36..4770ccc127993 100644 --- a/src/librustdoc/html/static/js/storage.js +++ b/src/librustdoc/html/static/js/storage.js @@ -5,15 +5,28 @@ // the page, so we don't see major layout changes during the load of the page. "use strict"; +/** + * @import * as rustdoc from "./rustdoc.d.ts"; + */ + const builtinThemes = ["light", "dark", "ayu"]; const darkThemes = ["dark", "ayu"]; -window.currentTheme = document.getElementById("themeStyle"); +window.currentTheme = (function() { + const currentTheme = document.getElementById("themeStyle"); + return currentTheme instanceof HTMLLinkElement ? currentTheme : null; +})(); const settingsDataset = (function() { const settingsElement = document.getElementById("default-settings"); return settingsElement && settingsElement.dataset ? settingsElement.dataset : null; })(); +/** + * Get a configuration value. If it's not set, get the default. + * + * @param {string} settingName + * @returns + */ function getSettingValue(settingName) { const current = getCurrentValue(settingName); if (current === null && settingsDataset !== null) { @@ -29,17 +42,39 @@ function getSettingValue(settingName) { const localStoredTheme = getSettingValue("theme"); +/** + * Check if a DOM Element has the given class set. + * If `elem` is null, returns false. + * + * @param {HTMLElement|null} elem + * @param {string} className + * @returns {boolean} + */ // eslint-disable-next-line no-unused-vars function hasClass(elem, className) { - return elem && elem.classList && elem.classList.contains(className); + return !!elem && !!elem.classList && elem.classList.contains(className); } +/** + * Add a class to a DOM Element. If `elem` is null, + * does nothing. This function is idempotent. + * + * @param {HTMLElement|null} elem + * @param {string} className + */ function addClass(elem, className) { if (elem && elem.classList) { elem.classList.add(className); } } +/** + * Remove a class from a DOM Element. If `elem` is null, + * does nothing. This function is idempotent. + * + * @param {HTMLElement|null} elem + * @param {string} className + */ // eslint-disable-next-line no-unused-vars function removeClass(elem, className) { if (elem && elem.classList) { @@ -49,8 +84,8 @@ function removeClass(elem, className) { /** * Run a callback for every element of an Array. - * @param {Array} arr - The array to iterate over - * @param {function(?)} func - The callback + * @param {Array} arr - The array to iterate over + * @param {function(?): boolean|undefined} func - The callback */ function onEach(arr, func) { for (const elem of arr) { @@ -67,8 +102,8 @@ function onEach(arr, func) { * or a "live" NodeList while modifying it can be very slow. * https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection * https://developer.mozilla.org/en-US/docs/Web/API/NodeList - * @param {NodeList|HTMLCollection} lazyArray - An array to iterate over - * @param {function(?)} func - The callback + * @param {NodeList|HTMLCollection} lazyArray - An array to iterate over + * @param {function(?): boolean} func - The callback */ // eslint-disable-next-line no-unused-vars function onEachLazy(lazyArray, func) { @@ -77,6 +112,15 @@ function onEachLazy(lazyArray, func) { func); } +/** + * Set a configuration value. This uses localstorage, + * with a `rustdoc-` prefix, to avoid clashing with other + * web apps that may be running in the same domain (for example, mdBook). + * If localStorage is disabled, this function does nothing. + * + * @param {string} name + * @param {string} value + */ function updateLocalStorage(name, value) { try { window.localStorage.setItem("rustdoc-" + name, value); @@ -85,6 +129,15 @@ function updateLocalStorage(name, value) { } } +/** + * Get a configuration value. If localStorage is disabled, + * this function returns null. If the setting was never + * changed by the user, it also returns null; if you want to + * be able to use a default value, call `getSettingValue` instead. + * + * @param {string} name + * @returns {string|null} + */ function getCurrentValue(name) { try { return window.localStorage.getItem("rustdoc-" + name); @@ -93,19 +146,29 @@ function getCurrentValue(name) { } } -// Get a value from the rustdoc-vars div, which is used to convey data from -// Rust to the JS. If there is no such element, return null. -const getVar = (function getVar(name) { +/** + * Get a value from the rustdoc-vars div, which is used to convey data from + * Rust to the JS. If there is no such element, return null. + * + * @param {string} name + * @returns {string|null} + */ +function getVar(name) { const el = document.querySelector("head > meta[name='rustdoc-vars']"); - return el ? el.attributes["data-" + name].value : null; -}); + return el ? el.getAttribute("data-" + name) : null; +} +/** + * Change the current theme. + * @param {string|null} newThemeName + * @param {boolean} saveTheme + */ function switchTheme(newThemeName, saveTheme) { - const themeNames = getVar("themes").split(",").filter(t => t); + const themeNames = (getVar("themes") || "").split(",").filter(t => t); themeNames.push(...builtinThemes); // Ensure that the new theme name is among the defined themes - if (themeNames.indexOf(newThemeName) === -1) { + if (newThemeName === null || themeNames.indexOf(newThemeName) === -1) { return; } @@ -118,7 +181,7 @@ function switchTheme(newThemeName, saveTheme) { document.documentElement.setAttribute("data-theme", newThemeName); if (builtinThemes.indexOf(newThemeName) !== -1) { - if (window.currentTheme) { + if (window.currentTheme && window.currentTheme.parentNode) { window.currentTheme.parentNode.removeChild(window.currentTheme); window.currentTheme = null; } @@ -130,7 +193,10 @@ function switchTheme(newThemeName, saveTheme) { // rendering, but if we are done, it would blank the page. if (document.readyState === "loading") { document.write(``); - window.currentTheme = document.getElementById("themeStyle"); + window.currentTheme = (function() { + const currentTheme = document.getElementById("themeStyle"); + return currentTheme instanceof HTMLLinkElement ? currentTheme : null; + })(); } else { window.currentTheme = document.createElement("link"); window.currentTheme.rel = "stylesheet"; @@ -179,11 +245,13 @@ const updateTheme = (function() { return updateTheme; })(); +// @ts-ignore if (getSettingValue("use-system-theme") !== "false" && window.matchMedia) { // update the preferred dark theme if the user is already using a dark theme // See https://github.com/rust-lang/rust/pull/77809#issuecomment-707875732 if (getSettingValue("use-system-theme") === null && getSettingValue("preferred-dark-theme") === null + && localStoredTheme !== null && darkThemes.indexOf(localStoredTheme) >= 0) { updateLocalStorage("preferred-dark-theme", localStoredTheme); } diff --git a/src/librustdoc/html/static/js/tsconfig.json b/src/librustdoc/html/static/js/tsconfig.json new file mode 100644 index 0000000000000..b81099bb9dfd0 --- /dev/null +++ b/src/librustdoc/html/static/js/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "es2023", + "module": "esnext", + "rootDir": "./", + "allowJs": true, + "checkJs": true, + "noEmit": true, + "strict": true, + "skipLibCheck": true + }, + "typeAcquisition": { + "include": ["./rustdoc.d.ts"] + } +} diff --git a/src/tools/clippy/.github/deploy.sh b/src/tools/clippy/.github/deploy.sh index ea118a3b6fce5..2f062799a3607 100644 --- a/src/tools/clippy/.github/deploy.sh +++ b/src/tools/clippy/.github/deploy.sh @@ -45,6 +45,8 @@ if [[ -n $TAG_NAME ]]; then git add "$TAG_NAME" # Update the symlink git add stable + # Update the index.html file + git add index.html git commit -m "Add documentation for ${TAG_NAME} release: ${SHA}" elif [[ $BETA = "true" ]]; then if git diff --exit-code --quiet -- beta/; then diff --git a/src/tools/clippy/.github/driver.sh b/src/tools/clippy/.github/driver.sh index 701be6bd76d34..5a81b41129188 100755 --- a/src/tools/clippy/.github/driver.sh +++ b/src/tools/clippy/.github/driver.sh @@ -47,9 +47,9 @@ unset CARGO_MANIFEST_DIR # Run a lint and make sure it produces the expected output. It's also expected to exit with code 1 # FIXME: How to match the clippy invocation in compile-test.rs? -./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/box_default.rs 2>box_default.stderr && exit 1 -sed -e "/= help: for/d" box_default.stderr > normalized.stderr -diff -u normalized.stderr tests/ui/box_default.stderr +./target/debug/clippy-driver -Dwarnings -Aunused -Zui-testing --emit metadata --crate-type bin tests/ui/string_to_string.rs 2>string_to_string.stderr && exit 1 +sed -e "/= help: for/d" string_to_string.stderr > normalized.stderr +diff -u normalized.stderr tests/ui/string_to_string.stderr # make sure "clippy-driver --rustc --arg" and "rustc --arg" behave the same SYSROOT=$(rustc --print sysroot) diff --git a/src/tools/clippy/.github/workflows/clippy_changelog.yml b/src/tools/clippy/.github/workflows/clippy_changelog.yml new file mode 100644 index 0000000000000..a2657bfea490d --- /dev/null +++ b/src/tools/clippy/.github/workflows/clippy_changelog.yml @@ -0,0 +1,59 @@ +name: Clippy changelog check + +on: + merge_group: + pull_request: + types: [opened, reopened, synchronize, edited] + +concurrency: + # For a given workflow, if we push to the same PR, cancel all previous builds on that PR. + # If the push is not attached to a PR, we will cancel all builds on the same branch. + group: "${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}" + cancel-in-progress: true + +jobs: + changelog: + runs-on: ubuntu-latest + + defaults: + run: + shell: bash + + steps: + # Run + - name: Check Changelog + if: ${{ github.event_name == 'pull_request' }} + run: | + body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR_NUMBER" | \ + python -c "import sys, json; print(json.load(sys.stdin)['body'])") + output=$(awk '/^changelog:\s*\S/ && !/changelog: \[.*\]: your change/' <<< "$body" | sed "s/changelog:\s*//g") + if [ -z "$output" ]; then + echo "ERROR: pull request message must contain 'changelog: ...' with your changelog. Please add it." + exit 1 + else + echo "changelog: $output" + fi + env: + PYTHONIOENCODING: 'utf-8' + PR_NUMBER: '${{ github.event.number }}' + + # We need to have the "conclusion" job also on PR CI, to make it possible + # to add PRs to a merge queue. + conclusion_changelog: + needs: [ changelog ] + # We need to ensure this job does *not* get skipped if its dependencies fail, + # because a skipped job is considered a success by GitHub. So we have to + # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run + # when the workflow is canceled manually. + # + # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! + if: ${{ !cancelled() }} + runs-on: ubuntu-latest + steps: + # Manually check the status of all dependencies. `if: failure()` does not work. + - name: Conclusion + run: | + # Print the dependent jobs to see them in the CI log + jq -C <<< '${{ toJson(needs) }}' + # Check if all jobs that we depend on (in the needs array) were successful. + jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}' diff --git a/src/tools/clippy/.github/workflows/clippy_mq.yml b/src/tools/clippy/.github/workflows/clippy_mq.yml index dee7d028655e3..c337a96bdac52 100644 --- a/src/tools/clippy/.github/workflows/clippy_mq.yml +++ b/src/tools/clippy/.github/workflows/clippy_mq.yml @@ -15,37 +15,7 @@ defaults: shell: bash jobs: - changelog: - runs-on: ubuntu-latest - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - ref: ${{ github.ref }} - # Unsetting this would make so that any malicious package could get our Github Token - persist-credentials: false - - # Run - - name: Check Changelog - run: | - MESSAGE=$(git log --format=%B -n 1) - PR=$(echo "$MESSAGE" | grep -o "#[0-9]*" | head -1 | sed -e 's/^#//') - body=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s "https://api.github.com/repos/rust-lang/rust-clippy/pulls/$PR" | \ - python -c "import sys, json; print(json.load(sys.stdin)['body'])") - output=$(grep "^changelog:\s*\S" <<< "$body" | sed "s/changelog:\s*//g") || { - echo "ERROR: PR body must contain 'changelog: ...'" - exit 1 - } - if [[ "$output" = "none" ]]; then - echo "WARNING: changelog is 'none'" - else - echo "changelog: $output" - fi - env: - PYTHONIOENCODING: 'utf-8' base: - needs: changelog strategy: matrix: include: @@ -119,7 +89,6 @@ jobs: OS: ${{ runner.os }} metadata_collection: - needs: changelog runs-on: ubuntu-latest steps: @@ -138,7 +107,6 @@ jobs: run: cargo collect-metadata integration_build: - needs: changelog runs-on: ubuntu-latest steps: @@ -228,7 +196,7 @@ jobs: INTEGRATION: ${{ matrix.integration }} conclusion: - needs: [ changelog, base, metadata_collection, integration_build, integration ] + needs: [ base, metadata_collection, integration_build, integration ] # We need to ensure this job does *not* get skipped if its dependencies fail, # because a skipped job is considered a success by GitHub. So we have to # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run diff --git a/src/tools/clippy/.github/workflows/lintcheck.yml b/src/tools/clippy/.github/workflows/lintcheck.yml index 64966f1d1898b..d487c7d949857 100644 --- a/src/tools/clippy/.github/workflows/lintcheck.yml +++ b/src/tools/clippy/.github/workflows/lintcheck.yml @@ -1,6 +1,12 @@ name: Lintcheck -on: pull_request +on: + pull_request: + paths-ignore: + - 'book/**' + - 'util/**' + - 'tests/**' + - '*.md' env: RUST_BACKTRACE: 1 diff --git a/src/tools/clippy/.github/workflows/remark.yml b/src/tools/clippy/.github/workflows/remark.yml index 69d00dc027e85..13902f78b541d 100644 --- a/src/tools/clippy/.github/workflows/remark.yml +++ b/src/tools/clippy/.github/workflows/remark.yml @@ -27,7 +27,7 @@ jobs: - name: Install mdbook run: | mkdir mdbook - curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.34/mdbook-v0.4.34-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook + curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.43/mdbook-v0.4.43-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook echo `pwd`/mdbook >> $GITHUB_PATH # Run diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 1770e8095a01c..bc42c07224e1a 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -5533,6 +5533,7 @@ Released 2018-09-13 [`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown [`doc_nested_refdefs`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_nested_refdefs +[`doc_overindented_list_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_overindented_list_items [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons [`double_ended_iterator_last`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_ended_iterator_last [`double_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_must_use @@ -5762,11 +5763,13 @@ Released 2018-09-13 [`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy [`manual_next_back`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_next_back [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive +[`manual_ok_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_err [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or [`manual_pattern_char_comparison`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_pattern_char_comparison [`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains [`manual_range_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_patterns [`manual_rem_euclid`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid +[`manual_repeat_n`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_repeat_n [`manual_retain`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain [`manual_rotate`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rotate [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic @@ -5904,6 +5907,7 @@ Released 2018-09-13 [`non_minimal_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_minimal_cfg [`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions [`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty +[`non_std_lazy_statics`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_std_lazy_statics [`non_zero_suggestions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_zero_suggestions [`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool [`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options @@ -6063,6 +6067,7 @@ Released 2018-09-13 [`size_of_in_element_count`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_in_element_count [`size_of_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#size_of_ref [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next +[`sliced_string_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#sliced_string_as_bytes [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization [`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive [`std_instead_of_alloc`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_alloc @@ -6172,12 +6177,14 @@ Released 2018-09-13 [`unnecessary_safety_comment`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_comment [`unnecessary_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc [`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports +[`unnecessary_semicolon`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_semicolon [`unnecessary_sort_by`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_sort_by [`unnecessary_struct_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_struct_initialization [`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap [`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps [`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern +[`unneeded_struct_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_struct_pattern [`unneeded_wildcard_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_wildcard_pattern [`unnested_or_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns [`unreachable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unreachable @@ -6216,6 +6223,7 @@ Released 2018-09-13 [`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion [`useless_format`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_format [`useless_let_if_seq`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_let_if_seq +[`useless_nonzero_new_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_nonzero_new_unchecked [`useless_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_transmute [`useless_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_vec [`vec_box`]: https://rust-lang.github.io/rust-clippy/master/index.html#vec_box diff --git a/src/tools/clippy/CONTRIBUTING.md b/src/tools/clippy/CONTRIBUTING.md index 1f6c918fc6cc6..3b33f71906383 100644 --- a/src/tools/clippy/CONTRIBUTING.md +++ b/src/tools/clippy/CONTRIBUTING.md @@ -199,7 +199,7 @@ currently. Between writing new lints, fixing issues, reviewing pull requests and responding to issues there may not always be enough time to stay on top of it all. -Our highest priority is fixing [crashes][l-crash] and [bugs][l-bug], for example +Our highest priority is fixing [ICEs][I-ICE] and [bugs][C-bug], for example an ICE in a popular crate that many other crates depend on. We don't want Clippy to crash on your code and we want it to be as reliable as the suggestions from Rust compiler errors. @@ -213,8 +213,8 @@ Or rather: before the sync this should be addressed, e.g. by removing a lint again, so it doesn't hit beta/stable. [triage]: https://forge.rust-lang.org/release/triage-procedure.html -[l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash -[l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug +[I-ICE]: https://github.com/rust-lang/rust-clippy/labels/I-ICE +[C-bug]: https://github.com/rust-lang/rust-clippy/labels/C-bug [p-low]: https://github.com/rust-lang/rust-clippy/labels/P-low [p-medium]: https://github.com/rust-lang/rust-clippy/labels/P-medium [p-high]: https://github.com/rust-lang/rust-clippy/labels/P-high diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index efa59fecc0e88..c9e4f15afbf8e 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -10,7 +10,7 @@ license = "MIT OR Apache-2.0" keywords = ["clippy", "lint", "plugin"] categories = ["development-tools", "development-tools::cargo-plugins"] build = "build.rs" -edition = "2021" +edition = "2024" publish = false [[bin]] diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md index cb3a22d4288fa..32c1d33e2ed36 100644 --- a/src/tools/clippy/README.md +++ b/src/tools/clippy/README.md @@ -159,11 +159,11 @@ line. (You can swap `clippy::all` with the specific lint category you are target You can add options to your code to `allow`/`warn`/`deny` Clippy lints: * the whole set of `Warn` lints using the `clippy` lint group (`#![deny(clippy::all)]`). - Note that `rustc` has additional [lint groups](https://doc.rust-lang.org/rustc/lints/groups.html). + Note that `rustc` has additional [lint groups](https://doc.rust-lang.org/rustc/lints/groups.html). * all lints using both the `clippy` and `clippy::pedantic` lint groups (`#![deny(clippy::all)]`, - `#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive - lints prone to false positives. + `#![deny(clippy::pedantic)]`). Note that `clippy::pedantic` contains some very aggressive + lints prone to false positives. * only some lints (`#![deny(clippy::single_match, clippy::box_vec)]`, etc.) diff --git a/src/tools/clippy/book/book.toml b/src/tools/clippy/book/book.toml index 93b6641f7e1e7..c918aadf83c4b 100644 --- a/src/tools/clippy/book/book.toml +++ b/src/tools/clippy/book/book.toml @@ -6,7 +6,7 @@ src = "src" title = "Clippy Documentation" [rust] -edition = "2018" +edition = "2024" [output.html] edit-url-template = "https://github.com/rust-lang/rust-clippy/edit/master/book/{path}" diff --git a/src/tools/clippy/book/src/development/adding_lints.md b/src/tools/clippy/book/src/development/adding_lints.md index c07568697d02d..c26ad319f4f5d 100644 --- a/src/tools/clippy/book/src/development/adding_lints.md +++ b/src/tools/clippy/book/src/development/adding_lints.md @@ -299,10 +299,11 @@ This is good, because it makes writing this particular lint less complicated. We have to make this decision with every new Clippy lint. It boils down to using either [`EarlyLintPass`][early_lint_pass] or [`LateLintPass`][late_lint_pass]. -In short, the `EarlyLintPass` runs before type checking and -[HIR](https://rustc-dev-guide.rust-lang.org/hir.html) lowering and the `LateLintPass` -has access to type information. Consider using the `LateLintPass` unless you need -something specific from the `EarlyLintPass`. +`EarlyLintPass` runs before type checking and +[HIR](https://rustc-dev-guide.rust-lang.org/hir.html) lowering, while `LateLintPass` +runs after these stages, providing access to type information. The `cargo dev new_lint` command +defaults to the recommended `LateLintPass`, but you can specify `--pass=early` if your lint +only needs AST level analysis. Since we don't need type information for checking the function name, we used `--pass=early` when running the new lint automation and all the imports were @@ -537,7 +538,7 @@ via `Tools -> Clippy` and you should see the generated code in the output below. If the command was executed successfully, you can copy the code over to where you are implementing your lint. -[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55 +[author_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=9a12cb60e5c6ad4e3003ac6d5e63cf55 ## Print HIR lint @@ -552,7 +553,7 @@ attribute to expressions you often need to enable _Clippy_. [_High-Level Intermediate Representation (HIR)_]: https://rustc-dev-guide.rust-lang.org/hir.html -[print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=daf14db3a7f39ca467cd1b86c34b9afb +[print_hir_example]: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2024&gist=daf14db3a7f39ca467cd1b86c34b9afb ## Documentation diff --git a/src/tools/clippy/book/src/development/common_tools_writing_lints.md b/src/tools/clippy/book/src/development/common_tools_writing_lints.md index c354e8914f5db..b44ad80a25cc8 100644 --- a/src/tools/clippy/book/src/development/common_tools_writing_lints.md +++ b/src/tools/clippy/book/src/development/common_tools_writing_lints.md @@ -265,10 +265,10 @@ functions to deal with macros: ``` [Ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html -[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.TyKind.html +[TyKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_type_ir/ty_kind/enum.TyKind.html [TypeckResults]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html [expr_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.expr_ty [LateContext]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint/struct.LateContext.html [TyCtxt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html -[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TypeckResults.html#method.pat_ty +[pat_ty]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TypeckResults.html#method.pat_ty [paths]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/paths/index.html diff --git a/src/tools/clippy/book/src/development/defining_lints.md b/src/tools/clippy/book/src/development/defining_lints.md index ceabb255e2d0b..169cecd7d11dc 100644 --- a/src/tools/clippy/book/src/development/defining_lints.md +++ b/src/tools/clippy/book/src/development/defining_lints.md @@ -139,10 +139,10 @@ Untracked files: ``` -## The `define_clippy_lints` macro +## The `declare_clippy_lint` macro After `cargo dev new_lint`, you should see a macro with the name -`define_clippy_lints`. It will be in the same file if you defined a standalone +`declare_clippy_lint`. It will be in the same file if you defined a standalone lint, and it will be in `mod.rs` if you defined a type-specific lint. The macro looks something like this: diff --git a/src/tools/clippy/book/src/development/infrastructure/release.md b/src/tools/clippy/book/src/development/infrastructure/release.md index 20b870eb69a8e..8b080c099b81c 100644 --- a/src/tools/clippy/book/src/development/infrastructure/release.md +++ b/src/tools/clippy/book/src/development/infrastructure/release.md @@ -96,9 +96,9 @@ git tag rust-1.XX.0 # XX should be exchanged with the correspondin git push upstream rust-1.XX.0 # `upstream` is the `rust-lang/rust-clippy` remote ``` -After this, the release should be available on the Clippy [release page]. +After this, the release should be available on the Clippy [tags page]. -[release page]: https://github.com/rust-lang/rust-clippy/releases +[tags page]: https://github.com/rust-lang/rust-clippy/tags ## Publish `clippy_utils` diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index 181e794e6e468..b8f9fff9c6138 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -750,6 +750,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`manual_pattern_char_comparison`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_pattern_char_comparison) * [`manual_range_contains`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains) * [`manual_rem_euclid`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid) +* [`manual_repeat_n`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_repeat_n) * [`manual_retain`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain) * [`manual_split_once`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once) * [`manual_str_repeat`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat) @@ -762,11 +763,13 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`mem_replace_with_default`](https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default) * [`missing_const_for_fn`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn) * [`needless_borrow`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow) +* [`non_std_lazy_statics`](https://rust-lang.github.io/rust-clippy/master/index.html#non_std_lazy_statics) * [`option_as_ref_deref`](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref) * [`option_map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or) * [`ptr_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr) * [`redundant_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names) * [`redundant_static_lifetimes`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes) +* [`same_item_push`](https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push) * [`seek_from_current`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current) * [`seek_rewind`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind) * [`transmute_ptr_to_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref) diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml index c761e207c6b61..e473a58394022 100644 --- a/src/tools/clippy/clippy_config/Cargo.toml +++ b/src/tools/clippy/clippy_config/Cargo.toml @@ -3,7 +3,7 @@ name = "clippy_config" # begin autogenerated version version = "0.1.86" # end autogenerated version -edition = "2021" +edition = "2024" publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index c616589c56e05..552141476f3ac 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -619,6 +619,7 @@ define_Conf! { manual_pattern_char_comparison, manual_range_contains, manual_rem_euclid, + manual_repeat_n, manual_retain, manual_split_once, manual_str_repeat, @@ -631,11 +632,13 @@ define_Conf! { mem_replace_with_default, missing_const_for_fn, needless_borrow, + non_std_lazy_statics, option_as_ref_deref, option_map_unwrap_or, ptr_as_ptr, redundant_field_names, redundant_static_lifetimes, + same_item_push, seek_from_current, seek_rewind, transmute_ptr_to_ref, diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml index d3a103eaf4c62..47b7b3758613c 100644 --- a/src/tools/clippy/clippy_dev/Cargo.toml +++ b/src/tools/clippy/clippy_dev/Cargo.toml @@ -2,7 +2,7 @@ name = "clippy_dev" description = "Clippy developer tooling" version = "0.0.1" -edition = "2021" +edition = "2024" [dependencies] aho-corasick = "1.0" diff --git a/src/tools/clippy/clippy_dev/src/setup/intellij.rs b/src/tools/clippy/clippy_dev/src/setup/intellij.rs index a7138f36a4ef5..c56811ee0a01e 100644 --- a/src/tools/clippy/clippy_dev/src/setup/intellij.rs +++ b/src/tools/clippy/clippy_dev/src/setup/intellij.rs @@ -62,7 +62,7 @@ fn check_and_get_rustc_dir(rustc_path: &str) -> Result { eprintln!("error: unable to get the absolute path of rustc ({err})"); return Err(()); }, - }; + } } let path = path.join("compiler"); diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index 612d1c0ae1398..fc0780f89a7f7 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -842,7 +842,7 @@ fn try_rename_file(old_name: &Path, new_name: &Path) -> bool { Ok(file) => drop(file), Err(e) if matches!(e.kind(), io::ErrorKind::AlreadyExists | io::ErrorKind::NotFound) => return false, Err(e) => panic_file(e, new_name, "create"), - }; + } match fs::rename(old_name, new_name) { Ok(()) => true, Err(e) => { diff --git a/src/tools/clippy/clippy_dummy/Cargo.toml b/src/tools/clippy/clippy_dummy/Cargo.toml index c206a1eb07b50..61bdd421c764e 100644 --- a/src/tools/clippy/clippy_dummy/Cargo.toml +++ b/src/tools/clippy/clippy_dummy/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clippy_dummy" # rename to clippy before publishing version = "0.0.303" -edition = "2018" +edition = "2024" readme = "crates-readme.md" description = "A bunch of helpful lints to avoid common pitfalls in Rust." build = 'build.rs' diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index b575ac1bf4cc5..c62a7ec783b6f 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -8,7 +8,7 @@ repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" license = "MIT OR Apache-2.0" keywords = ["clippy", "lint", "plugin"] -edition = "2021" +edition = "2024" [dependencies] arrayvec = { version = "0.7", default-features = false } diff --git a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs index 380b094d017e0..0389223c3e0fa 100644 --- a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -445,8 +445,8 @@ fn convert_assoc_item_kind(value: AssocItemKind) -> SourceItemOrderingTraitAssoc #[allow(clippy::enum_glob_use)] // Very local glob use for legibility. use SourceItemOrderingTraitAssocItemKind::*; match value { - AssocItemKind::Const { .. } => Const, - AssocItemKind::Type { .. } => Type, + AssocItemKind::Const => Const, + AssocItemKind::Type => Type, AssocItemKind::Fn { .. } => Fn, } } diff --git a/src/tools/clippy/clippy_lints/src/assigning_clones.rs b/src/tools/clippy/clippy_lints/src/assigning_clones.rs index c8dd77d9578d7..c01155ca86e01 100644 --- a/src/tools/clippy/clippy_lints/src/assigning_clones.rs +++ b/src/tools/clippy/clippy_lints/src/assigning_clones.rs @@ -257,7 +257,7 @@ fn build_sugg<'tcx>( // The receiver may have been a value type, so we need to add an `&` to // be sure the argument to clone_from will be a reference. arg_sugg = arg_sugg.addr(); - }; + } format!("{receiver_sugg}.clone_from({arg_sugg})") }, diff --git a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs index 8c91c65eaf76b..3e4bcfbfc1905 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs @@ -60,7 +60,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item_span: Span, attrs: &[Attribute]) } outer_attr_kind.insert(kind); }, - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/cargo/mod.rs b/src/tools/clippy/clippy_lints/src/cargo/mod.rs index 96a2b1614646e..60371dcd7715d 100644 --- a/src/tools/clippy/clippy_lints/src/cargo/mod.rs +++ b/src/tools/clippy/clippy_lints/src/cargo/mod.rs @@ -161,6 +161,15 @@ declare_clippy_lint! { /// [dependencies] /// regex = "*" /// ``` + /// Use instead: + /// ```toml + /// [dependencies] + /// # allow patch updates, but not minor or major version changes + /// some_crate_1 = "~1.2.3" + /// + /// # pin the version to a specific version + /// some_crate_2 = "=1.2.3" + /// ``` #[clippy::version = "1.32.0"] pub WILDCARD_DEPENDENCIES, cargo, diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs index 3cf4a43b0d4c7..504d0a267e47d 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs @@ -84,6 +84,6 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca diag .note("`usize` and `isize` may be as small as 16 bits on some platforms") .note("for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types"); - }; + } }); } diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs index 4be53ace6871e..45045e58ac75e 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs @@ -205,7 +205,7 @@ fn expr_muldiv_sign(cx: &LateContext<'_>, expr: &Expr<'_>) -> Sign { // - uncertain if there are any uncertain values (because they could be negative or positive), Sign::Uncertain => return Sign::Uncertain, Sign::ZeroOrPositive => (), - }; + } } // A mul/div is: @@ -236,7 +236,7 @@ fn expr_add_sign(cx: &LateContext<'_>, expr: &Expr<'_>) -> Sign { // - uncertain if there are any uncertain values (because they could be negative or positive), Sign::Uncertain => return Sign::Uncertain, Sign::ZeroOrPositive => positive_count += 1, - }; + } } // A sum is: diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs index 364f5c7dc7a08..1edfde974227e 100644 --- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs @@ -273,7 +273,7 @@ fn get_types_from_cast<'a>( }, _ => {}, } - }; + } None } diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 87e546fbf0144..86bcf8edd578b 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -142,6 +142,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::doc::DOC_LINK_WITH_QUOTES_INFO, crate::doc::DOC_MARKDOWN_INFO, crate::doc::DOC_NESTED_REFDEFS_INFO, + crate::doc::DOC_OVERINDENTED_LIST_ITEMS_INFO, crate::doc::EMPTY_DOCS_INFO, crate::doc::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO, crate::doc::EMPTY_LINE_AFTER_OUTER_ATTR_INFO, @@ -335,6 +336,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::matches::INFALLIBLE_DESTRUCTURING_MATCH_INFO, crate::matches::MANUAL_FILTER_INFO, crate::matches::MANUAL_MAP_INFO, + crate::matches::MANUAL_OK_ERR_INFO, crate::matches::MANUAL_UNWRAP_OR_INFO, crate::matches::MATCH_AS_REF_INFO, crate::matches::MATCH_BOOL_INFO, @@ -419,6 +421,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::methods::MANUAL_IS_VARIANT_AND_INFO, crate::methods::MANUAL_NEXT_BACK_INFO, crate::methods::MANUAL_OK_OR_INFO, + crate::methods::MANUAL_REPEAT_N_INFO, crate::methods::MANUAL_SATURATING_ARITHMETIC_INFO, crate::methods::MANUAL_SPLIT_ONCE_INFO, crate::methods::MANUAL_STR_REPEAT_INFO, @@ -466,6 +469,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::methods::SHOULD_IMPLEMENT_TRAIT_INFO, crate::methods::SINGLE_CHAR_ADD_STR_INFO, crate::methods::SKIP_WHILE_NEXT_INFO, + crate::methods::SLICED_STRING_AS_BYTES_INFO, crate::methods::STABLE_SORT_PRIMITIVE_INFO, crate::methods::STRING_EXTEND_CHARS_INFO, crate::methods::STRING_LIT_CHARS_ANY_INFO, @@ -495,6 +499,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::methods::UNWRAP_OR_DEFAULT_INFO, crate::methods::UNWRAP_USED_INFO, crate::methods::USELESS_ASREF_INFO, + crate::methods::USELESS_NONZERO_NEW_UNCHECKED_INFO, crate::methods::VEC_RESIZE_TO_ZERO_INFO, crate::methods::VERBOSE_FILE_READS_INFO, crate::methods::WAKER_CLONE_WAKE_INFO, @@ -572,6 +577,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::non_expressive_names::SIMILAR_NAMES_INFO, crate::non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS_INFO, crate::non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY_INFO, + crate::non_std_lazy_statics::NON_STD_LAZY_STATICS_INFO, crate::non_zero_suggestions::NON_ZERO_SUGGESTIONS_INFO, crate::nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES_INFO, crate::octal_escapes::OCTAL_ESCAPES_INFO, @@ -753,8 +759,10 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::unnecessary_map_on_constructor::UNNECESSARY_MAP_ON_CONSTRUCTOR_INFO, crate::unnecessary_owned_empty_strings::UNNECESSARY_OWNED_EMPTY_STRINGS_INFO, crate::unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS_INFO, + crate::unnecessary_semicolon::UNNECESSARY_SEMICOLON_INFO, crate::unnecessary_struct_initialization::UNNECESSARY_STRUCT_INITIALIZATION_INFO, crate::unnecessary_wraps::UNNECESSARY_WRAPS_INFO, + crate::unneeded_struct_pattern::UNNEEDED_STRUCT_PATTERN_INFO, crate::unnested_or_patterns::UNNESTED_OR_PATTERNS_INFO, crate::unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME_INFO, crate::unused_async::UNUSED_ASYNC_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs index 33a97222b8f8f..bbd5dc15542d1 100644 --- a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs +++ b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs @@ -80,6 +80,6 @@ impl LateLintPass<'_> for DefaultConstructedUnitStructs { String::new(), Applicability::MachineApplicable, ); - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs index c04a73c890f0e..ca3eaae7b85f3 100644 --- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs +++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs @@ -223,19 +223,17 @@ impl<'tcx> Visitor<'tcx> for NumericFallbackVisitor<'_, 'tcx> { } fn visit_pat(&mut self, pat: &'tcx Pat<'_>) { - match pat.kind { - PatKind::Expr(&PatExpr { - hir_id, - kind: PatExprKind::Lit { lit, .. }, - .. - }) => { - let ty = self.cx.typeck_results().node_type(hir_id); - self.check_lit(lit, ty, hir_id); - return; - }, - _ => {}, + if let PatKind::Expr(&PatExpr { + hir_id, + kind: PatExprKind::Lit { lit, .. }, + .. + }) = pat.kind + { + let ty = self.cx.typeck_results().node_type(hir_id); + self.check_lit(lit, ty, hir_id); + return; } - walk_pat(self, pat) + walk_pat(self, pat); } fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) { diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index f5589d8f8e250..123e358d7c3df 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -331,7 +331,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { deref_count += 1; }, None => break None, - }; + } }; let use_node = use_cx.use_node(cx); diff --git a/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs b/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs index f9e4a43c0e7a8..2577324f23df9 100644 --- a/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs +++ b/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs @@ -1,11 +1,12 @@ -use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use itertools::Itertools; use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_span::{BytePos, Span}; +use std::cmp::Ordering; use std::ops::Range; -use super::DOC_LAZY_CONTINUATION; +use super::{DOC_LAZY_CONTINUATION, DOC_OVERINDENTED_LIST_ITEMS}; fn map_container_to_text(c: &super::Container) -> &'static str { match c { @@ -28,12 +29,57 @@ pub(super) fn check( return; } + // Blockquote let ccount = doc[range.clone()].chars().filter(|c| *c == '>').count(); let blockquote_level = containers .iter() .filter(|c| matches!(c, super::Container::Blockquote)) .count(); - let lcount = doc[range.clone()].chars().filter(|c| *c == ' ').count(); + if ccount < blockquote_level { + span_lint_and_then( + cx, + DOC_LAZY_CONTINUATION, + span, + "doc quote line without `>` marker", + |diag| { + let mut doc_start_range = &doc[range]; + let mut suggested = String::new(); + for c in containers { + let text = map_container_to_text(c); + if doc_start_range.starts_with(text) { + doc_start_range = &doc_start_range[text.len()..]; + span = span.with_lo( + span.lo() + BytePos(u32::try_from(text.len()).expect("text is not 2**32 or bigger")), + ); + } else if matches!(c, super::Container::Blockquote) + && let Some(i) = doc_start_range.find('>') + { + doc_start_range = &doc_start_range[i + 1..]; + span = span + .with_lo(span.lo() + BytePos(u32::try_from(i).expect("text is not 2**32 or bigger") + 1)); + } else { + suggested.push_str(text); + } + } + diag.span_suggestion_verbose( + span, + "add markers to start of line", + suggested, + Applicability::MachineApplicable, + ); + diag.help("if this not intended to be a quote at all, escape it with `\\>`"); + }, + ); + return; + } + + if ccount != 0 && blockquote_level != 0 { + // If this doc is a blockquote, we don't go further. + return; + } + + // List + let leading_spaces = doc[range].chars().filter(|c| *c == ' ').count(); let list_indentation = containers .iter() .map(|c| { @@ -44,50 +90,36 @@ pub(super) fn check( } }) .sum(); - if ccount < blockquote_level || lcount < list_indentation { - let msg = if ccount < blockquote_level { - "doc quote line without `>` marker" - } else { - "doc list item without indentation" - }; - span_lint_and_then(cx, DOC_LAZY_CONTINUATION, span, msg, |diag| { - if ccount == 0 && blockquote_level == 0 { + match leading_spaces.cmp(&list_indentation) { + Ordering::Less => span_lint_and_then( + cx, + DOC_LAZY_CONTINUATION, + span, + "doc list item without indentation", + |diag| { // simpler suggestion style for indentation - let indent = list_indentation - lcount; + let indent = list_indentation - leading_spaces; diag.span_suggestion_verbose( span.shrink_to_hi(), "indent this line", - std::iter::repeat(" ").take(indent).join(""), + std::iter::repeat_n(" ", indent).join(""), Applicability::MaybeIncorrect, ); diag.help("if this is supposed to be its own paragraph, add a blank line"); - return; - } - let mut doc_start_range = &doc[range]; - let mut suggested = String::new(); - for c in containers { - let text = map_container_to_text(c); - if doc_start_range.starts_with(text) { - doc_start_range = &doc_start_range[text.len()..]; - span = span - .with_lo(span.lo() + BytePos(u32::try_from(text.len()).expect("text is not 2**32 or bigger"))); - } else if matches!(c, super::Container::Blockquote) - && let Some(i) = doc_start_range.find('>') - { - doc_start_range = &doc_start_range[i + 1..]; - span = - span.with_lo(span.lo() + BytePos(u32::try_from(i).expect("text is not 2**32 or bigger") + 1)); - } else { - suggested.push_str(text); - } - } - diag.span_suggestion_verbose( + }, + ), + Ordering::Greater => { + let sugg = std::iter::repeat_n(" ", list_indentation).join(""); + span_lint_and_sugg( + cx, + DOC_OVERINDENTED_LIST_ITEMS, span, - "add markers to start of line", - suggested, - Applicability::MachineApplicable, + "doc list item overindented", + format!("try using `{sugg}` ({list_indentation} spaces)"), + sugg, + Applicability::MaybeIncorrect, ); - diag.help("if this not intended to be a quote at all, escape it with `\\>`"); - }); + }, + Ordering::Equal => {}, } } diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index 7561a6cf2a780..15530c3dbc509 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -428,6 +428,39 @@ declare_clippy_lint! { "require every line of a paragraph to be indented and marked" } +declare_clippy_lint! { + /// ### What it does + /// + /// Detects overindented list items in doc comments where the continuation + /// lines are indented more than necessary. + /// + /// ### Why is this bad? + /// + /// Overindented list items in doc comments can lead to inconsistent and + /// poorly formatted documentation when rendered. Excessive indentation may + /// cause the text to be misinterpreted as a nested list item or code block, + /// affecting readability and the overall structure of the documentation. + /// + /// ### Example + /// + /// ```no_run + /// /// - This is the first item in a list + /// /// and this line is overindented. + /// # fn foo() {} + /// ``` + /// + /// Fixes this into: + /// ```no_run + /// /// - This is the first item in a list + /// /// and this line is overindented. + /// # fn foo() {} + /// ``` + #[clippy::version = "1.80.0"] + pub DOC_OVERINDENTED_LIST_ITEMS, + style, + "ensure list items are not overindented" +} + declare_clippy_lint! { /// ### What it does /// Checks if the first paragraph in the documentation of items listed in the module page is too long. @@ -617,6 +650,7 @@ impl_lint_pass!(Documentation => [ SUSPICIOUS_DOC_COMMENTS, EMPTY_DOCS, DOC_LAZY_CONTINUATION, + DOC_OVERINDENTED_LIST_ITEMS, EMPTY_LINE_AFTER_OUTER_ATTR, EMPTY_LINE_AFTER_DOC_COMMENTS, TOO_LONG_FIRST_DOC_PARAGRAPH, diff --git a/src/tools/clippy/clippy_lints/src/enum_clike.rs b/src/tools/clippy/clippy_lints/src/enum_clike.rs index e9e9d00907ea0..a090a987d4fc3 100644 --- a/src/tools/clippy/clippy_lints/src/enum_clike.rs +++ b/src/tools/clippy/clippy_lints/src/enum_clike.rs @@ -70,7 +70,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant { var.span, "C-like enum variant discriminant is not portable to 32-bit targets", ); - }; + } } } } diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 57a30de7ad14a..52b699274bbbd 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -3,7 +3,9 @@ use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet_opt; use clippy_utils::ty::get_type_diagnostic_name; use clippy_utils::usage::{local_used_after_expr, local_used_in}; -use clippy_utils::{get_path_from_caller_to_method_type, is_adjusted, path_to_local, path_to_local_id}; +use clippy_utils::{ + get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local, path_to_local_id, +}; use rustc_errors::Applicability; use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, Safety, TyKind}; use rustc_infer::infer::TyCtxtInferExt; @@ -101,19 +103,20 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx }; if body.value.span.from_expansion() { - if body.params.is_empty() { - if let Some(VecArgs::Vec(&[])) = VecArgs::hir(cx, body.value) { - // replace `|| vec![]` with `Vec::new` - span_lint_and_sugg( - cx, - REDUNDANT_CLOSURE, - expr.span, - "redundant closure", - "replace the closure with `Vec::new`", - "std::vec::Vec::new".into(), - Applicability::MachineApplicable, - ); - } + if body.params.is_empty() + && let Some(VecArgs::Vec(&[])) = VecArgs::hir(cx, body.value) + { + let vec_crate = if is_no_std_crate(cx) { "alloc" } else { "std" }; + // replace `|| vec![]` with `Vec::new` + span_lint_and_sugg( + cx, + REDUNDANT_CLOSURE, + expr.span, + "redundant closure", + "replace the closure with `Vec::new`", + format!("{vec_crate}::vec::Vec::new"), + Applicability::MachineApplicable, + ); } // skip `foo(|| macro!())` return; diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs index 688979311c89a..cdbfe7af8f9de 100644 --- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs +++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs @@ -183,7 +183,7 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> { .collect() }; self.emit_sugg(spans, msg, help); - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs index 05e341e06fde4..752dbc0db4dbc 100644 --- a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs +++ b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs @@ -45,7 +45,7 @@ pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body: for param in generics.params { if param.is_impl_trait() { report(cx, param, generics); - }; + } } } } diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs index 1a01f5f885c30..90d3db2700fa0 100644 --- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -160,7 +160,7 @@ fn check_needless_must_use( && !is_must_use_ty(cx, future_ty) { return; - }; + } } span_lint_and_help( diff --git a/src/tools/clippy/clippy_lints/src/future_not_send.rs b/src/tools/clippy/clippy_lints/src/future_not_send.rs index bb2dc9995df7e..3ccfa51ab70b7 100644 --- a/src/tools/clippy/clippy_lints/src/future_not_send.rs +++ b/src/tools/clippy/clippy_lints/src/future_not_send.rs @@ -79,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { && let Some(future_trait) = cx.tcx.lang_items().future_trait() && let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::Send) { - let preds = cx.tcx.explicit_item_super_predicates(def_id); + let preds = cx.tcx.explicit_item_self_bounds(def_id); let is_future = preds.iter_instantiated_copied(cx.tcx, args).any(|(p, _)| { p.as_trait_clause() .is_some_and(|trait_pred| trait_pred.skip_binder().trait_ref.def_id == future_trait) diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs index dd5908553e59b..41d2b18803d95 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs @@ -120,7 +120,7 @@ fn get_const<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<(u128, B let ecx = ConstEvalCtxt::new(cx); if let Some(Constant::Int(c)) = ecx.eval(r) { return Some((c, op.node, l)); - }; + } if let Some(Constant::Int(c)) = ecx.eval(l) { return Some((c, invert_op(op.node)?, r)); } diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs index 37481dc7feb7b..152d506a7c00d 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs @@ -350,7 +350,7 @@ fn check_with_condition<'tcx>( if cx.typeck_results().expr_ty(cond_left).is_signed() { } else { print_lint_and_sugg(cx, var_name, expr); - }; + } } }, ExprKind::Path(QPath::TypeRelative(_, name)) => { diff --git a/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs b/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs index 5131f5b7269b6..b1cb6da9475ba 100644 --- a/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs +++ b/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs @@ -65,6 +65,6 @@ impl LateLintPass<'_> for IterOverHashType { expr.span, "iteration over unordered hash-based type", ); - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs index 2d2438514cc9d..34ded6c65009d 100644 --- a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs @@ -41,6 +41,6 @@ impl<'tcx> LateLintPass<'tcx> for UnderscoreTyped { Some(ty.span.with_lo(local.pat.span.hi())), "remove the explicit type `_` declaration", ); - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index fad6f9d088031..4b700673d0f80 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -279,6 +279,7 @@ mod non_copy_const; mod non_expressive_names; mod non_octal_unix_permissions; mod non_send_fields_in_send_ty; +mod non_std_lazy_statics; mod non_zero_suggestions; mod nonstandard_macro_braces; mod octal_escapes; @@ -372,8 +373,10 @@ mod unnecessary_literal_bound; mod unnecessary_map_on_constructor; mod unnecessary_owned_empty_strings; mod unnecessary_self_imports; +mod unnecessary_semicolon; mod unnecessary_struct_initialization; mod unnecessary_wraps; +mod unneeded_struct_pattern; mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_async; @@ -680,7 +683,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(unit_types::UnitTypes)); store.register_late_pass(move |_| Box::new(loops::Loops::new(conf))); store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::new(lifetimes::Lifetimes)); + store.register_late_pass(move |_| Box::new(lifetimes::Lifetimes::new(conf))); store.register_late_pass(|_| Box::new(entry::HashMapPass)); store.register_late_pass(|_| Box::new(minmax::MinMaxPass)); store.register_late_pass(|_| Box::new(zero_div_zero::ZeroDiv)); @@ -970,5 +973,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp)); store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound)); store.register_late_pass(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf))); + store.register_late_pass(|_| Box::new(unneeded_struct_pattern::UnneededStructPattern)); + store.register_late_pass(|_| Box::::default()); + store.register_late_pass(move |_| Box::new(non_std_lazy_statics::NonStdLazyStatic::new(conf))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index e6761ea5c67c3..c9ab0beb5dfa0 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -1,4 +1,6 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::trait_ref_of_method; use itertools::Itertools; use rustc_ast::visit::{try_visit, walk_list}; @@ -20,7 +22,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter as middle_nested_filter; use rustc_middle::lint::in_external_macro; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::Span; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::{Ident, kw}; @@ -91,7 +93,19 @@ declare_clippy_lint! { "unused lifetimes in function definitions" } -declare_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]); +pub struct Lifetimes { + msrv: Msrv, +} + +impl Lifetimes { + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } + } +} + +impl_lint_pass!(Lifetimes => [NEEDLESS_LIFETIMES, EXTRA_UNUSED_LIFETIMES]); impl<'tcx> LateLintPass<'tcx> for Lifetimes { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { @@ -102,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { .. } = item.kind { - check_fn_inner(cx, sig, Some(id), None, generics, item.span, true); + check_fn_inner(cx, sig, Some(id), None, generics, item.span, true, &self.msrv); } else if let ItemKind::Impl(impl_) = item.kind { if !item.span.from_expansion() { report_extra_impl_lifetimes(cx, impl_); @@ -121,6 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { item.generics, item.span, report_extra_lifetimes, + &self.msrv, ); } } @@ -131,11 +146,14 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { TraitFn::Required(sig) => (None, Some(sig)), TraitFn::Provided(id) => (Some(id), None), }; - check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true); + check_fn_inner(cx, sig, body, trait_sig, item.generics, item.span, true, &self.msrv); } } + + extract_msrv_attr!(LateContext); } +#[allow(clippy::too_many_arguments)] fn check_fn_inner<'tcx>( cx: &LateContext<'tcx>, sig: &'tcx FnSig<'_>, @@ -144,6 +162,7 @@ fn check_fn_inner<'tcx>( generics: &'tcx Generics<'_>, span: Span, report_extra_lifetimes: bool, + msrv: &Msrv, ) { if in_external_macro(cx.sess(), span) || has_where_lifetimes(cx, generics) { return; @@ -195,7 +214,7 @@ fn check_fn_inner<'tcx>( } } - if let Some((elidable_lts, usages)) = could_use_elision(cx, sig.decl, body, trait_sig, generics.params) { + if let Some((elidable_lts, usages)) = could_use_elision(cx, sig.decl, body, trait_sig, generics.params, msrv) { if usages.iter().any(|usage| !usage.ident.span.eq_ctxt(span)) { return; } @@ -216,6 +235,7 @@ fn could_use_elision<'tcx>( body: Option, trait_sig: Option<&[Ident]>, named_generics: &'tcx [GenericParam<'_>], + msrv: &Msrv, ) -> Option<(Vec, Vec)> { // There are two scenarios where elision works: // * no output references, all input references have different LT @@ -249,17 +269,17 @@ fn could_use_elision<'tcx>( let input_lts = input_visitor.lts; let output_lts = output_visitor.lts; - if let Some(trait_sig) = trait_sig { - if explicit_self_type(cx, func, trait_sig.first().copied()) { - return None; - } + if let Some(trait_sig) = trait_sig + && non_elidable_self_type(cx, func, trait_sig.first().copied(), msrv) + { + return None; } if let Some(body_id) = body { let body = cx.tcx.hir().body(body_id); let first_ident = body.params.first().and_then(|param| param.pat.simple_ident()); - if explicit_self_type(cx, func, first_ident) { + if non_elidable_self_type(cx, func, first_ident, msrv) { return None; } @@ -332,9 +352,15 @@ fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxIndexSet(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option) -> bool { - if let Some(ident) = ident +// elision doesn't work for explicit self types before Rust 1.81, see rust-lang/rust#69064 +fn non_elidable_self_type<'tcx>( + cx: &LateContext<'tcx>, + func: &FnDecl<'tcx>, + ident: Option, + msrv: &Msrv, +) -> bool { + if !msrv.meets(msrvs::EXPLICIT_SELF_TYPE_ELISION) + && let Some(ident) = ident && ident.name == kw::SelfLower && !func.implicit_self.has_implicit_self() && let Some(self_ty) = func.inputs.first() @@ -488,11 +514,13 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_ false } +#[allow(clippy::struct_excessive_bools)] struct Usage { lifetime: Lifetime, in_where_predicate: bool, in_bounded_ty: bool, in_generics_arg: bool, + lifetime_elision_impossible: bool, } struct LifetimeChecker<'cx, 'tcx, F> { @@ -501,6 +529,7 @@ struct LifetimeChecker<'cx, 'tcx, F> { where_predicate_depth: usize, bounded_ty_depth: usize, generic_args_depth: usize, + lifetime_elision_impossible: bool, phantom: std::marker::PhantomData, } @@ -525,6 +554,7 @@ where where_predicate_depth: 0, bounded_ty_depth: 0, generic_args_depth: 0, + lifetime_elision_impossible: false, phantom: std::marker::PhantomData, } } @@ -566,6 +596,7 @@ where in_where_predicate: self.where_predicate_depth != 0, in_bounded_ty: self.bounded_ty_depth != 0, in_generics_arg: self.generic_args_depth != 0, + lifetime_elision_impossible: self.lifetime_elision_impossible, }); } } @@ -592,11 +623,44 @@ where self.generic_args_depth -= 1; } + fn visit_fn_decl(&mut self, fd: &'tcx FnDecl<'tcx>) -> Self::Result { + self.lifetime_elision_impossible = !is_candidate_for_elision(fd); + walk_fn_decl(self, fd); + self.lifetime_elision_impossible = false; + } + fn nested_visit_map(&mut self) -> Self::Map { self.cx.tcx.hir() } } +/// Check if `fd` supports function elision with an anonymous (or elided) lifetime, +/// and has a lifetime somewhere in its output type. +fn is_candidate_for_elision(fd: &FnDecl<'_>) -> bool { + struct V; + + impl Visitor<'_> for V { + type Result = ControlFlow; + + fn visit_lifetime(&mut self, lifetime: &Lifetime) -> Self::Result { + ControlFlow::Break(lifetime.is_elided() || lifetime.is_anonymous()) + } + } + + if fd.lifetime_elision_allowed + && let Return(ret_ty) = fd.output + && walk_unambig_ty(&mut V, ret_ty).is_break() + { + // The first encountered input lifetime will either be one on `self`, or will be the only lifetime. + fd.inputs + .iter() + .find_map(|ty| walk_unambig_ty(&mut V, ty).break_value()) + .unwrap() + } else { + false + } +} + fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>, generics: &'tcx Generics<'_>) { let mut checker = LifetimeChecker::::new(cx, generics); @@ -662,6 +726,7 @@ fn report_elidable_impl_lifetimes<'tcx>( Usage { lifetime, in_where_predicate: false, + lifetime_elision_impossible: false, .. }, ] = usages.as_slice() @@ -719,7 +784,7 @@ fn report_elidable_lifetimes( |diag| { if !include_suggestions { return; - }; + } if let Some(suggestions) = elision_suggestions(cx, generics, elidable_lts, usages) { diag.multipart_suggestion("elide the lifetimes", suggestions, Applicability::MachineApplicable); diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs index e2dcb20f906d3..a4cedf3bed354 100644 --- a/src/tools/clippy/clippy_lints/src/literal_representation.rs +++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs @@ -251,7 +251,7 @@ impl LiteralDigitGrouping { ); if !consistent { return Err(WarningType::InconsistentDigitGrouping); - }; + } } Ok(()) diff --git a/src/tools/clippy/clippy_lints/src/literal_string_with_formatting_args.rs b/src/tools/clippy/clippy_lints/src/literal_string_with_formatting_args.rs index 49353a1b76bec..a957c0e22a292 100644 --- a/src/tools/clippy/clippy_lints/src/literal_string_with_formatting_args.rs +++ b/src/tools/clippy/clippy_lints/src/literal_string_with_formatting_args.rs @@ -29,9 +29,9 @@ declare_clippy_lint! { /// let y = "hello"; /// x.expect(&format!("{y:?}")); /// ``` - #[clippy::version = "1.83.0"] + #[clippy::version = "1.85.0"] pub LITERAL_STRING_WITH_FORMATTING_ARGS, - suspicious, + nursery, "Checks if string literals have formatting arguments" } diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs index f3ca4a4a57152..c5e75af2303c6 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mod.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs @@ -830,7 +830,7 @@ impl Loops { for_kv_map::check(cx, pat, arg, body); mut_range_bound::check(cx, arg, body); single_element_loop::check(cx, pat, arg, body, expr); - same_item_push::check(cx, pat, arg, body, expr); + same_item_push::check(cx, pat, arg, body, expr, &self.msrv); manual_flatten::check(cx, pat, arg, body, span); manual_find::check(cx, pat, arg, body, span, expr); unused_enumerate_index::check(cx, pat, arg, body); diff --git a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs index 951ebc9caef04..c27e930c99a5e 100644 --- a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs +++ b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs @@ -1,8 +1,9 @@ use super::SAME_ITEM_PUSH; -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::path_to_local; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::Msrv; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; +use clippy_utils::{msrvs, path_to_local, std_or_core}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -19,19 +20,30 @@ pub(super) fn check<'tcx>( _: &'tcx Expr<'_>, body: &'tcx Expr<'_>, _: &'tcx Expr<'_>, + msrv: &Msrv, ) { - fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>, ctxt: SyntaxContext) { + fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>, ctxt: SyntaxContext, msrv: &Msrv) { let mut app = Applicability::Unspecified; let vec_str = snippet_with_context(cx, vec.span, ctxt, "", &mut app).0; let item_str = snippet_with_context(cx, pushed_item.span, ctxt, "", &mut app).0; - span_lint_and_help( + let secondary_help = if msrv.meets(msrvs::REPEAT_N) + && let Some(std_or_core) = std_or_core(cx) + { + format!("or `{vec_str}.extend({std_or_core}::iter::repeat_n({item_str}, SIZE))`") + } else { + format!("or `{vec_str}.resize(NEW_SIZE, {item_str})`") + }; + + span_lint_and_then( cx, SAME_ITEM_PUSH, vec.span, - "it looks like the same item is being pushed into this Vec", - None, - format!("consider using vec![{item_str};SIZE] or {vec_str}.resize(NEW_SIZE, {item_str})"), + "it looks like the same item is being pushed into this `Vec`", + |diag| { + diag.help(format!("consider using `vec![{item_str};SIZE]`")) + .help(secondary_help); + }, ); } @@ -67,11 +79,11 @@ pub(super) fn check<'tcx>( { match init.kind { // immutable bindings that are initialized with literal - ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt), + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt, msrv), // immutable bindings that are initialized with constant ExprKind::Path(ref path) => { if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) { - emit_lint(cx, vec, pushed_item, ctxt); + emit_lint(cx, vec, pushed_item, ctxt, msrv); } }, _ => {}, @@ -79,11 +91,11 @@ pub(super) fn check<'tcx>( } }, // constant - Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item, ctxt), + Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item, ctxt, msrv), _ => {}, } }, - ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt), + ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt, msrv), _ => {}, } } diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs index d2a2321dae805..17e25635ce105 100644 --- a/src/tools/clippy/clippy_lints/src/manual_bits.rs +++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs @@ -6,7 +6,7 @@ use clippy_utils::source::snippet_with_context; use rustc_ast::ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; +use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; @@ -99,7 +99,7 @@ fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option< && let QPath::Resolved(_, count_func_path) = count_func_qpath && let Some(segment_zero) = count_func_path.segments.first() && let Some(args) = segment_zero.args - && let Some(real_ty_span) = args.args.first().map(|arg| arg.span()) + && let Some(real_ty_span) = args.args.first().map(GenericArg::span) && let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id() && cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id) { diff --git a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs index aa59b047b169a..816ca17b3d2d5 100644 --- a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs +++ b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs @@ -102,12 +102,48 @@ impl<'tcx> LateLintPass<'tcx> for ManualDivCeil { { build_suggestion(cx, expr, add_lhs, div_rhs, &mut applicability); } + + // (x + (Y - 1)) / Y + if inner_op.node == BinOpKind::Add && differ_by_one(inner_rhs, div_rhs) { + build_suggestion(cx, expr, inner_lhs, div_rhs, &mut applicability); + } + + // ((Y - 1) + x) / Y + if inner_op.node == BinOpKind::Add && differ_by_one(inner_lhs, div_rhs) { + build_suggestion(cx, expr, inner_rhs, div_rhs, &mut applicability); + } + + // (x - (-Y - 1)) / Y + if inner_op.node == BinOpKind::Sub + && let ExprKind::Unary(UnOp::Neg, abs_div_rhs) = div_rhs.kind + && differ_by_one(abs_div_rhs, inner_rhs) + { + build_suggestion(cx, expr, inner_lhs, div_rhs, &mut applicability); + } } } extract_msrv_attr!(LateContext); } +/// Checks if two expressions represent non-zero integer literals such that `small_expr + 1 == +/// large_expr`. +fn differ_by_one(small_expr: &Expr<'_>, large_expr: &Expr<'_>) -> bool { + if let ExprKind::Lit(small) = small_expr.kind + && let ExprKind::Lit(large) = large_expr.kind + && let LitKind::Int(s, _) = small.node + && let LitKind::Int(l, _) = large.node + { + Some(l.get()) == s.get().checked_add(1) + } else if let ExprKind::Unary(UnOp::Neg, small_inner_expr) = small_expr.kind + && let ExprKind::Unary(UnOp::Neg, large_inner_expr) = large_expr.kind + { + differ_by_one(large_inner_expr, small_inner_expr) + } else { + false + } +} + fn check_int_ty_and_feature(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { let expr_ty = cx.typeck_results().expr_ty(expr); match expr_ty.peel_refs().kind() { diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs index 9860deba84324..38106277a88fe 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs @@ -7,7 +7,7 @@ use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operator use rustc_ast::LitKind::{Byte, Char}; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd, PatExpr, PatExprKind, Lit}; +use rustc_hir::{Expr, ExprKind, Lit, Node, Param, PatExpr, PatExprKind, PatKind, RangeEnd}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; @@ -202,8 +202,14 @@ fn check_expr_range(start: &Expr<'_>, end: &Expr<'_>) -> CharRange { } fn check_range(start: &PatExpr<'_>, end: &PatExpr<'_>) -> CharRange { - if let PatExprKind::Lit{ lit: start_lit, negated: false } = &start.kind - && let PatExprKind::Lit{ lit: end_lit, negated: false } = &end.kind + if let PatExprKind::Lit { + lit: start_lit, + negated: false, + } = &start.kind + && let PatExprKind::Lit { + lit: end_lit, + negated: false, + } = &end.kind { check_lit_range(start_lit, end_lit) } else { diff --git a/src/tools/clippy/clippy_lints/src/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs index a70955a7c78da..8503dde3fb6b4 100644 --- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs @@ -106,7 +106,7 @@ impl<'tcx> QuestionMark { emit_manual_let_else(cx, stmt.span, match_expr, &ident_map, pat_arm.pat, diverging_arm.body); }, } - }; + } } } @@ -295,7 +295,7 @@ fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: boo PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Path(..) ) { return; - }; + } let ty = typeck_results.pat_ty(pat); // Option and Result are allowed, everything else isn't. if !(is_type_diagnostic_item(cx, ty, sym::Option) || is_type_diagnostic_item(cx, ty, sym::Result)) { diff --git a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs index ebfd946b07ee0..8aeec89f0bfa7 100644 --- a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs +++ b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs @@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { } }, _ => return, - }; + } let mut app = Applicability::MachineApplicable; let rem_of = snippet_with_context(cx, rem2_lhs.span, ctxt, "_", &mut app).0; diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs index 79de41db3438b..d69384a2cb702 100644 --- a/src/tools/clippy/clippy_lints/src/manual_strip.rs +++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs @@ -86,7 +86,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip { let target_res = cx.qpath_res(target_path, target_arg.hir_id); if target_res == Res::Err { return; - }; + } if let Res::Local(hir_id) = target_res && let Some(used_mutably) = mutated_variables(then, cx) diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs b/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs index cfa054706d6bf..4cc43e427ec61 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs @@ -34,7 +34,7 @@ fn get_cond_expr<'tcx>( needs_negated: is_none_expr(cx, then_expr), /* if the `then_expr` resolves to `None`, need to negate the * cond */ }); - }; + } None } @@ -45,7 +45,7 @@ fn peels_blocks_incl_unsafe_opt<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> if block.stmts.is_empty() { return block.expr; } - }; + } None } @@ -68,14 +68,14 @@ fn is_some_expr(cx: &LateContext<'_>, target: HirId, ctxt: SyntaxContext, expr: && is_res_lang_ctor(cx, path_res(cx, callee), OptionSome) && path_to_local_id(arg, target); } - }; + } false } fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let Some(inner_expr) = peels_blocks_incl_unsafe_opt(expr) { return is_res_lang_ctor(cx, path_res(cx, inner_expr), OptionNone); - }; + } false } diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs b/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs new file mode 100644 index 0000000000000..b1a555b91d1bc --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs @@ -0,0 +1,135 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::option_arg_ty; +use clippy_utils::{is_res_lang_ctor, path_res, peel_blocks, span_contains_comment}; +use rustc_ast::BindingMode; +use rustc_errors::Applicability; +use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind, Path, QPath}; +use rustc_lint::{LateContext, LintContext}; +use rustc_middle::ty::Ty; +use rustc_span::symbol::Ident; + +use super::MANUAL_OK_ERR; + +pub(crate) fn check_if_let( + cx: &LateContext<'_>, + expr: &Expr<'_>, + let_pat: &Pat<'_>, + let_expr: &Expr<'_>, + if_then: &Expr<'_>, + else_expr: &Expr<'_>, +) { + if let Some(inner_expr_ty) = option_arg_ty(cx, cx.typeck_results().expr_ty(expr)) + && let Some((is_ok, ident)) = is_ok_or_err(cx, let_pat) + && is_some_ident(cx, if_then, ident, inner_expr_ty) + && is_none(cx, else_expr) + { + apply_lint(cx, expr, let_expr, is_ok); + } +} + +pub(crate) fn check_match(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]) { + if let Some(inner_expr_ty) = option_arg_ty(cx, cx.typeck_results().expr_ty(expr)) + && arms.len() == 2 + && arms.iter().all(|arm| arm.guard.is_none()) + && let Some((idx, is_ok)) = arms.iter().enumerate().find_map(|(arm_idx, arm)| { + // Check if the arm is a `Ok(x) => x` or `Err(x) => x` alternative. + // In this case, return its index and whether it uses `Ok` or `Err`. + if let Some((is_ok, ident)) = is_ok_or_err(cx, arm.pat) + && is_some_ident(cx, arm.body, ident, inner_expr_ty) + { + Some((arm_idx, is_ok)) + } else { + None + } + }) + // Accept wildcard only as the second arm + && is_variant_or_wildcard(cx, arms[1-idx].pat, idx == 0, is_ok) + // Check that the body of the non `Ok`/`Err` arm is `None` + && is_none(cx, arms[1 - idx].body) + { + apply_lint(cx, expr, scrutinee, is_ok); + } +} + +/// Check that `pat` applied to a `Result` only matches `Ok(_)`, `Err(_)`, not a subset or a +/// superset of it. If `can_be_wild` is `true`, wildcards are also accepted. In the case of +/// a non-wildcard, `must_match_err` indicates whether the `Err` or the `Ok` variant should be +/// accepted. +fn is_variant_or_wildcard(cx: &LateContext<'_>, pat: &Pat<'_>, can_be_wild: bool, must_match_err: bool) -> bool { + match pat.kind { + PatKind::Wild | PatKind::Path(..) | PatKind::Binding(_, _, _, None) if can_be_wild => true, + PatKind::TupleStruct(qpath, ..) => { + is_res_lang_ctor(cx, cx.qpath_res(&qpath, pat.hir_id), ResultErr) == must_match_err + }, + PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) => { + is_variant_or_wildcard(cx, pat, can_be_wild, must_match_err) + }, + _ => false, + } +} + +/// Return `Some((true, IDENT))` if `pat` contains `Ok(IDENT)`, `Some((false, IDENT))` if it +/// 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 res = cx.qpath_res(qpath, pat.hir_id) + && let Res::Def(DefKind::Ctor(..), id) = res + && let id @ Some(_) = cx.tcx.opt_parent(id) + { + let lang_items = cx.tcx.lang_items(); + if id == lang_items.result_ok_variant() { + return Some((true, ident)); + } else if id == lang_items.result_err_variant() { + return Some((false, ident)); + } + } + None +} + +/// Check if `expr` contains `Some(ident)`, possibly as a block +fn is_some_ident<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, ident: &Ident, ty: Ty<'tcx>) -> bool { + if let ExprKind::Call(body_callee, [body_arg]) = peel_blocks(expr).kind + && is_res_lang_ctor(cx, path_res(cx, body_callee), OptionSome) + && cx.typeck_results().expr_ty(body_arg) == ty + && let ExprKind::Path(QPath::Resolved( + _, + Path { + segments: [segment], .. + }, + )) = body_arg.kind + { + segment.ident.name == ident.name + } else { + false + } +} + +/// Check if `expr` is `None`, possibly as a block +fn is_none(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + is_res_lang_ctor(cx, path_res(cx, peel_blocks(expr)), OptionNone) +} + +/// Suggest replacing `expr` by `scrutinee.METHOD()`, where `METHOD` is either `ok` or +/// `err`, depending on `is_ok`. +fn apply_lint(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Expr<'_>, is_ok: bool) { + let method = if is_ok { "ok" } else { "err" }; + let mut app = if span_contains_comment(cx.sess().source_map(), expr.span) { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; + let scrut = Sugg::hir_with_applicability(cx, scrutinee, "..", &mut app).maybe_par(); + span_lint_and_sugg( + cx, + MANUAL_OK_ERR, + expr.span, + format!("manual implementation of `{method}`"), + "replace with", + format!("{scrut}.{method}()"), + app, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs index bac5cf88cfbf8..0b57740064c19 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs @@ -109,7 +109,7 @@ where } }, None => return None, - }; + } let mut app = Applicability::MachineApplicable; diff --git a/src/tools/clippy/clippy_lints/src/matches/match_bool.rs b/src/tools/clippy/clippy_lints/src/matches/match_bool.rs index 7e43d222a6623..b90cf6357c5c0 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_bool.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_bool.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_unit_expr; -use clippy_utils::source::{expr_block, snippet}; +use clippy_utils::source::expr_block; use clippy_utils::sugg::Sugg; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -17,17 +17,28 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>] cx, MATCH_BOOL, expr.span, - "you seem to be trying to match on a boolean expression", + "`match` on a boolean expression", move |diag| { if arms.len() == 2 { - // no guards - let exprs = if let PatKind::Expr(arm_bool) = arms[0].pat.kind { + let mut app = Applicability::MachineApplicable; + let test_sugg = if let PatKind::Expr(arm_bool) = arms[0].pat.kind { + let test = Sugg::hir_with_applicability(cx, scrutinee, "_", &mut app); if let PatExprKind::Lit { lit, .. } = arm_bool.kind { - match lit.node { - LitKind::Bool(true) => Some((arms[0].body, arms[1].body)), - LitKind::Bool(false) => Some((arms[1].body, arms[0].body)), + match &lit.node { + LitKind::Bool(true) => Some(test), + LitKind::Bool(false) => Some(!test), _ => None, } + .map(|test| { + if let Some(guard) = &arms[0] + .guard + .map(|g| Sugg::hir_with_applicability(cx, g, "_", &mut app)) + { + test.and(guard) + } else { + test + } + }) } else { None } @@ -35,39 +46,31 @@ pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>] None }; - if let Some((true_expr, false_expr)) = exprs { - let mut app = Applicability::HasPlaceholders; + if let Some(test_sugg) = test_sugg { let ctxt = expr.span.ctxt(); + let (true_expr, false_expr) = (arms[0].body, arms[1].body); let sugg = match (is_unit_expr(true_expr), is_unit_expr(false_expr)) { (false, false) => Some(format!( "if {} {} else {}", - snippet(cx, scrutinee.span, "b"), + test_sugg, expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app), expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app) )), (false, true) => Some(format!( "if {} {}", - snippet(cx, scrutinee.span, "b"), + test_sugg, expr_block(cx, true_expr, ctxt, "..", Some(expr.span), &mut app) )), - (true, false) => { - let test = Sugg::hir(cx, scrutinee, ".."); - Some(format!( - "if {} {}", - !test, - expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app) - )) - }, + (true, false) => Some(format!( + "if {} {}", + !test_sugg, + expr_block(cx, false_expr, ctxt, "..", Some(expr.span), &mut app) + )), (true, true) => None, }; if let Some(sugg) = sugg { - diag.span_suggestion( - expr.span, - "consider using an `if`/`else` expression", - sugg, - Applicability::HasPlaceholders, - ); + diag.span_suggestion(expr.span, "consider using an `if`/`else` expression", sugg, app); } } } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs index 223d0dc76569b..d697f427c7052 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs @@ -117,7 +117,7 @@ where if let ty::Ref(..) = cx.typeck_results().expr_ty(ex_inner).kind() { ex_new = ex_inner; } - }; + } span_lint_and_sugg( cx, MATCH_LIKE_MATCHES_MACRO, diff --git a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs index 9f5b7c855a139..df1b83cbb516a 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -5,7 +5,7 @@ use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr}; -use rustc_hir::{Arm, Expr, ExprKind, PatExpr, PatExprKind, LangItem, PatKind}; +use rustc_hir::{Arm, Expr, ExprKind, LangItem, PatExpr, PatExprKind, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::Span; diff --git a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs index 91e40e4275c07..595655600890b 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs @@ -170,7 +170,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { ); }); }, - }; + } } enum CommonPrefixSearcher<'a> { diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index ac1eae07eff63..a7fdd483c16cd 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -2,6 +2,7 @@ mod collapsible_match; mod infallible_destructuring_match; mod manual_filter; mod manual_map; +mod manual_ok_err; mod manual_unwrap_or; mod manual_utils; mod match_as_ref; @@ -972,6 +973,40 @@ declare_clippy_lint! { "checks for unnecessary guards in match expressions" } +declare_clippy_lint! { + /// ### What it does + /// Checks for manual implementation of `.ok()` or `.err()` + /// on `Result` values. + /// + /// ### Why is this bad? + /// Using `.ok()` or `.err()` rather than a `match` or + /// `if let` is less complex and more readable. + /// + /// ### Example + /// ```no_run + /// # fn func() -> Result { Ok(0) } + /// let a = match func() { + /// Ok(v) => Some(v), + /// Err(_) => None, + /// }; + /// let b = if let Err(v) = func() { + /// Some(v) + /// } else { + /// None + /// }; + /// ``` + /// Use instead: + /// ```no_run + /// # fn func() -> Result { Ok(0) } + /// let a = func().ok(); + /// let b = func().err(); + /// ``` + #[clippy::version = "1.86.0"] + pub MANUAL_OK_ERR, + complexity, + "find manual implementations of `.ok()` or `.err()` on `Result`" +} + pub struct Matches { msrv: Msrv, infallible_destructuring_match_linted: bool, @@ -1013,6 +1048,7 @@ impl_lint_pass!(Matches => [ MANUAL_MAP, MANUAL_FILTER, REDUNDANT_GUARDS, + MANUAL_OK_ERR, ]); impl<'tcx> LateLintPass<'tcx> for Matches { @@ -1091,6 +1127,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { manual_unwrap_or::check_match(cx, expr, ex, arms); manual_map::check_match(cx, expr, ex, arms); manual_filter::check_match(cx, ex, arms, expr); + manual_ok_err::check_match(cx, expr, ex, arms); } if self.infallible_destructuring_match_linted { @@ -1134,6 +1171,14 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if_let.if_then, else_expr, ); + manual_ok_err::check_if_let( + cx, + expr, + if_let.let_pat, + if_let.let_expr, + if_let.if_then, + else_expr, + ); } } redundant_pattern_match::check_if_let( diff --git a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 2ce6a8a85a5eb..35f2e780d2e29 100644 --- a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -2,7 +2,7 @@ use std::ops::ControlFlow; use crate::FxHashSet; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{indent_of, snippet}; +use clippy_utils::source::{first_line_of_span, indent_of, snippet}; use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy}; use clippy_utils::{get_attr, is_lint_allowed}; use itertools::Itertools; @@ -152,7 +152,7 @@ fn set_suggestion<'tcx>(diag: &mut Diag<'_, ()>, cx: &LateContext<'tcx>, expr: & diag.multipart_suggestion( suggestion_message, vec![ - (expr.span.shrink_to_lo(), replacement), + (first_line_of_span(cx, expr.span).shrink_to_lo(), replacement), (found.found_span, scrutinee_replacement), ], Applicability::MaybeIncorrect, @@ -441,8 +441,9 @@ impl<'tcx> Visitor<'tcx> for SigDropHelper<'_, 'tcx> { let parent_expr_before = self.parent_expr.replace(ex); match ex.kind { - // Skip blocks because values in blocks will be dropped as usual. - ExprKind::Block(..) => (), + // Skip blocks because values in blocks will be dropped as usual, and await + // desugaring because temporary insides the future will have been dropped. + ExprKind::Block(..) | ExprKind::Match(_, _, MatchSource::AwaitDesugar) => (), _ => walk_expr(self, ex), } diff --git a/src/tools/clippy/clippy_lints/src/matches/try_err.rs b/src/tools/clippy/clippy_lints/src/matches/try_err.rs index 6c02207af49df..ff7769af1df41 100644 --- a/src/tools/clippy/clippy_lints/src/matches/try_err.rs +++ b/src/tools/clippy/clippy_lints/src/matches/try_err.rs @@ -46,7 +46,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine err_ty = ty; } else { return; - }; + } span_lint_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/matches/wild_in_or_pats.rs b/src/tools/clippy/clippy_lints/src/matches/wild_in_or_pats.rs index 390ba889fd2e6..b75d1ab9a7aa3 100644 --- a/src/tools/clippy/clippy_lints/src/matches/wild_in_or_pats.rs +++ b/src/tools/clippy/clippy_lints/src/matches/wild_in_or_pats.rs @@ -13,7 +13,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arms: &[Arm<'_>]) { && has_non_exhaustive_attr(cx.tcx, *adt_def) { return; - }; + } for arm in arms { if let PatKind::Or(fields) = arm.pat.kind { // look for multiple fields in this arm that contains at least one Wild pattern diff --git a/src/tools/clippy/clippy_lints/src/methods/bytecount.rs b/src/tools/clippy/clippy_lints/src/methods/bytecount.rs index 4a2124c74a882..687272e550bb0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bytecount.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bytecount.rs @@ -62,5 +62,5 @@ pub(super) fn check<'tcx>( ), applicability, ); - }; + } } diff --git a/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs b/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs index 34159f2d150ea..a9f6a41c2357d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bytes_count_to_len.rs @@ -32,5 +32,5 @@ pub(super) fn check<'tcx>( ), applicability, ); - }; + } } diff --git a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs index a82abc79f2a24..de22514c37c66 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bytes_nth.rs @@ -46,5 +46,5 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E format!("{receiver}.as_bytes().get({n}).copied()"), applicability, ); - }; + } } diff --git a/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs b/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs index 2a0a9d3710dca..223a960b800e7 100644 --- a/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs +++ b/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs @@ -32,7 +32,7 @@ pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, // &T where T: Copy ty::Ref(_, ty, _) if is_copy(cx, *ty) => {}, _ => return, - }; + } span_lint_and_sugg( cx, CLONED_INSTEAD_OF_COPIED, diff --git a/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs index 10360b4817bc9..cbf713a3b17c4 100644 --- a/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs @@ -1,8 +1,8 @@ use crate::methods::DRAIN_COLLECT; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::is_range_full; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_lang_item; +use clippy_utils::{is_range_full, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, Path, QPath}; use rustc_lint::LateContext; @@ -58,12 +58,13 @@ pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], expr: &Expr<'_>, re .then_some("Vec") .or_else(|| check_string(cx, args, expr_ty, recv_ty_no_refs, recv_path).then_some("String")) .or_else(|| check_collections(cx, expr_ty, recv_ty_no_refs)) + && let Some(exec_context) = std_or_core(cx) { let recv = snippet(cx, recv.span, ""); let sugg = if let ty::Ref(..) = recv_ty.kind() { - format!("std::mem::take({recv})") + format!("{exec_context}::mem::take({recv})") } else { - format!("std::mem::take(&mut {recv})") + format!("{exec_context}::mem::take(&mut {recv})") }; span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/err_expect.rs b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs index 44b55570eead2..f2786efa44cb6 100644 --- a/src/tools/clippy/clippy_lints/src/methods/err_expect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs @@ -37,7 +37,7 @@ pub(super) fn check( "expect_err".to_string(), Applicability::MachineApplicable, ); - }; + } } /// Given a `Result` type, return its data (`T`). diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs index 6dc48c26ba93b..daa6e0e7f940c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs @@ -58,7 +58,7 @@ pub(super) fn check<'tcx>( if ty.is_str() && can_be_static_str(cx, arg) { return false; } - }; + } true } diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs index 163058713377b..aa45969c8982b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs @@ -25,5 +25,5 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span "into_iter()".to_string(), Applicability::MaybeIncorrect, ); - }; + } } diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_repeat_n.rs b/src/tools/clippy/clippy_lints/src/methods/manual_repeat_n.rs new file mode 100644 index 0000000000000..6e09bf132aa14 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/manual_repeat_n.rs @@ -0,0 +1,43 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source::{snippet, snippet_with_context}; +use clippy_utils::{expr_use_ctxt, fn_def_id, is_trait_method, std_or_core}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::MANUAL_REPEAT_N; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, + repeat_expr: &Expr<'_>, + take_arg: &Expr<'_>, + msrv: &Msrv, +) { + if msrv.meets(msrvs::REPEAT_N) + && !expr.span.from_expansion() + && is_trait_method(cx, expr, sym::Iterator) + && let ExprKind::Call(_, [repeat_arg]) = repeat_expr.kind + && let Some(def_id) = fn_def_id(cx, repeat_expr) + && cx.tcx.is_diagnostic_item(sym::iter_repeat, def_id) + && !expr_use_ctxt(cx, expr).is_ty_unified + && let Some(std_or_core) = std_or_core(cx) + { + let mut app = Applicability::MachineApplicable; + span_lint_and_sugg( + cx, + MANUAL_REPEAT_N, + expr.span, + "this `repeat().take()` can be written more concisely", + "consider using `repeat_n()` instead", + format!( + "{std_or_core}::iter::repeat_n({}, {})", + snippet_with_context(cx, repeat_arg.span, expr.span.ctxt(), "..", &mut app).0, + snippet(cx, take_arg.span, "..") + ), + app, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs b/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs index 1ebb71e251aba..78656ace831d5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; -use clippy_utils::{eager_or_lazy, higher, usage}; +use clippy_utils::{eager_or_lazy, higher, std_or_core, usage}; use rustc_ast::LitKind; use rustc_ast::ast::RangeLimits; use rustc_data_structures::packed::Pu128; @@ -75,6 +75,7 @@ pub(super) fn check( } = body_hir && !usage::BindingUsageFinder::are_params_used(cx, body_hir) && let Some(count) = extract_count_with_applicability(cx, range, &mut applicability) + && let Some(exec_context) = std_or_core(cx) { let method_to_use_name; let new_span; @@ -105,7 +106,7 @@ pub(super) fn check( let mut parts = vec![ ( receiver.span.to(method_call_span), - format!("std::iter::{method_to_use_name}"), + format!("{exec_context}::iter::{method_to_use_name}"), ), new_span, ]; diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index 3965c4d40878d..42418318fda8d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -58,6 +58,7 @@ mod manual_inspect; mod manual_is_variant_and; mod manual_next_back; mod manual_ok_or; +mod manual_repeat_n; mod manual_saturating_arithmetic; mod manual_str_repeat; mod manual_try_fold; @@ -101,6 +102,7 @@ mod single_char_add_str; mod single_char_insert_string; mod single_char_push_string; mod skip_while_next; +mod sliced_string_as_bytes; mod stable_sort_primitive; mod str_split; mod str_splitn; @@ -130,6 +132,7 @@ mod unnecessary_to_owned; mod unused_enumerate_index; mod unwrap_expect_used; mod useless_asref; +mod useless_nonzero_new_unchecked; mod utils; mod vec_resize_to_zero; mod verbose_file_reads; @@ -2416,14 +2419,14 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for usage of `.then_some(..).unwrap_or(..)` + /// Checks for unnecessary method chains that can be simplified into `if .. else ..`. /// /// ### Why is this bad? /// This can be written more clearly with `if .. else ..` /// /// ### Limitations /// This lint currently only looks for usages of - /// `.then_some(..).unwrap_or(..)`, but will be expanded + /// `.then_some(..).unwrap_or(..)` and `.then(..).unwrap_or(..)`, but will be expanded /// to account for similar patterns. /// /// ### Example @@ -4311,6 +4314,84 @@ declare_clippy_lint! { "using `Iterator::last` on a `DoubleEndedIterator`" } +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for `NonZero*::new_unchecked()` being used in a `const` context. + /// + /// ### Why is this bad? + /// + /// Using `NonZero*::new_unchecked()` is an `unsafe` function and requires an `unsafe` context. When used in a + /// context evaluated at compilation time, `NonZero*::new().unwrap()` will provide the same result with identical + /// runtime performances while not requiring `unsafe`. + /// + /// ### Example + /// ```no_run + /// use std::num::NonZeroUsize; + /// const PLAYERS: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(3) }; + /// ``` + /// Use instead: + /// ```no_run + /// use std::num::NonZeroUsize; + /// const PLAYERS: NonZeroUsize = NonZeroUsize::new(3).unwrap(); + /// ``` + #[clippy::version = "1.86.0"] + pub USELESS_NONZERO_NEW_UNCHECKED, + complexity, + "using `NonZero::new_unchecked()` in a `const` context" +} + +declare_clippy_lint! { + /// ### What it does + /// + /// Checks for `repeat().take()` that can be replaced with `repeat_n()`. + /// + /// ### Why is this bad? + /// + /// Using `repeat_n()` is more concise and clearer. Also, `repeat_n()` is sometimes faster than `repeat().take()` when the type of the element is non-trivial to clone because the original value can be reused for the last `.next()` call rather than always cloning. + /// + /// ### Example + /// ```no_run + /// let _ = std::iter::repeat(10).take(3); + /// ``` + /// Use instead: + /// ```no_run + /// let _ = std::iter::repeat_n(10, 3); + /// ``` + #[clippy::version = "1.86.0"] + pub MANUAL_REPEAT_N, + style, + "detect `repeat().take()` that can be replaced with `repeat_n()`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for string slices immediantly followed by `as_bytes`. + /// + /// ### Why is this bad? + /// It involves doing an unnecessary UTF-8 alignment check which is less efficient, and can cause a panic. + /// + /// ### Known problems + /// In some cases, the UTF-8 validation and potential panic from string slicing may be required for + /// the code's correctness. If you need to ensure the slice boundaries fall on valid UTF-8 character + /// boundaries, the original form (`s[1..5].as_bytes()`) should be preferred. + /// + /// ### Example + /// ```rust + /// let s = "Lorem ipsum"; + /// s[1..5].as_bytes(); + /// ``` + /// Use instead: + /// ```rust + /// let s = "Lorem ipsum"; + /// &s.as_bytes()[1..5]; + /// ``` + #[clippy::version = "1.86.0"] + pub SLICED_STRING_AS_BYTES, + perf, + "slicing a string and immediately calling as_bytes is less efficient and can lead to panics" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -4477,6 +4558,9 @@ impl_lint_pass!(Methods => [ MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES, UNNECESSARY_MAP_OR, DOUBLE_ENDED_ITERATOR_LAST, + USELESS_NONZERO_NEW_UNCHECKED, + MANUAL_REPEAT_N, + SLICED_STRING_AS_BYTES, ]); /// Extracts a method call name, args, and `Span` of the method name. @@ -4505,6 +4589,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { from_iter_instead_of_collect::check(cx, expr, args, func); unnecessary_fallible_conversions::check_function(cx, expr, func); manual_c_str_literals::check(cx, expr, func, args, &self.msrv); + useless_nonzero_new_unchecked::check(cx, expr, func, args, &self.msrv); }, ExprKind::MethodCall(method_call, receiver, args, _) => { let method_span = method_call.ident.span; @@ -4743,6 +4828,7 @@ impl Methods { if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) { redundant_as_str::check(cx, expr, recv, as_str_span, span); } + sliced_string_as_bytes::check(cx, expr, recv); }, ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), ("as_ptr", []) => manual_c_str_literals::check_as_ptr(cx, expr, recv, &self.msrv), @@ -4924,8 +5010,8 @@ impl Methods { }, ("is_empty", []) => { match method_call(recv) { - Some(("as_bytes", prev_recv, [], _, _)) => { - needless_as_bytes::check(cx, "is_empty", recv, prev_recv, expr.span); + Some((prev_method @ ("as_bytes" | "bytes"), prev_recv, [], _, _)) => { + needless_as_bytes::check(cx, prev_method, "is_empty", prev_recv, expr.span); }, Some(("as_str", recv, [], as_str_span, _)) => { redundant_as_str::check(cx, expr, recv, as_str_span, span); @@ -4962,8 +5048,8 @@ impl Methods { double_ended_iterator_last::check(cx, expr, recv, call_span); }, ("len", []) => { - if let Some(("as_bytes", prev_recv, [], _, _)) = method_call(recv) { - needless_as_bytes::check(cx, "len", recv, prev_recv, expr.span); + if let Some((prev_method @ ("as_bytes" | "bytes"), prev_recv, [], _, _)) = method_call(recv) { + needless_as_bytes::check(cx, prev_method, "len", prev_recv, expr.span); } }, ("lock", []) => { @@ -5015,7 +5101,7 @@ impl Methods { option_map_or_none::check(cx, expr, recv, def, map); manual_ok_or::check(cx, expr, recv, def, map); option_map_or_err_ok::check(cx, expr, recv, def, map); - unnecessary_map_or::check(cx, expr, recv, def, map, &self.msrv); + unnecessary_map_or::check(cx, expr, recv, def, map, span, &self.msrv); }, ("map_or_else", [def, map]) => { result_map_or_else_none::check(cx, expr, recv, def, map); @@ -5146,6 +5232,7 @@ impl Methods { ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), ("take", [arg]) => { iter_out_of_bounds::check_take(cx, expr, recv, arg); + manual_repeat_n::check(cx, expr, recv, arg, &self.msrv); if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) { iter_overeager_cloned::check( cx, @@ -5220,8 +5307,8 @@ impl Methods { Some(("map", m_recv, [m_arg], span, _)) => { option_map_unwrap_or::check(cx, expr, m_recv, m_arg, recv, u_arg, span, &self.msrv); }, - Some(("then_some", t_recv, [t_arg], _, _)) => { - obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg); + Some((then_method @ ("then" | "then_some"), t_recv, [t_arg], _, _)) => { + obfuscated_if_else::check(cx, expr, t_recv, t_arg, u_arg, then_method); }, _ => {}, } diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs b/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs index 75e9f31723039..7c9f7bae99063 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs @@ -8,18 +8,16 @@ use rustc_span::Span; use super::NEEDLESS_AS_BYTES; -pub fn check(cx: &LateContext<'_>, method: &str, recv: &Expr<'_>, prev_recv: &Expr<'_>, span: Span) { - if cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice() - && let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs() - && (is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str()) - { +pub fn check(cx: &LateContext<'_>, prev_method: &str, method: &str, prev_recv: &Expr<'_>, span: Span) { + let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs(); + if is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str() { let mut app = Applicability::MachineApplicable; let sugg = Sugg::hir_with_context(cx, prev_recv, span.ctxt(), "..", &mut app); span_lint_and_sugg( cx, NEEDLESS_AS_BYTES, span, - "needless call to `as_bytes()`", + format!("needless call to `{prev_method}`"), format!("`{method}()` can be called directly on strings"), format!("{sugg}.{method}()"), app, diff --git a/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs b/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs index 697eab32a33b0..b71f79f848242 100644 --- a/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs +++ b/src/tools/clippy/clippy_lints/src/methods/obfuscated_if_else.rs @@ -1,8 +1,11 @@ use super::OBFUSCATED_IF_ELSE; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; use rustc_hir as hir; +use rustc_hir::ExprKind; use rustc_lint::LateContext; pub(super) fn check<'tcx>( @@ -11,19 +14,30 @@ pub(super) fn check<'tcx>( then_recv: &'tcx hir::Expr<'_>, then_arg: &'tcx hir::Expr<'_>, unwrap_arg: &'tcx hir::Expr<'_>, + then_method_name: &str, ) { - // something.then_some(blah).unwrap_or(blah) - // ^^^^^^^^^-then_recv ^^^^-then_arg ^^^^- unwrap_arg - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- expr - let recv_ty = cx.typeck_results().expr_ty(then_recv); if recv_ty.is_bool() { - let mut applicability = Applicability::MachineApplicable; + let mut applicability = if switch_to_eager_eval(cx, then_arg) && switch_to_eager_eval(cx, unwrap_arg) { + Applicability::MachineApplicable + } else { + Applicability::MaybeIncorrect + }; + + let if_then = match then_method_name { + "then" if let ExprKind::Closure(closure) = then_arg.kind => { + let body = cx.tcx.hir().body(closure.body); + snippet_with_applicability(cx, body.value.span, "..", &mut applicability) + }, + "then_some" => snippet_with_applicability(cx, then_arg.span, "..", &mut applicability), + _ => String::new().into(), + }; + let sugg = format!( "if {} {{ {} }} else {{ {} }}", - snippet_with_applicability(cx, then_recv.span, "..", &mut applicability), - snippet_with_applicability(cx, then_arg.span, "..", &mut applicability), + Sugg::hir_with_applicability(cx, then_recv, "..", &mut applicability), + if_then, snippet_with_applicability(cx, unwrap_arg.span, "..", &mut applicability) ); @@ -31,8 +45,7 @@ pub(super) fn check<'tcx>( cx, OBFUSCATED_IF_ELSE, expr.span, - "use of `.then_some(..).unwrap_or(..)` can be written \ - more clearly with `if .. else ..`", + "this method chain can be written more clearly with `if .. else ..`", "try", sugg, applicability, diff --git a/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs b/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs index febd7fd5cf2f4..b3811a335e1a0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs +++ b/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs @@ -37,7 +37,7 @@ pub(super) fn check( let _ = write!(sugg, r#".extension().is_some_and(|ext| ext == "{path}")"#); } else { let _ = write!(sugg, r#".extension().map_or(false, |ext| ext == "{path}")"#); - }; + } span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/sliced_string_as_bytes.rs b/src/tools/clippy/clippy_lints/src/methods/sliced_string_as_bytes.rs new file mode 100644 index 0000000000000..6d4cfdb34f319 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/sliced_string_as_bytes.rs @@ -0,0 +1,29 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_lang_item; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, LangItem, is_range_literal}; +use rustc_lint::LateContext; + +use super::SLICED_STRING_AS_BYTES; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>) { + if let ExprKind::Index(indexed, index, _) = recv.kind + && is_range_literal(index) + && let ty = cx.typeck_results().expr_ty(indexed).peel_refs() + && (ty.is_str() || is_type_lang_item(cx, ty, LangItem::String)) + { + let mut applicability = Applicability::MaybeIncorrect; + let stringish = snippet_with_applicability(cx, indexed.span, "_", &mut applicability); + let range = snippet_with_applicability(cx, index.span, "_", &mut applicability); + span_lint_and_sugg( + cx, + SLICED_STRING_AS_BYTES, + expr.span, + "calling `as_bytes` after slicing a string", + "try", + format!("&{stringish}.as_bytes()[{range}]"), + applicability, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 671c189a98e66..c0e0156858811 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -87,7 +87,7 @@ pub fn check_for_loop_iter( // skip lint return true; } - }; + } // the lint should not be executed if no violation happens let snippet = if let ExprKind::MethodCall(maybe_iter_method_name, collection, [], _) = receiver.kind diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs index b7dbebe60a42e..6dea1506d0e32 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs @@ -1,9 +1,8 @@ use std::borrow::Cow; -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::snippet_opt; use clippy_utils::sugg::{Sugg, make_binop}; use clippy_utils::ty::{get_type_diagnostic_name, implements_trait}; use clippy_utils::visitors::is_local_used; @@ -12,7 +11,7 @@ use rustc_ast::LitKind::Bool; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind}; use rustc_lint::LateContext; -use rustc_span::sym; +use rustc_span::{Span, sym}; use super::UNNECESSARY_MAP_OR; @@ -42,13 +41,14 @@ pub(super) fn check<'a>( recv: &Expr<'_>, def: &Expr<'_>, map: &Expr<'_>, + method_span: Span, msrv: &Msrv, ) { let ExprKind::Lit(def_kind) = def.kind else { return; }; - let recv_ty = cx.typeck_results().expr_ty(recv); + let recv_ty = cx.typeck_results().expr_ty_adjusted(recv); let Bool(def_bool) = def_kind.node else { return; @@ -60,6 +60,8 @@ pub(super) fn check<'a>( Some(_) | None => return, }; + let ext_def_span = def.span.until(map.span); + let (sugg, method, applicability) = if let ExprKind::Closure(map_closure) = map.kind && let closure_body = cx.tcx.hir().body(map_closure.body) && let closure_body_value = closure_body.value.peel_blocks() @@ -114,26 +116,17 @@ pub(super) fn check<'a>( } .into_string(); - (sugg, "a standard comparison", app) - } else if !def_bool - && msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND) - && let Some(recv_callsite) = snippet_opt(cx, recv.span.source_callsite()) - && let Some(span_callsite) = snippet_opt(cx, map.span.source_callsite()) - { + (vec![(expr.span, sugg)], "a standard comparison", app) + } else if !def_bool && msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND) { let suggested_name = variant.method_name(); ( - format!("{recv_callsite}.{suggested_name}({span_callsite})",), + vec![(method_span, suggested_name.into()), (ext_def_span, String::default())], suggested_name, Applicability::MachineApplicable, ) - } else if def_bool - && matches!(variant, Variant::Some) - && msrv.meets(msrvs::IS_NONE_OR) - && let Some(recv_callsite) = snippet_opt(cx, recv.span.source_callsite()) - && let Some(span_callsite) = snippet_opt(cx, map.span.source_callsite()) - { + } else if def_bool && matches!(variant, Variant::Some) && msrv.meets(msrvs::IS_NONE_OR) { ( - format!("{recv_callsite}.is_none_or({span_callsite})"), + vec![(method_span, "is_none_or".into()), (ext_def_span, String::default())], "is_none_or", Applicability::MachineApplicable, ) @@ -145,13 +138,13 @@ pub(super) fn check<'a>( return; } - span_lint_and_sugg( + span_lint_and_then( cx, UNNECESSARY_MAP_OR, expr.span, "this `map_or` can be simplified", - format!("use {method} instead"), - sugg, - applicability, + |diag| { + diag.multipart_suggestion_verbose(format!("use {method} instead"), sugg, applicability); + }, ); } diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs index 9a45b04d1a624..f0b29213e1e58 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -43,8 +43,7 @@ fn mirrored_exprs(a_expr: &Expr<'_>, a_ident: &Ident, b_expr: &Expr<'_>, b_ident && iter::zip(*left_args, *right_args).all(|(left, right)| mirrored_exprs(left, a_ident, right, b_ident)) }, // The two exprs are method calls. - // Check to see that the function is the same and the arguments are mirrored - // This is enough because the receiver of the method is listed in the arguments + // Check to see that the function is the same and the arguments and receivers are mirrored ( ExprKind::MethodCall(left_segment, left_receiver, left_args, _), ExprKind::MethodCall(right_segment, right_receiver, right_args, _), diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index 964f1603f0e51..7d72310c1c442 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -217,10 +217,13 @@ fn check_into_iter_call_arg( && implements_trait(cx, parent_ty, iterator_trait_id, &[]) && let Some(item_ty) = get_iterator_item_ty(cx, parent_ty) && let Some(receiver_snippet) = receiver.span.get_source_text(cx) + // If the receiver is a `Cow`, we can't remove the `into_owned` generally, see https://github.com/rust-lang/rust-clippy/issues/13624. + && !is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(receiver), sym::Cow) { if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) { return true; } + let cloned_or_copied = if is_copy(cx, item_ty) && msrv.meets(msrvs::ITERATOR_COPIED) { "copied" } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs b/src/tools/clippy/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs new file mode 100644 index 0000000000000..0bd50429c09db --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/useless_nonzero_new_unchecked.rs @@ -0,0 +1,59 @@ +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::is_inside_always_const_context; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::ty::is_type_diagnostic_item; +use rustc_errors::Applicability; +use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, Node, QPath, UnsafeSource}; +use rustc_lint::LateContext; +use rustc_span::sym; + +use super::USELESS_NONZERO_NEW_UNCHECKED; + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, func: &Expr<'tcx>, args: &[Expr<'_>], msrv: &Msrv) { + if msrv.meets(msrvs::CONST_UNWRAP) + && let ExprKind::Path(QPath::TypeRelative(ty, segment)) = func.kind + && segment.ident.name == sym::new_unchecked + && let [init_arg] = args + && is_inside_always_const_context(cx.tcx, expr.hir_id) + && is_type_diagnostic_item(cx, cx.typeck_results().node_type(ty.hir_id), sym::NonZero) + { + let mut app = Applicability::MachineApplicable; + let ty_str = snippet_with_applicability(cx, ty.span, "_", &mut app); + let msg = format!("`{ty_str}::new()` and `Option::unwrap()` can be safely used in a `const` context"); + let sugg = format!( + "{ty_str}::new({}).unwrap()", + snippet_with_applicability(cx, init_arg.span, "_", &mut app) + ); + + if let Node::Block(Block { + stmts: [], + span: block_span, + rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), + .. + }) = cx.tcx.parent_hir_node(expr.hir_id) + { + if !block_span.from_expansion() { + // The expression is the only component of an `unsafe` block. Propose + // to replace the block altogether. + span_lint_and_sugg( + cx, + USELESS_NONZERO_NEW_UNCHECKED, + *block_span, + msg, + "use instead", + sugg, + app, + ); + } + } else { + // The expression is enclosed in a larger `unsafe` context. Indicate that + // this may no longer be needed for the fixed expression. + span_lint_and_then(cx, USELESS_NONZERO_NEW_UNCHECKED, expr.span, msg, |diagnostic| { + diagnostic + .span_suggestion(expr.span, "use instead", sugg, app) + .note("the fixed expression does not require an `unsafe` context"); + }); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs index b856c929cf67b..b511b1e46b386 100644 --- a/src/tools/clippy/clippy_lints/src/misc.rs +++ b/src/tools/clippy/clippy_lints/src/misc.rs @@ -214,11 +214,12 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { ); }, ); - }; + } if let StmtKind::Semi(expr) = stmt.kind - && let ExprKind::Binary(ref binop, a, b) = expr.kind - && (binop.node == BinOpKind::And || binop.node == BinOpKind::Or) - && let Some(sugg) = Sugg::hir_opt(cx, a) + && let ExprKind::Binary(binop, a, b) = &expr.kind + && matches!(binop.node, BinOpKind::And | BinOpKind::Or) + && !stmt.span.from_expansion() + && expr.span.eq_ctxt(stmt.span) { span_lint_hir_and_then( cx, @@ -227,16 +228,14 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { stmt.span, "boolean short circuit operator in statement may be clearer using an explicit test", |diag| { - let sugg = if binop.node == BinOpKind::Or { !sugg } else { sugg }; - diag.span_suggestion( - stmt.span, - "replace it with", - format!("if {sugg} {{ {}; }}", &snippet(cx, b.span, ".."),), - Applicability::MachineApplicable, // snippet - ); + let mut app = Applicability::MachineApplicable; + let test = Sugg::hir_with_context(cx, a, expr.span.ctxt(), "_", &mut app); + let test = if binop.node == BinOpKind::Or { !test } else { test }; + let then = Sugg::hir_with_context(cx, b, expr.span.ctxt(), "_", &mut app); + diag.span_suggestion(stmt.span, "replace it with", format!("if {test} {{ {then}; }}"), app); }, ); - }; + } } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { diff --git a/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs b/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs index 748289454bea5..d52fe7e7d5b9c 100644 --- a/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs +++ b/src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs @@ -66,7 +66,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeParamMismatch { }) => impl_params.push((path.segments[0].ident.to_string(), path.span)), GenericArg::Type(_) => return, _ => (), - }; + } } // find the type that the Impl is for diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index 29dcbaa9e62a3..06e92985e6648 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -220,7 +220,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc { | hir::ItemKind::GlobalAsm(..) | hir::ItemKind::Impl { .. } | hir::ItemKind::Use(..) => note_prev_span_then_ret!(self.prev_span, it.span), - }; + } let (article, desc) = cx.tcx.article_and_description(it.owner_id.to_def_id()); diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs index f28b431ab997e..e9ec23b1efa61 100644 --- a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs +++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs @@ -207,7 +207,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug { // this prevents ICEs such as when self is a type parameter or a primitive type // (see #10887, #11063) && let Res::Def(DefKind::Struct | DefKind::Enum | DefKind::Union, self_path_did) = self_path.res - && cx.match_def_path(trait_def_id, &[sym::core, sym::fmt, sym::Debug]) + && cx.tcx.is_diagnostic_item(sym::Debug, trait_def_id) // don't trigger if this impl was derived && !cx.tcx.has_attr(item.owner_id, sym::automatically_derived) && !item.span.from_expansion() diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index 05aa425de9edf..bba1b63be277f 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -135,7 +135,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline { | hir::ItemKind::ForeignMod { .. } | hir::ItemKind::Impl { .. } | hir::ItemKind::Use(..) => {}, - }; + } } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { diff --git a/src/tools/clippy/clippy_lints/src/multi_assignments.rs b/src/tools/clippy/clippy_lints/src/multi_assignments.rs index 9a6b1dfc52b5c..4383f28717dcb 100644 --- a/src/tools/clippy/clippy_lints/src/multi_assignments.rs +++ b/src/tools/clippy/clippy_lints/src/multi_assignments.rs @@ -56,10 +56,10 @@ impl EarlyLintPass for MultiAssignments { if let ExprKind::Assign(target, source, _) = &expr.kind { if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(target).kind { span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively"); - }; + } if let ExprKind::Assign(_target, _source, _) = &strip_paren_blocks(source).kind { span_lint(cx, MULTI_ASSIGNMENTS, expr.span, "assignments don't nest intuitively"); } - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs b/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs index 882ab2dda7aaa..0e1980a6acb61 100644 --- a/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs +++ b/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs @@ -1,5 +1,5 @@ use rustc_ast::visit::FnKind; -use rustc_ast::{NodeId, WherePredicateKind}; +use rustc_ast::{Fn, NodeId, WherePredicateKind}; use rustc_data_structures::fx::FxHashMap; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; @@ -39,7 +39,7 @@ declare_lint_pass!(MultipleBoundLocations => [MULTIPLE_BOUND_LOCATIONS]); impl EarlyLintPass for MultipleBoundLocations { fn check_fn(&mut self, cx: &EarlyContext<'_>, kind: FnKind<'_>, _: Span, _: NodeId) { - if let FnKind::Fn(_, _, _, _, generics, _) = kind + if let FnKind::Fn(_, _, _, Fn { generics, .. }) = kind && !generics.params.is_empty() && !generics.where_clause.predicates.is_empty() { diff --git a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 79252bba74d72..aad6ae52a6db0 100644 --- a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -171,7 +171,7 @@ fn collect_unsafe_exprs<'tcx>( }, _ => {}, - }; + } Continue::<(), _>(Descend::Yes) }); diff --git a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs index 3884149645883..86c084423b713 100644 --- a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs +++ b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs @@ -91,7 +91,7 @@ impl<'tcx> LateLintPass<'tcx> for Mutex { ty::Uint(t) if t != UintTy::Usize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), ty::Int(t) if t != IntTy::Isize => span_lint(cx, MUTEX_INTEGER, expr.span, msg), _ => span_lint(cx, MUTEX_ATOMIC, expr.span, msg), - }; + } } } } diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs index a67addea94866..4e19a2f409dd3 100644 --- a/src/tools/clippy/clippy_lints/src/needless_late_init.rs +++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs @@ -107,6 +107,10 @@ struct LocalAssign { impl LocalAssign { fn from_expr(expr: &Expr<'_>, span: Span) -> Option { + if expr.span.from_expansion() { + return None; + } + if let ExprKind::Assign(lhs, rhs, _) = expr.kind { if lhs.span.from_expansion() { return None; @@ -336,7 +340,7 @@ fn check<'tcx>( ); }, _ => {}, - }; + } Some(()) } diff --git a/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs b/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs index 9ee4e49327775..2e2916c957daa 100644 --- a/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs +++ b/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs @@ -37,7 +37,9 @@ declare_lint_pass!(NoMangleWithRustAbi => [NO_MANGLE_WITH_RUST_ABI]); impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { - if let ItemKind::Fn { sig: fn_sig, .. } = &item.kind { + if let ItemKind::Fn { sig: fn_sig, .. } = &item.kind + && !item.span.from_expansion() + { let attrs = cx.tcx.hir().attrs(item.hir_id()); let mut app = Applicability::MaybeIncorrect; let fn_snippet = snippet_with_applicability(cx, fn_sig.span.with_hi(item.ident.span.lo()), "..", &mut app); diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index 8409d179b0f58..147654675ec91 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -89,16 +89,6 @@ declare_clippy_lint! { /// /// The `const` value should be stored inside a `static` item. /// - /// ### Known problems - /// When an enum has variants with interior mutability, use of its non - /// interior mutable variants can generate false positives. See issue - /// [#3962](https://github.com/rust-lang/rust-clippy/issues/3962) - /// - /// Types that have underlying or potential interior mutability trigger the lint whether - /// the interior mutable field is used or not. See issues - /// [#5812](https://github.com/rust-lang/rust-clippy/issues/5812) and - /// [#3825](https://github.com/rust-lang/rust-clippy/issues/3825) - /// /// ### Example /// ```no_run /// use std::sync::atomic::{AtomicUsize, Ordering::SeqCst}; diff --git a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs index 0caa19cd84436..852c3885f5689 100644 --- a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs +++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs @@ -73,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { } }, _ => {}, - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs b/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs new file mode 100644 index 0000000000000..312610db0423c --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs @@ -0,0 +1,306 @@ +use clippy_config::Conf; +use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::msrvs::Msrv; +use clippy_utils::visitors::for_each_expr; +use clippy_utils::{def_path_def_ids, fn_def_id, path_def_id}; +use rustc_data_structures::fx::FxIndexMap; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::{CrateNum, DefId}; +use rustc_hir::{self as hir, BodyId, Expr, ExprKind, Item, ItemKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; +use rustc_session::impl_lint_pass; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Lints when `once_cell::sync::Lazy` or `lazy_static!` are used to define a static variable, + /// and suggests replacing such cases with `std::sync::LazyLock` instead. + /// + /// Note: This lint will not trigger in crate with `no_std` context, or with MSRV < 1.80.0. It + /// also will not trigger on `once_cell::sync::Lazy` usage in crates which use other types + /// from `once_cell`, such as `once_cell::race::OnceBox`. + /// + /// ### Why restrict this? + /// - Reduces the need for an extra dependency + /// - Enforce convention of using standard library types when possible + /// + /// ### Example + /// ```ignore + /// lazy_static! { + /// static ref FOO: String = "foo".to_uppercase(); + /// } + /// static BAR: once_cell::sync::Lazy = once_cell::sync::Lazy::new(|| "BAR".to_lowercase()); + /// ``` + /// Use instead: + /// ```ignore + /// static FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "FOO".to_lowercase()); + /// static BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| "BAR".to_lowercase()); + /// ``` + #[clippy::version = "1.81.0"] + pub NON_STD_LAZY_STATICS, + pedantic, + "lazy static that could be replaced by `std::sync::LazyLock`" +} + +/// A list containing functions with corresponding replacements in `LazyLock`. +/// +/// Some functions could be replaced as well if we have replaced `Lazy` to `LazyLock`, +/// therefore after suggesting replace the type, we need to make sure the function calls can be +/// replaced, otherwise the suggestions cannot be applied thus the applicability should be +/// `Unspecified` or `MaybeIncorret`. +static FUNCTION_REPLACEMENTS: &[(&str, Option<&str>)] = &[ + ("once_cell::sync::Lazy::force", Some("std::sync::LazyLock::force")), + ("once_cell::sync::Lazy::get", None), // `std::sync::LazyLock::get` is experimental + ("once_cell::sync::Lazy::new", Some("std::sync::LazyLock::new")), + // Note: `Lazy::{into_value, get_mut, force_mut}` are not in the list. + // Because the lint only checks for `static`s, and using these functions with statics + // will either be a hard error or triggers `static_mut_ref` that will be hard errors. + // But keep in mind that if somehow we decide to expand this lint to catch non-statics, + // add those functions into the list. +]; + +pub struct NonStdLazyStatic { + msrv: Msrv, + lazy_static_lazy_static: Vec, + once_cell_crate: Vec, + once_cell_sync_lazy: Vec, + once_cell_sync_lazy_new: Vec, + sugg_map: FxIndexMap>, + lazy_type_defs: FxIndexMap, + uses_other_once_cell_types: bool, +} + +impl NonStdLazyStatic { + #[must_use] + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + lazy_static_lazy_static: Vec::new(), + once_cell_crate: Vec::new(), + once_cell_sync_lazy: Vec::new(), + once_cell_sync_lazy_new: Vec::new(), + sugg_map: FxIndexMap::default(), + lazy_type_defs: FxIndexMap::default(), + uses_other_once_cell_types: false, + } + } +} + +impl_lint_pass!(NonStdLazyStatic => [NON_STD_LAZY_STATICS]); + +/// Return if current MSRV does not meet the requirement for `lazy_cell` feature, +/// or current context has `no_std` attribute. +macro_rules! ensure_prerequisite { + ($msrv:expr, $cx:ident) => { + if !$msrv.meets(clippy_utils::msrvs::LAZY_CELL) || clippy_utils::is_no_std_crate($cx) { + return; + } + }; +} + +impl<'hir> LateLintPass<'hir> for NonStdLazyStatic { + extract_msrv_attr!(LateContext); + + fn check_crate(&mut self, cx: &LateContext<'hir>) { + // Do not lint if current crate does not support `LazyLock`. + ensure_prerequisite!(self.msrv, cx); + + // Fetch def_ids for external paths + self.lazy_static_lazy_static = def_path_def_ids(cx.tcx, &["lazy_static", "lazy_static"]).collect(); + self.once_cell_sync_lazy = def_path_def_ids(cx.tcx, &["once_cell", "sync", "Lazy"]).collect(); + self.once_cell_sync_lazy_new = def_path_def_ids(cx.tcx, &["once_cell", "sync", "Lazy", "new"]).collect(); + // And CrateNums for `once_cell` crate + self.once_cell_crate = self.once_cell_sync_lazy.iter().map(|d| d.krate).collect(); + + // Convert hardcoded fn replacement list into a map with def_id + for (path, sugg) in FUNCTION_REPLACEMENTS { + let path_vec: Vec<&str> = path.split("::").collect(); + for did in def_path_def_ids(cx.tcx, &path_vec) { + self.sugg_map.insert(did, sugg.map(ToOwned::to_owned)); + } + } + } + + fn check_item(&mut self, cx: &LateContext<'hir>, item: &Item<'hir>) { + ensure_prerequisite!(self.msrv, cx); + + if let ItemKind::Static(..) = item.kind + && let Some(macro_call) = clippy_utils::macros::root_macro_call(item.span) + && self.lazy_static_lazy_static.contains(¯o_call.def_id) + { + span_lint( + cx, + NON_STD_LAZY_STATICS, + macro_call.span, + "this macro has been superceded by `std::sync::LazyLock`", + ); + return; + } + + if in_external_macro(cx.sess(), item.span) { + return; + } + + if let Some(lazy_info) = LazyInfo::from_item(self, cx, item) { + self.lazy_type_defs.insert(item.owner_id.to_def_id(), lazy_info); + } + } + + fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &Expr<'hir>) { + ensure_prerequisite!(self.msrv, cx); + + // All functions in the `FUNCTION_REPLACEMENTS` have only one args + if let ExprKind::Call(callee, [arg]) = expr.kind + && let Some(call_def_id) = fn_def_id(cx, expr) + && self.sugg_map.contains_key(&call_def_id) + && let ExprKind::Path(qpath) = arg.peel_borrows().kind + && let Some(arg_def_id) = cx.typeck_results().qpath_res(&qpath, arg.hir_id).opt_def_id() + && let Some(lazy_info) = self.lazy_type_defs.get_mut(&arg_def_id) + { + lazy_info.calls_span_and_id.insert(callee.span, call_def_id); + } + } + + fn check_ty(&mut self, cx: &LateContext<'hir>, ty: &'hir rustc_hir::Ty<'hir, rustc_hir::AmbigArg>) { + ensure_prerequisite!(self.msrv, cx); + + // Record if types from `once_cell` besides `sync::Lazy` are used. + if let rustc_hir::TyKind::Path(qpath) = ty.peel_refs().kind + && let Some(ty_def_id) = cx.qpath_res(&qpath, ty.hir_id).opt_def_id() + // Is from `once_cell` crate + && self.once_cell_crate.contains(&ty_def_id.krate) + // And is NOT `once_cell::sync::Lazy` + && !self.once_cell_sync_lazy.contains(&ty_def_id) + { + self.uses_other_once_cell_types = true; + } + } + + fn check_crate_post(&mut self, cx: &LateContext<'hir>) { + ensure_prerequisite!(self.msrv, cx); + + if !self.uses_other_once_cell_types { + for (_, lazy_info) in &self.lazy_type_defs { + lazy_info.lint(cx, &self.sugg_map); + } + } + } +} + +struct LazyInfo { + /// Span of the [`hir::Ty`] without including args. + /// i.e.: + /// ```ignore + /// static FOO: Lazy = Lazy::new(...); + /// // ^^^^ + /// ``` + ty_span_no_args: Span, + /// `Span` and `DefId` of calls on `Lazy` type. + /// i.e.: + /// ```ignore + /// static FOO: Lazy = Lazy::new(...); + /// // ^^^^^^^^^ + /// ``` + calls_span_and_id: FxIndexMap, +} + +impl LazyInfo { + fn from_item(state: &NonStdLazyStatic, cx: &LateContext<'_>, item: &Item<'_>) -> Option { + // Check if item is a `once_cell:sync::Lazy` static. + if let ItemKind::Static(ty, _, body_id) = item.kind + && let Some(path_def_id) = path_def_id(cx, ty) + && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind + && state.once_cell_sync_lazy.contains(&path_def_id) + { + let ty_span_no_args = path_span_without_args(path); + let body = cx.tcx.hir().body(body_id); + + // visit body to collect `Lazy::new` calls + let mut new_fn_calls = FxIndexMap::default(); + for_each_expr::<(), ()>(cx, body, |ex| { + if let Some((fn_did, call_span)) = fn_def_id_and_span_from_body(cx, ex, body_id) + && state.once_cell_sync_lazy_new.contains(&fn_did) + { + new_fn_calls.insert(call_span, fn_did); + } + std::ops::ControlFlow::Continue(()) + }); + + Some(LazyInfo { + ty_span_no_args, + calls_span_and_id: new_fn_calls, + }) + } else { + None + } + } + + fn lint(&self, cx: &LateContext<'_>, sugg_map: &FxIndexMap>) { + // Applicability might get adjusted to `Unspecified` later if any calls + // in `calls_span_and_id` are not replaceable judging by the `sugg_map`. + let mut appl = Applicability::MachineApplicable; + let mut suggs = vec![(self.ty_span_no_args, "std::sync::LazyLock".to_string())]; + + for (span, def_id) in &self.calls_span_and_id { + let maybe_sugg = sugg_map.get(def_id).cloned().flatten(); + if let Some(sugg) = maybe_sugg { + suggs.push((*span, sugg)); + } else { + // If NO suggested replacement, not machine applicable + appl = Applicability::Unspecified; + } + } + + span_lint_and_then( + cx, + NON_STD_LAZY_STATICS, + self.ty_span_no_args, + "this type has been superceded by `LazyLock` in the standard library", + |diag| { + diag.multipart_suggestion("use `std::sync::LazyLock` instead", suggs, appl); + }, + ); + } +} + +/// Return the span of a given `Path` without including any of its args. +/// +/// NB: Re-write of a private function `rustc_lint::non_local_def::path_span_without_args`. +fn path_span_without_args(path: &hir::Path<'_>) -> Span { + path.segments + .last() + .and_then(|seg| seg.args) + .map_or(path.span, |args| path.span.until(args.span_ext)) +} + +/// Returns the `DefId` and `Span` of the callee if the given expression is a function call. +/// +/// NB: Modified from [`clippy_utils::fn_def_id`], to support calling in an static `Item`'s body. +fn fn_def_id_and_span_from_body(cx: &LateContext<'_>, expr: &Expr<'_>, body_id: BodyId) -> Option<(DefId, Span)> { + // FIXME: find a way to cache the result. + let typeck = cx.tcx.typeck_body(body_id); + match &expr.kind { + ExprKind::Call( + Expr { + kind: ExprKind::Path(qpath), + hir_id: path_hir_id, + span, + .. + }, + .., + ) => { + // Only return Fn-like DefIds, not the DefIds of statics/consts/etc that contain or + // deref to fn pointers, dyn Fn, impl Fn - #8850 + if let Res::Def(DefKind::Fn | DefKind::Ctor(..) | DefKind::AssocFn, id) = + typeck.qpath_res(qpath, *path_hir_id) + { + Some((id, *span)) + } else { + None + } + }, + _ => None, + } +} diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs index 0eca788c78746..9d07a14718da1 100644 --- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -104,7 +104,7 @@ impl ArithmeticSideEffects { if !tcx.is_diagnostic_item(sym::NonZero, adt.did()) { return false; - }; + } let int_type = substs.type_at(0); let unsigned_int_types = [ @@ -214,13 +214,13 @@ impl ArithmeticSideEffects { | hir::BinOpKind::Sub ) { return; - }; + } let (mut actual_lhs, lhs_ref_counter) = peel_hir_expr_refs(lhs); let (mut actual_rhs, rhs_ref_counter) = peel_hir_expr_refs(rhs); actual_lhs = expr_or_init(cx, actual_lhs); actual_rhs = expr_or_init(cx, actual_rhs); let lhs_ty = cx.typeck_results().expr_ty(actual_lhs).peel_refs(); - let rhs_ty = cx.typeck_results().expr_ty(actual_rhs).peel_refs(); + let rhs_ty = cx.typeck_results().expr_ty_adjusted(actual_rhs).peel_refs(); if self.has_allowed_binary(lhs_ty, rhs_ty) { return; } @@ -283,7 +283,7 @@ impl ArithmeticSideEffects { if ConstEvalCtxt::new(cx).eval_simple(receiver).is_some() { return; } - let instance_ty = cx.typeck_results().expr_ty(receiver); + let instance_ty = cx.typeck_results().expr_ty_adjusted(receiver); if !Self::is_integral(instance_ty) { return; } @@ -311,7 +311,7 @@ impl ArithmeticSideEffects { if ConstEvalCtxt::new(cx).eval(un_expr).is_some() { return; } - let ty = cx.typeck_results().expr_ty(expr).peel_refs(); + let ty = cx.typeck_results().expr_ty_adjusted(expr).peel_refs(); if self.has_allowed_unary(ty) { return; } diff --git a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs index b0d872e98fd4a..cf6b8992973a7 100644 --- a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs +++ b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs @@ -104,7 +104,7 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) } else { expr_snip = arg_snip.to_string(); eq_impl = without_deref; - }; + } let span; let hint; diff --git a/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs b/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs index 1a0bfd8b99703..10455d3b93a00 100644 --- a/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs @@ -127,7 +127,7 @@ pub(super) fn check<'tcx>( None, note, ); - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/operators/double_comparison.rs b/src/tools/clippy/clippy_lints/src/operators/double_comparison.rs index d72a2fc3b1ab5..54f50f11e0349 100644 --- a/src/tools/clippy/clippy_lints/src/operators/double_comparison.rs +++ b/src/tools/clippy/clippy_lints/src/operators/double_comparison.rs @@ -49,5 +49,5 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, op: BinOpKind, lhs: &'tcx Expr lint_double_comparison!(==); }, _ => (), - }; + } } diff --git a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs index 8272d3643d420..01dc6a27c33e3 100644 --- a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs @@ -120,7 +120,7 @@ fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { if let ty::Array(arr_ty, _) = value { return matches!(arr_ty.kind(), ty::Float(_)); - }; + } matches!(value, ty::Float(_)) } diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs index 9e8a821c3f4e8..d9845bc3b0f74 100644 --- a/src/tools/clippy/clippy_lints/src/operators/mod.rs +++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs @@ -262,7 +262,7 @@ declare_clippy_lint! { /// to `trailing_zeros` /// /// ### Why is this bad? - /// `x.trailing_zeros() > 4` is much clearer than `x & 15 + /// `x.trailing_zeros() >= 4` is much clearer than `x & 15 /// == 0` /// /// ### Known problems @@ -278,7 +278,7 @@ declare_clippy_lint! { /// /// ```no_run /// # let x: i32 = 1; - /// if x.trailing_zeros() > 4 { } + /// if x.trailing_zeros() >= 4 { } /// ``` #[clippy::version = "pre 1.29.0"] pub VERBOSE_BIT_MASK, diff --git a/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs b/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs index c83bdda347a66..691d7b904eff5 100644 --- a/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs @@ -30,7 +30,7 @@ pub(super) fn check<'tcx>( } else { check_non_const_operands(cx, e, lhs); } - }; + } } fn used_in_comparison_with_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/operators/modulo_one.rs b/src/tools/clippy/clippy_lints/src/operators/modulo_one.rs index 54eea14833ffe..fc5565e821edd 100644 --- a/src/tools/clippy/clippy_lints/src/operators/modulo_one.rs +++ b/src/tools/clippy/clippy_lints/src/operators/modulo_one.rs @@ -21,6 +21,6 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, right: "any number modulo -1 will panic/overflow or result in 0", ); } - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs index 794bef7b32143..55676522419c6 100644 --- a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs +++ b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs @@ -53,6 +53,6 @@ impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl { ); } } - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/precedence.rs b/src/tools/clippy/clippy_lints/src/precedence.rs index 031f09310590b..421b2b7475553 100644 --- a/src/tools/clippy/clippy_lints/src/precedence.rs +++ b/src/tools/clippy/clippy_lints/src/precedence.rs @@ -43,7 +43,7 @@ impl EarlyLintPass for Precedence { cx, PRECEDENCE, expr.span, - "operator precedence can trip the unwary", + "operator precedence might not be obvious", "consider parenthesizing your expression", sugg, appl, diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs index 41a44de536b15..b4dadef57a3c5 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs @@ -208,7 +208,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall { // avoid clippy::double_parens if !is_in_fn_call_arg { hint = hint.maybe_par(); - }; + } diag.span_suggestion(full_expr.span, "try doing something like", hint, applicability); } diff --git a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs index 1b557730ecade..8d6b1c7274d93 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs @@ -1,8 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::HasSession; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::impl_lint_pass; use rustc_span::def_id::CRATE_DEF_ID; @@ -49,6 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { && !cx.effective_visibilities.is_exported(item.owner_id.def_id) && self.is_exported.last() == Some(&false) && is_not_macro_export(item) + && !in_external_macro(cx.sess(), item.span) { let span = item.span.with_hi(item.ident.span.hi()); let descr = cx.tcx.def_kind(item.owner_id).descr(item.owner_id.to_def_id()); diff --git a/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs b/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs index 81556f3961416..7bd4d6e993b47 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs @@ -215,6 +215,6 @@ impl LateLintPass<'_> for RedundantTypeAnnotations { }, _ => (), } - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs b/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs index f54cafffb83c9..5dddf9263a359 100644 --- a/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs +++ b/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::VecArgs; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::source::snippet; -use clippy_utils::{expr_or_init, fn_def_id}; +use clippy_utils::{expr_or_init, fn_def_id, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -93,6 +93,7 @@ fn check_repeat_fn(cx: &LateContext<'_>, expr: &Expr<'_>) { && let ExprKind::Call(_, [repeat_expr]) = expr.kind && fn_def_id(cx, repeat_expr).is_some_and(|did| cx.tcx.is_diagnostic_item(sym::vec_with_capacity, did)) && !repeat_expr.span.from_expansion() + && let Some(exec_context) = std_or_core(cx) { emit_lint( cx, @@ -100,7 +101,10 @@ fn check_repeat_fn(cx: &LateContext<'_>, expr: &Expr<'_>) { "iter::repeat", "none of the yielded `Vec`s will have the requested capacity", "if you intended to create an iterator that yields `Vec`s with an initial capacity, try", - format!("std::iter::repeat_with(|| {})", snippet(cx, repeat_expr.span, "..")), + format!( + "{exec_context}::iter::repeat_with(|| {})", + snippet(cx, repeat_expr.span, "..") + ), ); } } diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index dfaee8cc3054d..664e984fece3d 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::visitors::{Descend, for_each_expr, for_each_unconsumed_temporary}; +use clippy_utils::visitors::{Descend, for_each_expr}; use clippy_utils::{ - binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res, - path_to_local_id, span_contains_cfg, span_find_starting_semi, + binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, + leaks_droppable_temporary_with_limited_lifetime, path_res, path_to_local_id, span_contains_cfg, + span_find_starting_semi, }; use core::ops::ControlFlow; use rustc_ast::MetaItemInner; @@ -389,22 +390,8 @@ fn check_final_expr<'tcx>( } }; - if let Some(inner) = inner { - if for_each_unconsumed_temporary(cx, inner, |temporary_ty| { - if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env()) - && temporary_ty - .walk() - .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if !re.is_static())) - { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) - } - }) - .is_break() - { - return; - } + if inner.is_some_and(|inner| leaks_droppable_temporary_with_limited_lifetime(cx, inner)) { + return; } if ret_span.from_expansion() || is_from_proc_macro(cx, expr) { diff --git a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs index 9737b84cdb9c7..2d989b1cf0ba0 100644 --- a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs +++ b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs @@ -1,9 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::get_trait_def_id; use clippy_utils::higher::VecArgs; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::implements_trait; +use clippy_utils::{get_trait_def_id, is_no_std_crate}; use rustc_ast::{LitIntType, LitKind, UintTy}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, QPath, StructTailExpr}; @@ -125,7 +125,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit { span, format!("{suggested_type} of `Range` that is only one element"), |diag| { - if should_emit_every_value { + if should_emit_every_value && !is_no_std_crate(cx) { diag.span_suggestion( span, "if you wanted a `Vec` that contains the entire range, try", diff --git a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs index db1c75fc3dea9..f72ff10dd43c4 100644 --- a/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs +++ b/src/tools/clippy/clippy_lints/src/size_of_in_element_count.rs @@ -97,7 +97,7 @@ fn get_pointee_ty_and_count_expr<'tcx>( && let Some(pointee_ty) = cx.typeck_results().node_args(func.hir_id).types().next() { return Some((pointee_ty, count)); - }; + } if let ExprKind::MethodCall(method_path, ptr_self, [.., count], _) = expr.kind // Find calls to copy_{from,to}{,_nonoverlapping} and write_bytes methods && let method_ident = method_path.ident.as_str() @@ -108,7 +108,7 @@ fn get_pointee_ty_and_count_expr<'tcx>( cx.typeck_results().expr_ty(ptr_self).kind() { return Some((*pointee_ty, count)); - }; + } None } @@ -130,6 +130,6 @@ impl<'tcx> LateLintPass<'tcx> for SizeOfInElementCount { && pointee_ty == ty_used_for_size_of { span_lint_and_help(cx, SIZE_OF_IN_ELEMENT_COUNT, count_expr.span, LINT_MSG, None, HELP_MSG); - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs index d2d693eaa1f38..d26288adb3919 100644 --- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs @@ -3,6 +3,7 @@ use clippy_utils::macros::matching_root_macro_call; use clippy_utils::sugg::Sugg; use clippy_utils::{ SpanlessEq, get_enclosing_block, is_integer_literal, is_path_diagnostic_item, path_to_local, path_to_local_id, + span_contains_comment, }; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; @@ -190,7 +191,7 @@ impl SlowVectorInit { InitializationType::Extend(e) | InitializationType::Resize(e) => { Self::emit_lint(cx, e, vec_alloc, "slow zero-filling initialization"); }, - }; + } } fn emit_lint(cx: &LateContext<'_>, slow_fill: &Expr<'_>, vec_alloc: &VecAllocation<'_>, msg: &'static str) { @@ -206,6 +207,14 @@ impl SlowVectorInit { let span_to_replace = slow_fill .span .with_lo(vec_alloc.allocation_expr.span.source_callsite().lo()); + + // If there is no comment in `span_to_replace`, Clippy can automatically fix the code. + let app = if span_contains_comment(cx.tcx.sess.source_map(), span_to_replace) { + Applicability::Unspecified + } else { + Applicability::MachineApplicable + }; + span_lint_and_sugg( cx, SLOW_VECTOR_INITIALIZATION, @@ -213,7 +222,7 @@ impl SlowVectorInit { msg, "consider replacing this with", format!("vec![0; {len_expr}]"), - Applicability::Unspecified, + app, ); } } diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs index ff11680051232..9b4c3d275ae73 100644 --- a/src/tools/clippy/clippy_lints/src/swap.rs +++ b/src/tools/clippy/clippy_lints/src/swap.rs @@ -296,7 +296,7 @@ fn check_xor_swap<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { { let span = s1.span.to(s3.span); generate_swap_warning(block, cx, lhs0, rhs0, rhs1, rhs2, span, true); - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs index 531422798a68c..bed4e60ba62da 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs @@ -50,10 +50,7 @@ pub(super) fn check<'tcx>( } let args = last.args; let missing_generic = match args { - Some(args) if !args.args.is_empty() => args.args.iter().any(|arg| match arg { - GenericArg::Infer(_) => true, - _ => false, - }), + Some(args) if !args.args.is_empty() => args.args.iter().any(|arg| matches!(arg, GenericArg::Infer(_))), _ => true, }; if !missing_generic { diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs index 3729dfd3e86f2..f27aaa2fa77aa 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs @@ -24,7 +24,7 @@ pub(super) fn check<'tcx>( if !tcx.is_diagnostic_item(sym::NonZero, adt.did()) { return false; - }; + } let int_ty = substs.type_at(0); if from_ty != int_ty { diff --git a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs index 99a55f9fc357e..008e09dd8bd12 100644 --- a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs @@ -119,7 +119,7 @@ fn check_tuple<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, elements: & && let LitKind::Int(val, _) = lit.node { return (val == i as u128).then_some(lhs); - }; + } None }) diff --git a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs index 1a5fdf0cd64f2..2e97772407fd5 100644 --- a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs +++ b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs @@ -71,7 +71,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m Applicability::Unspecified, ); return true; - }; + } false }, _ => false, diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_semicolon.rs b/src/tools/clippy/clippy_lints/src/unnecessary_semicolon.rs new file mode 100644 index 0000000000000..efbc536dcb4d1 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unnecessary_semicolon.rs @@ -0,0 +1,109 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::leaks_droppable_temporary_with_limited_lifetime; +use rustc_errors::Applicability; +use rustc_hir::{Block, ExprKind, HirId, MatchSource, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::impl_lint_pass; +use rustc_span::edition::Edition::Edition2021; + +declare_clippy_lint! { + /// ### What it does + /// Checks for the presence of a semicolon at the end of + /// a `match` or `if` statement evaluating to `()`. + /// + /// ### Why is this bad? + /// The semicolon is not needed, and may be removed to + /// avoid confusion and visual clutter. + /// + /// ### Example + /// ```no_run + /// # let a: u32 = 42; + /// if a > 10 { + /// println!("a is greater than 10"); + /// }; + /// ``` + /// Use instead: + /// ```no_run + /// # let a: u32 = 42; + /// if a > 10 { + /// println!("a is greater than 10"); + /// } + /// ``` + #[clippy::version = "1.86.0"] + pub UNNECESSARY_SEMICOLON, + pedantic, + "unnecessary semicolon after expression returning `()`" +} + +#[derive(Default)] +pub struct UnnecessarySemicolon { + last_statements: Vec, +} + +impl_lint_pass!(UnnecessarySemicolon => [UNNECESSARY_SEMICOLON]); + +impl UnnecessarySemicolon { + /// Enter or leave a block, remembering the last statement of the block. + fn handle_block(&mut self, cx: &LateContext<'_>, block: &Block<'_>, enter: bool) { + // Up to edition 2021, removing the semicolon of the last statement of a block + // may result in the scrutinee temporary values to live longer than the block + // variables. To avoid this problem, we do not lint the last statement of an + // expressionless block. + if cx.tcx.sess.edition() <= Edition2021 + && block.expr.is_none() + && let Some(last_stmt) = block.stmts.last() + { + if enter { + self.last_statements.push(last_stmt.hir_id); + } else { + self.last_statements.pop(); + } + } + } + + /// Checks if `stmt` is the last statement in an expressionless block for edition ≤ 2021. + fn is_last_in_block(&self, stmt: &Stmt<'_>) -> bool { + self.last_statements + .last() + .is_some_and(|last_stmt_id| last_stmt_id == &stmt.hir_id) + } +} + +impl<'tcx> LateLintPass<'tcx> for UnnecessarySemicolon { + fn check_block(&mut self, cx: &LateContext<'_>, block: &Block<'_>) { + self.handle_block(cx, block, true); + } + + fn check_block_post(&mut self, cx: &LateContext<'_>, block: &Block<'_>) { + self.handle_block(cx, block, false); + } + + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &Stmt<'tcx>) { + // rustfmt already takes care of removing semicolons at the end + // of loops. + if let StmtKind::Semi(expr) = stmt.kind + && !stmt.span.from_expansion() + && !expr.span.from_expansion() + && matches!( + expr.kind, + ExprKind::If(..) | ExprKind::Match(_, _, MatchSource::Normal | MatchSource::Postfix) + ) + && cx.typeck_results().expr_ty(expr) == cx.tcx.types.unit + { + if self.is_last_in_block(stmt) && leaks_droppable_temporary_with_limited_lifetime(cx, expr) { + return; + } + + let semi_span = expr.span.shrink_to_hi().to(stmt.span.shrink_to_hi()); + span_lint_and_sugg( + cx, + UNNECESSARY_SEMICOLON, + semi_span, + "unnecessary semicolon", + "remove", + String::new(), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/unneeded_struct_pattern.rs b/src/tools/clippy/clippy_lints/src/unneeded_struct_pattern.rs new file mode 100644 index 0000000000000..40ba70d451dbd --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unneeded_struct_pattern.rs @@ -0,0 +1,76 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_from_proc_macro; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Pat, PatKind, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// Checks for struct patterns that match against unit variant. + /// + /// ### Why is this bad? + /// Struct pattern `{ }` or `{ .. }` is not needed for unit variant. + /// + /// ### Example + /// ```no_run + /// match Some(42) { + /// Some(v) => v, + /// None { .. } => 0, + /// }; + /// // Or + /// match Some(42) { + /// Some(v) => v, + /// None { } => 0, + /// }; + /// ``` + /// Use instead: + /// ```no_run + /// match Some(42) { + /// Some(v) => v, + /// None => 0, + /// }; + /// ``` + #[clippy::version = "1.83.0"] + pub UNNEEDED_STRUCT_PATTERN, + style, + "using struct pattern to match against unit variant" +} + +declare_lint_pass!(UnneededStructPattern => [UNNEEDED_STRUCT_PATTERN]); + +impl LateLintPass<'_> for UnneededStructPattern { + fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) { + if !pat.span.from_expansion() + && let PatKind::Struct(path, [], _) = &pat.kind + && let QPath::Resolved(_, path) = path + && let Res::Def(DefKind::Variant, did) = path.res + { + let enum_did = cx.tcx.parent(did); + let variant = cx.tcx.adt_def(enum_did).variant_with_id(did); + + let has_only_fields_brackets = variant.ctor.is_some() && variant.fields.is_empty(); + let non_exhaustive_activated = !variant.def_id.is_local() && variant.is_field_list_non_exhaustive(); + if !has_only_fields_brackets || non_exhaustive_activated { + return; + } + + if is_from_proc_macro(cx, *path) { + return; + } + + if let Some(brackets_span) = pat.span.trim_start(path.span) { + span_lint_and_sugg( + cx, + UNNEEDED_STRUCT_PATTERN, + brackets_span, + "struct pattern is not needed for a unit variant", + "remove the struct pattern", + String::new(), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs index 7c9455bf8abef..e65123b8a9492 100644 --- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs +++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs @@ -182,7 +182,7 @@ fn check_expr<'a>(cx: &LateContext<'a>, expr: &'a hir::Expr<'a>) { emit_lint(cx, expr.span, expr.hir_id, op, &[]); }, _ => {}, - }; + } } fn should_lint<'a>(cx: &LateContext<'a>, mut inner: &'a hir::Expr<'a>) -> Option { diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index 7ffab81a5444c..5e452c6d2ac09 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context}; +use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::{DiagExt as _, Sugg}; use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts}; use clippy_utils::{ @@ -12,6 +12,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::Obligation; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::adjustment::{Adjust, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{self, AdtDef, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt}; use rustc_session::impl_lint_pass; use rustc_span::{Span, sym}; @@ -251,26 +252,25 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { // ^^^ let (into_iter_recv, depth) = into_iter_deep_call(cx, into_iter_recv); - let plural = if depth == 0 { "" } else { "s" }; - let mut applicability = Applicability::MachineApplicable; - let sugg = snippet_with_applicability( - cx, - into_iter_recv.span.source_callsite(), - "", - &mut applicability, - ) - .into_owned(); span_lint_and_then( cx, USELESS_CONVERSION, e.span, "explicit call to `.into_iter()` in function argument accepting `IntoIterator`", |diag| { - diag.span_suggestion( - e.span, + let receiver_span = into_iter_recv.span.source_callsite(); + let adjustments = adjustments(cx, into_iter_recv); + let mut sugg = if adjustments.is_empty() { + vec![] + } else { + vec![(receiver_span.shrink_to_lo(), adjustments)] + }; + let plural = if depth == 0 { "" } else { "s" }; + sugg.push((e.span.with_lo(receiver_span.hi()), String::new())); + diag.multipart_suggestion( format!("consider removing the `.into_iter()`{plural}"), sugg, - applicability, + Applicability::MachineApplicable, ); diag.span_note(span, "this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()`"); }, @@ -431,3 +431,16 @@ fn has_eligible_receiver(cx: &LateContext<'_>, recv: &Expr<'_>, expr: &Expr<'_>) } false } + +fn adjustments(cx: &LateContext<'_>, expr: &Expr<'_>) -> String { + let mut prefix = String::new(); + for adj in cx.typeck_results().expr_adjustments(expr) { + match adj.kind { + Adjust::Deref(_) => prefix = format!("*{prefix}"), + Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Mut { .. })) => prefix = format!("&mut {prefix}"), + Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Not)) => prefix = format!("&{prefix}"), + _ => {}, + } + } + prefix +} diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs index 49aad881994e9..b8bcb9b375601 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs @@ -69,6 +69,6 @@ impl<'tcx> LateLintPass<'tcx> for SlowSymbolComparisons { ), applicability, ); - }; + } } } diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs index 0730b561bc29e..03c667846b611 100644 --- a/src/tools/clippy/clippy_lints/src/vec.rs +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -69,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec { }; if self.allow_in_test && is_in_test(cx.tcx, expr.hir_id) { return; - }; + } // the parent callsite of this `vec!` expression, or span to the borrowed one such as `&vec!` let callsite = expr.span.parent_callsite().unwrap_or(expr.span); diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index 7fa070cd226be..68b7e1592e2ea 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -3,7 +3,7 @@ name = "clippy_utils" # begin autogenerated version version = "0.1.86" # end autogenerated version -edition = "2021" +edition = "2024" description = "Helpful tools for writing lints, provided as they are used in Clippy" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index c267b804124af..251e3dfe41bed 100644 --- a/src/tools/clippy/clippy_utils/README.md +++ b/src/tools/clippy/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2025-01-09 +nightly-2025-01-28 ``` diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs index cd6290ced3344..179d42a8b5dc0 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -400,7 +400,9 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) { TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")), TyKind::Path(qpath) => qpath_search_pat(&qpath), TyKind::Infer(()) => (Pat::Str("_"), Pat::Str("_")), - TyKind::TraitObject(_, tagged_ptr) if let TraitObjectSyntax::Dyn = tagged_ptr.tag() => (Pat::Str("dyn"), Pat::Str("")), + TyKind::TraitObject(_, tagged_ptr) if let TraitObjectSyntax::Dyn = tagged_ptr.tag() => { + (Pat::Str("dyn"), Pat::Str("")) + }, // NOTE: `TraitObject` is incomplete. It will always return true then. _ => (Pat::Str(""), Pat::Str("")), } diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index d46beddf73124..a660623f41850 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -12,7 +12,9 @@ use rustc_apfloat::ieee::{Half, Quad}; use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_data_structures::sync::Lrc; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp, PatExpr, PatExprKind}; +use rustc_hir::{ + BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, PatExpr, PatExprKind, QPath, UnOp, +}; use rustc_lexer::tokenize; use rustc_lint::LateContext; use rustc_middle::mir::ConstValue; @@ -451,8 +453,8 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } else { Some(val) } - } - PatExprKind::ConstBlock(ConstBlock { body, ..}) => self.expr(self.tcx.hir().body(*body).value), + }, + PatExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir().body(*body).value), PatExprKind::Path(qpath) => self.qpath(qpath, pat_expr.hir_id), } } diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index 60be7e4a4d395..6bb876322f24f 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -475,7 +475,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) - Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)), _ => Some(VecInitKind::WithExprCapacity(arg.hir_id)), }; - }; + } }, ExprKind::Path(QPath::Resolved(_, path)) if cx.tcx.is_diagnostic_item(sym::default_fn, path.res.opt_def_id()?) diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 9e11a57d1b301..e471dfb6ef191 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -88,7 +88,7 @@ use core::mem; use core::ops::ControlFlow; use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; -use std::iter::{once, repeat}; +use std::iter::{once, repeat_n}; use std::sync::{Mutex, MutexGuard, OnceLock}; use itertools::Itertools; @@ -116,15 +116,15 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ - self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgsRef, IntTy, Ty, TyCtxt, - TypeVisitableExt, UintTy, UpvarCapture, + self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgKind, GenericArgsRef, IntTy, Ty, + TyCtxt, TypeVisitableExt, UintTy, UpvarCapture, }; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{Ident, Symbol, kw}; use rustc_span::{InnerSpan, Span, sym}; use rustc_target::abi::Integer; -use visitors::Visitable; +use visitors::{Visitable, for_each_unconsumed_temporary}; use crate::consts::{ConstEvalCtxt, Constant, mir_to_const}; use crate::higher::Range; @@ -814,7 +814,7 @@ fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &' e = ep; }, _ => break e, - }; + } }; result.reverse(); (result, root) @@ -2045,7 +2045,7 @@ pub fn get_async_fn_body<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'_>) -> Option<&'t { return Some(expr); } - }; + } None } @@ -3420,7 +3420,7 @@ fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> St })) .join("::") } else { - repeat(String::from("super")).take(go_up_by).chain(path).join("::") + repeat_n(String::from("super"), go_up_by).chain(path).join("::") } } @@ -3465,3 +3465,20 @@ pub fn is_receiver_of_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool } false } + +/// Returns true if `expr` creates any temporary whose type references a non-static lifetime and has +/// a significant drop and does not consume it. +pub fn leaks_droppable_temporary_with_limited_lifetime<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + for_each_unconsumed_temporary(cx, expr, |temporary_ty| { + if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env()) + && temporary_ty + .walk() + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if !re.is_static())) + { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .is_break() +} diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs index 45beb146eb6ab..f4c730ef118b0 100644 --- a/src/tools/clippy/clippy_utils/src/macros.rs +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -251,7 +251,7 @@ impl<'a> PanicExpn<'a> { // This has no argument if name == "panic_cold_explicit" { return Some(Self::Empty); - }; + } let [arg, rest @ ..] = args else { return None; diff --git a/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs b/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs index 5eb9b3b8f227b..605764cef89fe 100644 --- a/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs +++ b/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs @@ -184,9 +184,10 @@ impl<'b, 'tcx> PossibleBorrowerMap<'b, 'tcx> { vis.visit_body(mir); vis.into_map(cx) }; - let maybe_storage_live_result = MaybeStorageLive::new(Cow::Owned(DenseBitSet::new_empty(mir.local_decls.len()))) - .iterate_to_fixpoint(cx.tcx, mir, Some("redundant_clone")) - .into_results_cursor(mir); + let maybe_storage_live_result = + MaybeStorageLive::new(Cow::Owned(DenseBitSet::new_empty(mir.local_decls.len()))) + .iterate_to_fixpoint(cx.tcx, mir, Some("redundant_clone")) + .into_results_cursor(mir); let mut vis = PossibleBorrowerVisitor::new(cx, mir, possible_origin); vis.visit_body(mir); vis.into_map(cx, maybe_storage_live_result) diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index 2169a5fdd63ba..d73cb7e35611c 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -18,10 +18,10 @@ macro_rules! msrv_aliases { // names may refer to stabilized feature flags or library items msrv_aliases! { - 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY } + 1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP } 1,82,0 { IS_NONE_OR, REPEAT_N, RAW_REF_OP } - 1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE } - 1,80,0 { BOX_INTO_ITER } + 1,81,0 { LINT_REASONS_STABILIZATION, ERROR_IN_CORE, EXPLICIT_SELF_TYPE_ELISION } + 1,80,0 { BOX_INTO_ITER, LAZY_CELL } 1,77,0 { C_STR_LITERALS } 1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT } 1,74,0 { REPR_RUST } diff --git a/src/tools/clippy/clippy_utils/src/numeric_literal.rs b/src/tools/clippy/clippy_utils/src/numeric_literal.rs index 2c49df9d807f7..bb2a628211000 100644 --- a/src/tools/clippy/clippy_utils/src/numeric_literal.rs +++ b/src/tools/clippy/clippy_utils/src/numeric_literal.rs @@ -125,7 +125,7 @@ impl<'a> NumericLiteral<'a> { integer = &digits[..exp_start]; } else { fraction = Some(&digits[integer.len() + 1..exp_start]); - }; + } exponent = Some((&digits[exp_start..=i], &digits[i + 1..])); break; }, diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index 104ae154e3695..287bdc9a6fd6f 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -29,13 +29,14 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv) let def_id = body.source.def_id(); for local in &body.local_decls { - check_ty(tcx, local.ty, local.source_info.span)?; + check_ty(tcx, local.ty, local.source_info.span, msrv)?; } // impl trait is gone in MIR, so check the return type manually check_ty( tcx, tcx.fn_sig(def_id).instantiate_identity().output().skip_binder(), body.local_decls.iter().next().unwrap().source_info.span, + msrv, )?; for bb in &*body.basic_blocks { @@ -51,7 +52,7 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv) Ok(()) } -fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult { +fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, msrv: &Msrv) -> McfResult { for arg in ty.walk() { let ty = match arg.unpack() { GenericArgKind::Type(ty) => ty, @@ -62,7 +63,7 @@ fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult { }; match ty.kind() { - ty::Ref(_, _, hir::Mutability::Mut) => { + ty::Ref(_, _, hir::Mutability::Mut) if !msrv.meets(msrvs::CONST_MUT_REFS) => { return Err((span, "mutable references in const fn are unstable".into())); }, ty::Alias(ty::Opaque, ..) => return Err((span, "`impl Trait` in const fn is unstable".into())), diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index 32e7c2bbf7cb6..e9a05c45747de 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -96,7 +96,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' return false; } - for (predicate, _span) in cx.tcx.explicit_item_super_predicates(def_id).iter_identity_copied() { + for (predicate, _span) in cx.tcx.explicit_item_self_bounds(def_id).iter_identity_copied() { match predicate.kind().skip_binder() { // For `impl Trait`, it will register a predicate of `T: Trait`, so we go through // and check substitutions to find `U`. @@ -118,7 +118,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<' if contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen) { return true; } - }; + } }, _ => (), } @@ -322,7 +322,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { }, ty::Tuple(args) => args.iter().any(|ty| is_must_use_ty(cx, ty)), ty::Alias(ty::Opaque, AliasTy { def_id, .. }) => { - for (predicate, _) in cx.tcx.explicit_item_super_predicates(def_id).skip_binder() { + for (predicate, _) in cx.tcx.explicit_item_self_bounds(def_id).skip_binder() { if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() { if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) { return true; @@ -712,7 +712,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option sig_from_bounds( cx, ty, - cx.tcx.item_super_predicates(def_id).iter_instantiated(cx.tcx, args), + cx.tcx.item_self_bounds(def_id).iter_instantiated(cx.tcx, args), cx.tcx.opt_parent(def_id), ), ty::FnPtr(sig_tys, hdr) => Some(ExprFnSig::Sig(sig_tys.with(hdr), None)), @@ -1341,3 +1341,14 @@ pub fn get_field_by_name<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, name: Symbol) -> _ => None, } } + +/// Check if `ty` is an `Option` and return its argument type if it is. +pub fn option_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + match ty.kind() { + ty::Adt(adt, args) => cx + .tcx + .is_diagnostic_item(sym::Option, adt.did()) + .then(|| args.type_at(0)), + _ => None, + } +} diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs index b5cec31ba9dab..3398ff8af2f5a 100644 --- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs @@ -55,7 +55,7 @@ fn expr_type_certainty(cx: &LateContext<'_>, expr: &Expr<'_>) -> Certainty { && let Some(self_ty_def_id) = adt_def_id(self_ty(cx, method_def_id)) { receiver_type_certainty = receiver_type_certainty.with_def_id(self_ty_def_id); - }; + } let lhs = path_segment_certainty(cx, receiver_type_certainty, method, false); let rhs = if type_is_inferable_from_arguments(cx, expr) { meet( diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index dcc763a8abd77..99984c41714bd 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -2,12 +2,11 @@ use crate::ty::needs_ordered_drop; use crate::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; use rustc_ast::visit::{VisitorResult, try_visit}; -use rustc_hir::{self as hir, AmbigArg}; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::intravisit::{self, Visitor, walk_block, walk_expr}; use rustc_hir::{ - AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, LetExpr, Pat, QPath, - Stmt, StructTailExpr, UnOp, UnsafeSource, + self as hir, AmbigArg, AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, + ItemKind, LetExpr, Pat, QPath, Stmt, StructTailExpr, UnOp, UnsafeSource, }; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; diff --git a/src/tools/clippy/lintcheck/Cargo.toml b/src/tools/clippy/lintcheck/Cargo.toml index b0e4e3e3e573b..55e588f5ec736 100644 --- a/src/tools/clippy/lintcheck/Cargo.toml +++ b/src/tools/clippy/lintcheck/Cargo.toml @@ -6,7 +6,7 @@ readme = "README.md" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-clippy" categories = ["development-tools"] -edition = "2021" +edition = "2024" publish = false default-run = "lintcheck" diff --git a/src/tools/clippy/lintcheck/src/config.rs b/src/tools/clippy/lintcheck/src/config.rs index bd4fcc5e337e9..af243f94274d0 100644 --- a/src/tools/clippy/lintcheck/src/config.rs +++ b/src/tools/clippy/lintcheck/src/config.rs @@ -107,7 +107,7 @@ impl LintcheckConfig { } else { std::thread::available_parallelism().map_or(1, NonZero::get) }; - }; + } for lint_name in &mut config.lint_filter { *lint_name = format!( diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs index 03e2a24f6f989..e88d9f427becd 100644 --- a/src/tools/clippy/lintcheck/src/main.rs +++ b/src/tools/clippy/lintcheck/src/main.rs @@ -145,7 +145,7 @@ impl Crate { assert_eq!(status.code(), Some(0)); return Vec::new(); - }; + } if !config.fix { cmd.arg("--message-format=json"); @@ -313,7 +313,7 @@ fn lintcheck(config: LintcheckConfig) { filter }) .collect_into(&mut lint_level_args); - }; + } let crates: Vec = crates .into_iter() diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index b1f0a82b1f475..c15d1fe6cd348 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2025-01-09" +channel = "nightly-2025-01-28" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/rustc_tools_util/Cargo.toml b/src/tools/clippy/rustc_tools_util/Cargo.toml index b63632916ba15..cba0256394823 100644 --- a/src/tools/clippy/rustc_tools_util/Cargo.toml +++ b/src/tools/clippy/rustc_tools_util/Cargo.toml @@ -7,6 +7,6 @@ readme = "README.md" license = "MIT OR Apache-2.0" keywords = ["rustc", "tool", "git", "version", "hash"] categories = ["development-tools"] -edition = "2018" +edition = "2024" [dependencies] diff --git a/src/tools/clippy/rustfmt.toml b/src/tools/clippy/rustfmt.toml index 4248f42f654b1..0dc6adce7bfce 100644 --- a/src/tools/clippy/rustfmt.toml +++ b/src/tools/clippy/rustfmt.toml @@ -2,8 +2,8 @@ max_width = 120 comment_width = 100 match_block_trailing_comma = true wrap_comments = true -edition = "2021" +edition = "2024" error_on_line_overflow = true imports_granularity = "Module" -version = "Two" +style_edition = "2024" ignore = ["tests/ui/crashes/ice-10912.rs"] diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index 68edefd3095cf..8201f332d3333 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -223,7 +223,7 @@ pub fn main() { if !has_sysroot_arg(args) { args.extend(vec!["--sysroot".into(), sys_root]); } - }; + } }; // make "clippy-driver --rustc" work like a subcommand that passes further args to "rustc" diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index e2e4d92df79f9..d1b1a1d232329 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -300,7 +300,9 @@ fn run_ui_cargo(cx: &TestContext) { } fn main() { - set_var("CLIPPY_DISABLE_DOCS_LINKS", "true"); + unsafe { + set_var("CLIPPY_DISABLE_DOCS_LINKS", "true"); + } let cx = TestContext::new(); diff --git a/src/tools/clippy/tests/missing-test-files.rs b/src/tools/clippy/tests/missing-test-files.rs index 64eba5e0888a4..565dcd73f582d 100644 --- a/src/tools/clippy/tests/missing-test-files.rs +++ b/src/tools/clippy/tests/missing-test-files.rs @@ -60,7 +60,7 @@ fn explore_directory(dir: &Path) -> Vec { } }, _ => {}, - }; + } } } } diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr index ff178924bd154..ae5d8ef1d0b59 100644 --- a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr +++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr @@ -13,5 +13,9 @@ note: rustc running on note: compiler flags: -Z ui-testing -Z deduplicate-diagnostics=no +query stack during panic: +#0 [early_lint_checks] perform lints prior to macro expansion +#1 [hir_crate] getting the crate HIR +... and 3 other queries... use `env RUST_BACKTRACE=1` to see the full query stack note: Clippy version: foo diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs index 3f20407308516..f09106773c7ee 100644 --- a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs +++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs @@ -170,6 +170,7 @@ pub fn hard_coded_allowed() { let _ = Saturating(0u32) + Saturating(0u32); let _ = String::new() + ""; + let _ = String::new() + &String::new(); let _ = Wrapping(0u32) + Wrapping(0u32); let saturating: Saturating = Saturating(0u32); @@ -408,11 +409,14 @@ pub fn unknown_ops_or_runtime_ops_that_can_overflow() { _n.wrapping_rem(_n); _n.wrapping_rem_euclid(_n); + _n.saturating_div(*Box::new(_n)); + // Unary _n = -_n; _n = -&_n; _custom = -_custom; _custom = -&_custom; + _ = -*Box::new(_n); } // Copied and pasted from the `integer_arithmetic` lint for comparison. @@ -534,4 +538,11 @@ pub fn issue_12318() { one.sub_assign(1); } +pub fn explicit_methods() { + use core::ops::Add; + let one: i32 = 1; + one.add(&one); + Box::new(one).add(one); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr index 78b1aca4b8a47..9b4cfb83fbb29 100644 --- a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr +++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr @@ -14,730 +14,760 @@ LL | let _ = 1f128 + 1f128; | ^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:307:5 + --> tests/ui/arithmetic_side_effects.rs:173:13 + | +LL | let _ = String::new() + &String::new(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> tests/ui/arithmetic_side_effects.rs:308:5 | LL | _n += 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:308:5 + --> tests/ui/arithmetic_side_effects.rs:309:5 | LL | _n += &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:309:5 + --> tests/ui/arithmetic_side_effects.rs:310:5 | LL | _n -= 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:310:5 + --> tests/ui/arithmetic_side_effects.rs:311:5 | LL | _n -= &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:311:5 + --> tests/ui/arithmetic_side_effects.rs:312:5 | LL | _n /= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:312:5 + --> tests/ui/arithmetic_side_effects.rs:313:5 | LL | _n /= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:313:5 + --> tests/ui/arithmetic_side_effects.rs:314:5 | LL | _n %= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:314:5 + --> tests/ui/arithmetic_side_effects.rs:315:5 | LL | _n %= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:315:5 + --> tests/ui/arithmetic_side_effects.rs:316:5 | LL | _n *= 2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:316:5 + --> tests/ui/arithmetic_side_effects.rs:317:5 | LL | _n *= &2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:317:5 + --> tests/ui/arithmetic_side_effects.rs:318:5 | LL | _n += -1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:318:5 + --> tests/ui/arithmetic_side_effects.rs:319:5 | LL | _n += &-1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:319:5 + --> tests/ui/arithmetic_side_effects.rs:320:5 | LL | _n -= -1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:320:5 + --> tests/ui/arithmetic_side_effects.rs:321:5 | LL | _n -= &-1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:321:5 + --> tests/ui/arithmetic_side_effects.rs:322:5 | LL | _n /= -0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:322:5 + --> tests/ui/arithmetic_side_effects.rs:323:5 | LL | _n /= &-0; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:323:5 + --> tests/ui/arithmetic_side_effects.rs:324:5 | LL | _n %= -0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:324:5 + --> tests/ui/arithmetic_side_effects.rs:325:5 | LL | _n %= &-0; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:325:5 + --> tests/ui/arithmetic_side_effects.rs:326:5 | LL | _n *= -2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:326:5 + --> tests/ui/arithmetic_side_effects.rs:327:5 | LL | _n *= &-2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:327:5 + --> tests/ui/arithmetic_side_effects.rs:328:5 | LL | _custom += Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:328:5 + --> tests/ui/arithmetic_side_effects.rs:329:5 | LL | _custom += &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:329:5 + --> tests/ui/arithmetic_side_effects.rs:330:5 | LL | _custom -= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:330:5 + --> tests/ui/arithmetic_side_effects.rs:331:5 | LL | _custom -= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:331:5 + --> tests/ui/arithmetic_side_effects.rs:332:5 | LL | _custom /= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:332:5 + --> tests/ui/arithmetic_side_effects.rs:333:5 | LL | _custom /= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:333:5 + --> tests/ui/arithmetic_side_effects.rs:334:5 | LL | _custom %= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:334:5 + --> tests/ui/arithmetic_side_effects.rs:335:5 | LL | _custom %= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:335:5 + --> tests/ui/arithmetic_side_effects.rs:336:5 | LL | _custom *= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:336:5 + --> tests/ui/arithmetic_side_effects.rs:337:5 | LL | _custom *= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:337:5 + --> tests/ui/arithmetic_side_effects.rs:338:5 | LL | _custom >>= Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:338:5 + --> tests/ui/arithmetic_side_effects.rs:339:5 | LL | _custom >>= &Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:339:5 + --> tests/ui/arithmetic_side_effects.rs:340:5 | LL | _custom <<= Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:340:5 + --> tests/ui/arithmetic_side_effects.rs:341:5 | LL | _custom <<= &Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:341:5 + --> tests/ui/arithmetic_side_effects.rs:342:5 | LL | _custom += -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:342:5 + --> tests/ui/arithmetic_side_effects.rs:343:5 | LL | _custom += &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:343:5 + --> tests/ui/arithmetic_side_effects.rs:344:5 | LL | _custom -= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:344:5 + --> tests/ui/arithmetic_side_effects.rs:345:5 | LL | _custom -= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:345:5 + --> tests/ui/arithmetic_side_effects.rs:346:5 | LL | _custom /= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:346:5 + --> tests/ui/arithmetic_side_effects.rs:347:5 | LL | _custom /= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:347:5 + --> tests/ui/arithmetic_side_effects.rs:348:5 | LL | _custom %= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:348:5 + --> tests/ui/arithmetic_side_effects.rs:349:5 | LL | _custom %= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:349:5 + --> tests/ui/arithmetic_side_effects.rs:350:5 | LL | _custom *= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:350:5 + --> tests/ui/arithmetic_side_effects.rs:351:5 | LL | _custom *= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:351:5 + --> tests/ui/arithmetic_side_effects.rs:352:5 | LL | _custom >>= -Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:352:5 + --> tests/ui/arithmetic_side_effects.rs:353:5 | LL | _custom >>= &-Custom; | ^^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:353:5 + --> tests/ui/arithmetic_side_effects.rs:354:5 | LL | _custom <<= -Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:354:5 + --> tests/ui/arithmetic_side_effects.rs:355:5 | LL | _custom <<= &-Custom; | ^^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:357:10 + --> tests/ui/arithmetic_side_effects.rs:358:10 | LL | _n = _n + 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:358:10 + --> tests/ui/arithmetic_side_effects.rs:359:10 | LL | _n = _n + &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:359:10 + --> tests/ui/arithmetic_side_effects.rs:360:10 | LL | _n = 1 + _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:360:10 + --> tests/ui/arithmetic_side_effects.rs:361:10 | LL | _n = &1 + _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:361:10 + --> tests/ui/arithmetic_side_effects.rs:362:10 | LL | _n = _n - 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:362:10 + --> tests/ui/arithmetic_side_effects.rs:363:10 | LL | _n = _n - &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:363:10 + --> tests/ui/arithmetic_side_effects.rs:364:10 | LL | _n = 1 - _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:364:10 + --> tests/ui/arithmetic_side_effects.rs:365:10 | LL | _n = &1 - _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:365:10 + --> tests/ui/arithmetic_side_effects.rs:366:10 | LL | _n = _n / 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:366:10 + --> tests/ui/arithmetic_side_effects.rs:367:10 | LL | _n = _n / &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:367:10 + --> tests/ui/arithmetic_side_effects.rs:368:10 | LL | _n = _n % 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:368:10 + --> tests/ui/arithmetic_side_effects.rs:369:10 | LL | _n = _n % &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:369:10 + --> tests/ui/arithmetic_side_effects.rs:370:10 | LL | _n = _n * 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:370:10 + --> tests/ui/arithmetic_side_effects.rs:371:10 | LL | _n = _n * &2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:371:10 + --> tests/ui/arithmetic_side_effects.rs:372:10 | LL | _n = 2 * _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:372:10 + --> tests/ui/arithmetic_side_effects.rs:373:10 | LL | _n = &2 * _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:373:10 + --> tests/ui/arithmetic_side_effects.rs:374:10 | LL | _n = 23 + &85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:374:10 + --> tests/ui/arithmetic_side_effects.rs:375:10 | LL | _n = &23 + 85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:375:10 + --> tests/ui/arithmetic_side_effects.rs:376:10 | LL | _n = &23 + &85; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:376:15 + --> tests/ui/arithmetic_side_effects.rs:377:15 | LL | _custom = _custom + _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:377:15 + --> tests/ui/arithmetic_side_effects.rs:378:15 | LL | _custom = _custom + &_custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:378:15 + --> tests/ui/arithmetic_side_effects.rs:379:15 | LL | _custom = Custom + _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:379:15 + --> tests/ui/arithmetic_side_effects.rs:380:15 | LL | _custom = &Custom + _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:380:15 + --> tests/ui/arithmetic_side_effects.rs:381:15 | LL | _custom = _custom - Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:381:15 + --> tests/ui/arithmetic_side_effects.rs:382:15 | LL | _custom = _custom - &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:382:15 + --> tests/ui/arithmetic_side_effects.rs:383:15 | LL | _custom = Custom - _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:383:15 + --> tests/ui/arithmetic_side_effects.rs:384:15 | LL | _custom = &Custom - _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:384:15 + --> tests/ui/arithmetic_side_effects.rs:385:15 | LL | _custom = _custom / Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:385:15 + --> tests/ui/arithmetic_side_effects.rs:386:15 | LL | _custom = _custom / &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:386:15 + --> tests/ui/arithmetic_side_effects.rs:387:15 | LL | _custom = _custom % Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:387:15 + --> tests/ui/arithmetic_side_effects.rs:388:15 | LL | _custom = _custom % &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:388:15 + --> tests/ui/arithmetic_side_effects.rs:389:15 | LL | _custom = _custom * Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:389:15 + --> tests/ui/arithmetic_side_effects.rs:390:15 | LL | _custom = _custom * &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:390:15 + --> tests/ui/arithmetic_side_effects.rs:391:15 | LL | _custom = Custom * _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:391:15 + --> tests/ui/arithmetic_side_effects.rs:392:15 | LL | _custom = &Custom * _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:392:15 + --> tests/ui/arithmetic_side_effects.rs:393:15 | LL | _custom = Custom + &Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:393:15 + --> tests/ui/arithmetic_side_effects.rs:394:15 | LL | _custom = &Custom + Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:394:15 + --> tests/ui/arithmetic_side_effects.rs:395:15 | LL | _custom = &Custom + &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:395:15 + --> tests/ui/arithmetic_side_effects.rs:396:15 | LL | _custom = _custom >> _custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:396:15 + --> tests/ui/arithmetic_side_effects.rs:397:15 | LL | _custom = _custom >> &_custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:397:15 + --> tests/ui/arithmetic_side_effects.rs:398:15 | LL | _custom = Custom << _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:398:15 + --> tests/ui/arithmetic_side_effects.rs:399:15 | LL | _custom = &Custom << _custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:401:23 + --> tests/ui/arithmetic_side_effects.rs:402:23 | LL | _n.saturating_div(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:402:21 + --> tests/ui/arithmetic_side_effects.rs:403:21 | LL | _n.wrapping_div(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:403:21 + --> tests/ui/arithmetic_side_effects.rs:404:21 | LL | _n.wrapping_rem(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:404:28 + --> tests/ui/arithmetic_side_effects.rs:405:28 | LL | _n.wrapping_rem_euclid(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:406:23 + --> tests/ui/arithmetic_side_effects.rs:407:23 | LL | _n.saturating_div(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:407:21 + --> tests/ui/arithmetic_side_effects.rs:408:21 | LL | _n.wrapping_div(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:408:21 + --> tests/ui/arithmetic_side_effects.rs:409:21 | LL | _n.wrapping_rem(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:409:28 + --> tests/ui/arithmetic_side_effects.rs:410:28 | LL | _n.wrapping_rem_euclid(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:412:10 + --> tests/ui/arithmetic_side_effects.rs:412:23 + | +LL | _n.saturating_div(*Box::new(_n)); + | ^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> tests/ui/arithmetic_side_effects.rs:415:10 | LL | _n = -_n; | ^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:413:10 + --> tests/ui/arithmetic_side_effects.rs:416:10 | LL | _n = -&_n; | ^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:414:15 + --> tests/ui/arithmetic_side_effects.rs:417:15 | LL | _custom = -_custom; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:415:15 + --> tests/ui/arithmetic_side_effects.rs:418:15 | LL | _custom = -&_custom; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:424:5 + --> tests/ui/arithmetic_side_effects.rs:419:9 + | +LL | _ = -*Box::new(_n); + | ^^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> tests/ui/arithmetic_side_effects.rs:428:5 | LL | 1 + i; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:425:5 + --> tests/ui/arithmetic_side_effects.rs:429:5 | LL | i * 2; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:426:5 + --> tests/ui/arithmetic_side_effects.rs:430:5 | LL | 1 % i / 2; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:427:5 + --> tests/ui/arithmetic_side_effects.rs:431:5 | LL | i - 2 + 2 - i; | ^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:428:5 + --> tests/ui/arithmetic_side_effects.rs:432:5 | LL | -i; | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:439:5 + --> tests/ui/arithmetic_side_effects.rs:443:5 | LL | i += 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:440:5 + --> tests/ui/arithmetic_side_effects.rs:444:5 | LL | i -= 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:441:5 + --> tests/ui/arithmetic_side_effects.rs:445:5 | LL | i *= 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:443:5 + --> tests/ui/arithmetic_side_effects.rs:447:5 | LL | i /= 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:445:5 + --> tests/ui/arithmetic_side_effects.rs:449:5 | LL | i /= var1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:446:5 + --> tests/ui/arithmetic_side_effects.rs:450:5 | LL | i /= var2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:448:5 + --> tests/ui/arithmetic_side_effects.rs:452:5 | LL | i %= 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:450:5 + --> tests/ui/arithmetic_side_effects.rs:454:5 | LL | i %= var1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:451:5 + --> tests/ui/arithmetic_side_effects.rs:455:5 | LL | i %= var2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:461:5 + --> tests/ui/arithmetic_side_effects.rs:465:5 | LL | 10 / a | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:515:9 + --> tests/ui/arithmetic_side_effects.rs:519:9 | LL | x / maybe_zero | ^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:519:9 + --> tests/ui/arithmetic_side_effects.rs:523:9 | LL | x % maybe_zero | ^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:530:5 + --> tests/ui/arithmetic_side_effects.rs:534:5 | LL | one.add_assign(1); | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:534:5 + --> tests/ui/arithmetic_side_effects.rs:538:5 | LL | one.sub_assign(1); | ^^^^^^^^^^^^^^^^^ -error: aborting due to 123 previous errors +error: arithmetic operation that can potentially result in unexpected side-effects + --> tests/ui/arithmetic_side_effects.rs:544:5 + | +LL | one.add(&one); + | ^^^^^^^^^^^^^ + +error: arithmetic operation that can potentially result in unexpected side-effects + --> tests/ui/arithmetic_side_effects.rs:545:5 + | +LL | Box::new(one).add(one); + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 128 previous errors diff --git a/src/tools/clippy/tests/ui/auxiliary/non-exhaustive-enum.rs b/src/tools/clippy/tests/ui/auxiliary/non-exhaustive-enum.rs index 420232f9f8d89..e3205193ccea0 100644 --- a/src/tools/clippy/tests/ui/auxiliary/non-exhaustive-enum.rs +++ b/src/tools/clippy/tests/ui/auxiliary/non-exhaustive-enum.rs @@ -6,3 +6,24 @@ pub enum ErrorKind { #[doc(hidden)] Uncategorized, } + +#[non_exhaustive] +pub enum ExtNonExhaustiveEnum { + Unit, + Tuple(i32), + Struct { field: i32 }, +} + +pub enum ExtNonExhaustiveVariant { + ExhaustiveUnit, + #[non_exhaustive] + Unit, + #[non_exhaustive] + Tuple(i32), + #[non_exhaustive] + StructNoField {}, + #[non_exhaustive] + Struct { + field: i32, + }, +} diff --git a/src/tools/clippy/tests/ui/bytes_nth.fixed b/src/tools/clippy/tests/ui/bytes_nth.fixed index 11deb2390fd49..da35fcb55e5a6 100644 --- a/src/tools/clippy/tests/ui/bytes_nth.fixed +++ b/src/tools/clippy/tests/ui/bytes_nth.fixed @@ -1,4 +1,5 @@ #![allow(clippy::unnecessary_operation)] +#![allow(clippy::sliced_string_as_bytes)] #![warn(clippy::bytes_nth)] fn main() { diff --git a/src/tools/clippy/tests/ui/bytes_nth.rs b/src/tools/clippy/tests/ui/bytes_nth.rs index 62d9c7a5ea79b..5dbe84ecec8b7 100644 --- a/src/tools/clippy/tests/ui/bytes_nth.rs +++ b/src/tools/clippy/tests/ui/bytes_nth.rs @@ -1,4 +1,5 @@ #![allow(clippy::unnecessary_operation)] +#![allow(clippy::sliced_string_as_bytes)] #![warn(clippy::bytes_nth)] fn main() { diff --git a/src/tools/clippy/tests/ui/bytes_nth.stderr b/src/tools/clippy/tests/ui/bytes_nth.stderr index c6f21576c3dbf..c5f341cb37f6c 100644 --- a/src/tools/clippy/tests/ui/bytes_nth.stderr +++ b/src/tools/clippy/tests/ui/bytes_nth.stderr @@ -1,5 +1,5 @@ error: called `.bytes().nth()` on a `String` - --> tests/ui/bytes_nth.rs:6:13 + --> tests/ui/bytes_nth.rs:7:13 | LL | let _ = s.bytes().nth(3); | ^^^^^^^^^^^^^^^^ help: try: `s.as_bytes().get(3).copied()` @@ -8,13 +8,13 @@ LL | let _ = s.bytes().nth(3); = help: to override `-D warnings` add `#[allow(clippy::bytes_nth)]` error: called `.bytes().nth().unwrap()` on a `String` - --> tests/ui/bytes_nth.rs:7:14 + --> tests/ui/bytes_nth.rs:8:14 | LL | let _ = &s.bytes().nth(3).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.as_bytes()[3]` error: called `.bytes().nth()` on a `str` - --> tests/ui/bytes_nth.rs:8:13 + --> tests/ui/bytes_nth.rs:9:13 | LL | let _ = s[..].bytes().nth(3); | ^^^^^^^^^^^^^^^^^^^^ help: try: `s[..].as_bytes().get(3).copied()` diff --git a/src/tools/clippy/tests/ui/crashes/ice-11230.fixed b/src/tools/clippy/tests/ui/crashes/ice-11230.fixed new file mode 100644 index 0000000000000..1d4c3dd9dcc47 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-11230.fixed @@ -0,0 +1,16 @@ +// Test for https://github.com/rust-lang/rust-clippy/issues/11230 +#![warn(clippy::explicit_iter_loop)] +#![warn(clippy::needless_collect)] + +// explicit_iter_loop +fn main() { + const A: &[for<'a> fn(&'a ())] = &[]; + for v in A {} +} + +// needless_collect +trait Helper<'a>: Iterator {} + +fn x(w: &mut dyn for<'a> Helper<'a>) { + w.next().is_none(); +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-11230.rs b/src/tools/clippy/tests/ui/crashes/ice-11230.rs index 94044e9435ed4..a16fb271497cf 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-11230.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-11230.rs @@ -1,6 +1,16 @@ // Test for https://github.com/rust-lang/rust-clippy/issues/11230 +#![warn(clippy::explicit_iter_loop)] +#![warn(clippy::needless_collect)] +// explicit_iter_loop fn main() { const A: &[for<'a> fn(&'a ())] = &[]; for v in A.iter() {} } + +// needless_collect +trait Helper<'a>: Iterator {} + +fn x(w: &mut dyn for<'a> Helper<'a>) { + w.collect::>().is_empty(); +} diff --git a/src/tools/clippy/tests/ui/crashes/ice-11230.stderr b/src/tools/clippy/tests/ui/crashes/ice-11230.stderr new file mode 100644 index 0000000000000..7167d90e456e9 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-11230.stderr @@ -0,0 +1,20 @@ +error: it is more concise to loop over references to containers instead of using explicit iteration methods + --> tests/ui/crashes/ice-11230.rs:8:14 + | +LL | for v in A.iter() {} + | ^^^^^^^^ help: to write this more concisely, try: `A` + | + = note: `-D clippy::explicit-iter-loop` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::explicit_iter_loop)]` + +error: avoid using `collect()` when not needed + --> tests/ui/crashes/ice-11230.rs:15:7 + | +LL | w.collect::>().is_empty(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `next().is_none()` + | + = note: `-D clippy::needless-collect` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_collect)]` + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/crashes/ice-11422.fixed b/src/tools/clippy/tests/ui/crashes/ice-11422.fixed index ca5721cbb2bae..d996b1db08a63 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-11422.fixed +++ b/src/tools/clippy/tests/ui/crashes/ice-11422.fixed @@ -3,7 +3,7 @@ use std::fmt::Debug; use std::ops::*; -fn gen() -> impl PartialOrd + Debug {} +fn r#gen() -> impl PartialOrd + Debug {} struct Bar {} trait Foo {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-11422.rs b/src/tools/clippy/tests/ui/crashes/ice-11422.rs index 355ec2480bba4..eb89b7c38f43d 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-11422.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-11422.rs @@ -3,7 +3,7 @@ use std::fmt::Debug; use std::ops::*; -fn gen() -> impl PartialOrd + PartialEq + Debug {} +fn r#gen() -> impl PartialOrd + PartialEq + Debug {} struct Bar {} trait Foo {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-11422.stderr b/src/tools/clippy/tests/ui/crashes/ice-11422.stderr index a340977f4699d..67944e4e6e80d 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-11422.stderr +++ b/src/tools/clippy/tests/ui/crashes/ice-11422.stderr @@ -1,15 +1,15 @@ error: this bound is already specified as the supertrait of `PartialOrd` - --> tests/ui/crashes/ice-11422.rs:6:31 + --> tests/ui/crashes/ice-11422.rs:6:33 | -LL | fn gen() -> impl PartialOrd + PartialEq + Debug {} - | ^^^^^^^^^ +LL | fn r#gen() -> impl PartialOrd + PartialEq + Debug {} + | ^^^^^^^^^ | = note: `-D clippy::implied-bounds-in-impls` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::implied_bounds_in_impls)]` help: try removing this bound | -LL - fn gen() -> impl PartialOrd + PartialEq + Debug {} -LL + fn gen() -> impl PartialOrd + Debug {} +LL - fn r#gen() -> impl PartialOrd + PartialEq + Debug {} +LL + fn r#gen() -> impl PartialOrd + Debug {} | error: aborting due to 1 previous error diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed b/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed index 0822cc7c6350a..8e2ed1bbd18d6 100644 --- a/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed +++ b/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed @@ -1,4 +1,5 @@ #![warn(clippy::doc_lazy_continuation)] +#![allow(clippy::doc_overindented_list_items)] /// 1. nest here /// lazy continuation diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_list.rs b/src/tools/clippy/tests/ui/doc/doc_lazy_list.rs index 068de140e00a0..1da11d8fae26a 100644 --- a/src/tools/clippy/tests/ui/doc/doc_lazy_list.rs +++ b/src/tools/clippy/tests/ui/doc/doc_lazy_list.rs @@ -1,4 +1,5 @@ #![warn(clippy::doc_lazy_continuation)] +#![allow(clippy::doc_overindented_list_items)] /// 1. nest here /// lazy continuation diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr b/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr index b38f43b7555f1..cea6157119f75 100644 --- a/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr +++ b/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr @@ -1,5 +1,5 @@ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:4:5 + --> tests/ui/doc/doc_lazy_list.rs:5:5 | LL | /// lazy continuation | ^ @@ -13,7 +13,7 @@ LL | /// lazy continuation | +++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:9:5 + --> tests/ui/doc/doc_lazy_list.rs:10:5 | LL | /// lazy list continuations don't make warnings with this lint | ^ @@ -25,7 +25,7 @@ LL | /// lazy list continuations don't make warnings with this lint | +++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:11:5 + --> tests/ui/doc/doc_lazy_list.rs:12:5 | LL | /// because they don't have the | ^ @@ -37,7 +37,7 @@ LL | /// because they don't have the | +++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:16:5 + --> tests/ui/doc/doc_lazy_list.rs:17:5 | LL | /// lazy continuation | ^ @@ -49,7 +49,7 @@ LL | /// lazy continuation | ++++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:21:5 + --> tests/ui/doc/doc_lazy_list.rs:22:5 | LL | /// lazy list continuations don't make warnings with this lint | ^ @@ -61,7 +61,7 @@ LL | /// lazy list continuations don't make warnings with this lint | ++++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:23:5 + --> tests/ui/doc/doc_lazy_list.rs:24:5 | LL | /// because they don't have the | ^ @@ -73,7 +73,7 @@ LL | /// because they don't have the | ++++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:28:5 + --> tests/ui/doc/doc_lazy_list.rs:29:5 | LL | /// lazy continuation | ^ @@ -85,7 +85,7 @@ LL | /// lazy continuation | ++++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:33:5 + --> tests/ui/doc/doc_lazy_list.rs:34:5 | LL | /// this will warn on the lazy continuation | ^ @@ -97,7 +97,7 @@ LL | /// this will warn on the lazy continuation | ++++++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:35:5 + --> tests/ui/doc/doc_lazy_list.rs:36:5 | LL | /// and so should this | ^^^^ @@ -109,7 +109,7 @@ LL | /// and so should this | ++ error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:56:5 + --> tests/ui/doc/doc_lazy_list.rs:57:5 | LL | /// 'protocol_descriptors': [ | ^ @@ -121,7 +121,7 @@ LL | /// 'protocol_descriptors': [ | + error: doc list item without indentation - --> tests/ui/doc/doc_lazy_list.rs:75:5 + --> tests/ui/doc/doc_lazy_list.rs:76:5 | LL | /// ] | ^ diff --git a/src/tools/clippy/tests/ui/doc/doc_overindented_list_items.fixed b/src/tools/clippy/tests/ui/doc/doc_overindented_list_items.fixed new file mode 100644 index 0000000000000..940cff48c1e9e --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_overindented_list_items.fixed @@ -0,0 +1,28 @@ +#![warn(clippy::doc_overindented_list_items)] + +#[rustfmt::skip] +/// - first list item +/// overindented line +//~^ ERROR: doc list item overindented +/// this is overindented line too +//~^ ERROR: doc list item overindented +/// - second list item +fn foo() {} + +#[rustfmt::skip] +/// - first list item +/// overindented line +//~^ ERROR: doc list item overindented +/// this is overindented line too +//~^ ERROR: doc list item overindented +/// - second list item +fn bar() {} + +#[rustfmt::skip] +/// * first list item +/// overindented line +//~^ ERROR: doc list item overindented +/// this is overindented line too +//~^ ERROR: doc list item overindented +/// * second list item +fn baz() {} diff --git a/src/tools/clippy/tests/ui/doc/doc_overindented_list_items.rs b/src/tools/clippy/tests/ui/doc/doc_overindented_list_items.rs new file mode 100644 index 0000000000000..77f3ee8a64d42 --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_overindented_list_items.rs @@ -0,0 +1,28 @@ +#![warn(clippy::doc_overindented_list_items)] + +#[rustfmt::skip] +/// - first list item +/// overindented line +//~^ ERROR: doc list item overindented +/// this is overindented line too +//~^ ERROR: doc list item overindented +/// - second list item +fn foo() {} + +#[rustfmt::skip] +/// - first list item +/// overindented line +//~^ ERROR: doc list item overindented +/// this is overindented line too +//~^ ERROR: doc list item overindented +/// - second list item +fn bar() {} + +#[rustfmt::skip] +/// * first list item +/// overindented line +//~^ ERROR: doc list item overindented +/// this is overindented line too +//~^ ERROR: doc list item overindented +/// * second list item +fn baz() {} diff --git a/src/tools/clippy/tests/ui/doc/doc_overindented_list_items.stderr b/src/tools/clippy/tests/ui/doc/doc_overindented_list_items.stderr new file mode 100644 index 0000000000000..ff201ba5eb94a --- /dev/null +++ b/src/tools/clippy/tests/ui/doc/doc_overindented_list_items.stderr @@ -0,0 +1,41 @@ +error: doc list item overindented + --> tests/ui/doc/doc_overindented_list_items.rs:5:5 + | +LL | /// overindented line + | ^^^^^^^ help: try using ` ` (2 spaces) + | + = note: `-D clippy::doc-overindented-list-items` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::doc_overindented_list_items)]` + +error: doc list item overindented + --> tests/ui/doc/doc_overindented_list_items.rs:7:5 + | +LL | /// this is overindented line too + | ^^^^^ help: try using ` ` (2 spaces) + +error: doc list item overindented + --> tests/ui/doc/doc_overindented_list_items.rs:14:7 + | +LL | /// overindented line + | ^^^^^ help: try using ` ` (2 spaces) + +error: doc list item overindented + --> tests/ui/doc/doc_overindented_list_items.rs:16:7 + | +LL | /// this is overindented line too + | ^^^ help: try using ` ` (2 spaces) + +error: doc list item overindented + --> tests/ui/doc/doc_overindented_list_items.rs:23:5 + | +LL | /// overindented line + | ^^^^^^^ help: try using ` ` (2 spaces) + +error: doc list item overindented + --> tests/ui/doc/doc_overindented_list_items.rs:25:5 + | +LL | /// this is overindented line too + | ^^^^^ help: try using ` ` (2 spaces) + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/drain_collect_nostd.fixed b/src/tools/clippy/tests/ui/drain_collect_nostd.fixed new file mode 100644 index 0000000000000..a4ab2956f2a62 --- /dev/null +++ b/src/tools/clippy/tests/ui/drain_collect_nostd.fixed @@ -0,0 +1,8 @@ +#![warn(clippy::drain_collect)] +#![no_std] +extern crate alloc; +use alloc::vec::Vec; + +fn remove_all(v: &mut Vec) -> Vec { + core::mem::take(v) +} diff --git a/src/tools/clippy/tests/ui/drain_collect_nostd.rs b/src/tools/clippy/tests/ui/drain_collect_nostd.rs new file mode 100644 index 0000000000000..a8be1ce6bbd3b --- /dev/null +++ b/src/tools/clippy/tests/ui/drain_collect_nostd.rs @@ -0,0 +1,8 @@ +#![warn(clippy::drain_collect)] +#![no_std] +extern crate alloc; +use alloc::vec::Vec; + +fn remove_all(v: &mut Vec) -> Vec { + v.drain(..).collect() +} diff --git a/src/tools/clippy/tests/ui/drain_collect_nostd.stderr b/src/tools/clippy/tests/ui/drain_collect_nostd.stderr new file mode 100644 index 0000000000000..91b38932fee79 --- /dev/null +++ b/src/tools/clippy/tests/ui/drain_collect_nostd.stderr @@ -0,0 +1,11 @@ +error: you seem to be trying to move all elements into a new `Vec` + --> tests/ui/drain_collect_nostd.rs:7:5 + | +LL | v.drain(..).collect() + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `core::mem::take(v)` + | + = note: `-D clippy::drain-collect` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::drain_collect)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/eta_nostd.fixed b/src/tools/clippy/tests/ui/eta_nostd.fixed new file mode 100644 index 0000000000000..23059c52b67f7 --- /dev/null +++ b/src/tools/clippy/tests/ui/eta_nostd.fixed @@ -0,0 +1,10 @@ +#![warn(clippy::redundant_closure)] +#![no_std] + +extern crate alloc; +use alloc::vec; +use alloc::vec::Vec; + +fn issue_13895() { + let _: Option> = true.then(alloc::vec::Vec::new); +} diff --git a/src/tools/clippy/tests/ui/eta_nostd.rs b/src/tools/clippy/tests/ui/eta_nostd.rs new file mode 100644 index 0000000000000..ae44ac348c64f --- /dev/null +++ b/src/tools/clippy/tests/ui/eta_nostd.rs @@ -0,0 +1,10 @@ +#![warn(clippy::redundant_closure)] +#![no_std] + +extern crate alloc; +use alloc::vec; +use alloc::vec::Vec; + +fn issue_13895() { + let _: Option> = true.then(|| vec![]); +} diff --git a/src/tools/clippy/tests/ui/eta_nostd.stderr b/src/tools/clippy/tests/ui/eta_nostd.stderr new file mode 100644 index 0000000000000..4dfef43efa476 --- /dev/null +++ b/src/tools/clippy/tests/ui/eta_nostd.stderr @@ -0,0 +1,11 @@ +error: redundant closure + --> tests/ui/eta_nostd.rs:9:40 + | +LL | let _: Option> = true.then(|| vec![]); + | ^^^^^^^^^ help: replace the closure with `Vec::new`: `alloc::vec::Vec::new` + | + = note: `-D clippy::redundant-closure` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::redundant_closure)]` + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.fixed b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.fixed index c250162dfb8c0..67da45a348f97 100644 --- a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.fixed +++ b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.fixed @@ -1,6 +1,6 @@ #![warn(clippy::from_iter_instead_of_collect)] #![allow(unused_imports)] -#![allow(clippy::useless_vec)] +#![allow(clippy::useless_vec, clippy::manual_repeat_n)] use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque}; diff --git a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs index 8adbb841c8ba2..423a7454bed7b 100644 --- a/src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs +++ b/src/tools/clippy/tests/ui/from_iter_instead_of_collect.rs @@ -1,6 +1,6 @@ #![warn(clippy::from_iter_instead_of_collect)] #![allow(unused_imports)] -#![allow(clippy::useless_vec)] +#![allow(clippy::useless_vec, clippy::manual_repeat_n)] use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque}; diff --git a/src/tools/clippy/tests/ui/implicit_hasher.fixed b/src/tools/clippy/tests/ui/implicit_hasher.fixed index 2d6dc0274cf2a..971746ae95d73 100644 --- a/src/tools/clippy/tests/ui/implicit_hasher.fixed +++ b/src/tools/clippy/tests/ui/implicit_hasher.fixed @@ -70,7 +70,7 @@ pub fn map(map: &mut HashMap) {} pub fn set(set: &mut HashSet) {} #[inline_macros] -pub mod gen { +pub mod gen_ { use super::*; inline! { impl Foo for HashMap { diff --git a/src/tools/clippy/tests/ui/implicit_hasher.rs b/src/tools/clippy/tests/ui/implicit_hasher.rs index 0a334357bd1b8..b34aa1f81374e 100644 --- a/src/tools/clippy/tests/ui/implicit_hasher.rs +++ b/src/tools/clippy/tests/ui/implicit_hasher.rs @@ -70,7 +70,7 @@ pub fn map(map: &mut HashMap) {} pub fn set(set: &mut HashSet) {} #[inline_macros] -pub mod gen { +pub mod gen_ { use super::*; inline! { impl Foo for HashMap { diff --git a/src/tools/clippy/tests/ui/implicit_hasher.stderr b/src/tools/clippy/tests/ui/implicit_hasher.stderr index 48c6ebc209cf3..442f4789aacfe 100644 --- a/src/tools/clippy/tests/ui/implicit_hasher.stderr +++ b/src/tools/clippy/tests/ui/implicit_hasher.stderr @@ -98,7 +98,7 @@ error: impl for `HashMap` should be generalized over different hashers LL | impl Foo for HashMap { | ^^^^^^^^^^^^^ | - = note: this error originates in the macro `__inline_mac_mod_gen` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `__inline_mac_mod_gen_` (in Nightly builds, run with -Z macro-backtrace for more info) help: add a type parameter for `BuildHasher` | LL ~ impl Foo for HashMap { diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.fixed b/src/tools/clippy/tests/ui/manual_div_ceil.fixed index 1fb1df5b44253..f6eb5a9784acc 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.fixed +++ b/src/tools/clippy/tests/ui/manual_div_ceil.fixed @@ -50,3 +50,14 @@ fn issue_13843() { let _ = 1_000_000_u32.div_ceil(6u32); } + +fn issue_13950() { + let x = 33u32; + let _ = x.div_ceil(8); + let _ = x.div_ceil(8); + + let y = -33i32; + let _ = (y + -8) / -7; + let _ = (-8 + y) / -7; + let _ = (y - 8) / -7; +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.rs b/src/tools/clippy/tests/ui/manual_div_ceil.rs index 4f6d38f0d1450..2f063afe787dd 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.rs +++ b/src/tools/clippy/tests/ui/manual_div_ceil.rs @@ -50,3 +50,14 @@ fn issue_13843() { let _ = (1_000_000 + 6u32 - 1) / 6u32; } + +fn issue_13950() { + let x = 33u32; + let _ = (x + 7) / 8; + let _ = (7 + x) / 8; + + let y = -33i32; + let _ = (y + -8) / -7; + let _ = (-8 + y) / -7; + let _ = (y - 8) / -7; +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.stderr b/src/tools/clippy/tests/ui/manual_div_ceil.stderr index 3d87fe8e04090..0bac5d8ef1c12 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil.stderr +++ b/src/tools/clippy/tests/ui/manual_div_ceil.stderr @@ -85,5 +85,17 @@ error: manually reimplementing `div_ceil` LL | let _ = (1_000_000 + 6u32 - 1) / 6u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `1_000_000_u32.div_ceil(6u32)` -error: aborting due to 14 previous errors +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:56:13 + | +LL | let _ = (x + 7) / 8; + | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:57:13 + | +LL | let _ = (7 + x) / 8; + | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` + +error: aborting due to 16 previous errors diff --git a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed index f32b78aa14d08..01c58151bc946 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed +++ b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed @@ -50,3 +50,14 @@ fn issue_13843() { let _ = 1_000_000_u32.div_ceil(6u32); } + +fn issue_13950() { + let x = 33u32; + let _ = x.div_ceil(8); + let _ = x.div_ceil(8); + + let y = -33i32; + let _ = y.div_ceil(-7); + let _ = y.div_ceil(-7); + let _ = y.div_ceil(-7); +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs index 54d89fcbd4622..048ff401581f7 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs +++ b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs @@ -50,3 +50,14 @@ fn issue_13843() { let _ = (1_000_000 + 6u32 - 1) / 6u32; } + +fn issue_13950() { + let x = 33u32; + let _ = (x + 7) / 8; + let _ = (7 + x) / 8; + + let y = -33i32; + let _ = (y + -8) / -7; + let _ = (-8 + y) / -7; + let _ = (y - 8) / -7; +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr index c5e8c1a687cd8..807cfd82724e5 100644 --- a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr +++ b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr @@ -109,5 +109,35 @@ error: manually reimplementing `div_ceil` LL | let _ = (1_000_000 + 6u32 - 1) / 6u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `1_000_000_u32.div_ceil(6u32)` -error: aborting due to 18 previous errors +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:56:13 + | +LL | let _ = (x + 7) / 8; + | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:57:13 + | +LL | let _ = (7 + x) / 8; + | ^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(8)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:60:13 + | +LL | let _ = (y + -8) / -7; + | ^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `y.div_ceil(-7)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:61:13 + | +LL | let _ = (-8 + y) / -7; + | ^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `y.div_ceil(-7)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:62:13 + | +LL | let _ = (y - 8) / -7; + | ^^^^^^^^^^^^ help: consider using `.div_ceil()`: `y.div_ceil(-7)` + +error: aborting due to 23 previous errors diff --git a/src/tools/clippy/tests/ui/manual_ok_err.fixed b/src/tools/clippy/tests/ui/manual_ok_err.fixed new file mode 100644 index 0000000000000..e7e0464c47877 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_ok_err.fixed @@ -0,0 +1,91 @@ +#![warn(clippy::manual_ok_err)] + +fn funcall() -> Result { + todo!() +} + +fn main() { + let _ = funcall().ok(); + + let _ = funcall().ok(); + + let _ = funcall().err(); + + let _ = funcall().err(); + + let _ = funcall().ok(); + + let _ = funcall().err(); + + #[allow(clippy::redundant_pattern)] + let _ = funcall().ok(); + + struct S; + + impl std::ops::Neg for S { + type Output = Result; + + fn neg(self) -> Self::Output { + funcall() + } + } + + // Suggestion should be properly parenthesized + let _ = (-S).ok(); + + no_lint(); +} + +fn no_lint() { + let _ = match funcall() { + Ok(v) if v > 3 => Some(v), + _ => None, + }; + + let _ = match funcall() { + Err(_) => None, + Ok(3) => None, + Ok(v) => Some(v), + }; + + let _ = match funcall() { + _ => None, + Ok(v) => Some(v), + }; + + let _ = match funcall() { + Err(_) | Ok(3) => None, + Ok(v) => Some(v), + }; + + #[expect(clippy::redundant_pattern)] + let _ = match funcall() { + _v @ _ => None, + Ok(v) => Some(v), + }; + + // Content of `Option` and matching content of `Result` do + // not have the same type. + let _: Option<&dyn std::any::Any> = match Ok::<_, ()>(&1) { + Ok(v) => Some(v), + _ => None, + }; + + let _ = match Ok::<_, ()>(&1) { + _x => None, + Ok(v) => Some(v), + }; + + let _ = match Ok::<_, std::convert::Infallible>(1) { + Ok(3) => None, + Ok(v) => Some(v), + }; +} + +const fn cf(x: Result) -> Option { + // Do not lint in const code + match x { + Ok(v) => Some(v), + Err(_) => None, + } +} diff --git a/src/tools/clippy/tests/ui/manual_ok_err.rs b/src/tools/clippy/tests/ui/manual_ok_err.rs new file mode 100644 index 0000000000000..03ad773f47cf1 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_ok_err.rs @@ -0,0 +1,125 @@ +#![warn(clippy::manual_ok_err)] + +fn funcall() -> Result { + todo!() +} + +fn main() { + let _ = match funcall() { + //~^ manual_ok_err + Ok(v) => Some(v), + Err(_) => None, + }; + + let _ = match funcall() { + //~^ manual_ok_err + Ok(v) => Some(v), + _v => None, + }; + + let _ = match funcall() { + //~^ manual_ok_err + Err(v) => Some(v), + Ok(_) => None, + }; + + let _ = match funcall() { + //~^ manual_ok_err + Err(v) => Some(v), + _v => None, + }; + + let _ = if let Ok(v) = funcall() { + //~^ manual_ok_err + Some(v) + } else { + None + }; + + let _ = if let Err(v) = funcall() { + //~^ manual_ok_err + Some(v) + } else { + None + }; + + #[allow(clippy::redundant_pattern)] + let _ = match funcall() { + //~^ manual_ok_err + Ok(v) => Some(v), + _v @ _ => None, + }; + + struct S; + + impl std::ops::Neg for S { + type Output = Result; + + fn neg(self) -> Self::Output { + funcall() + } + } + + // Suggestion should be properly parenthesized + let _ = match -S { + //~^ manual_ok_err + Ok(v) => Some(v), + _ => None, + }; + + no_lint(); +} + +fn no_lint() { + let _ = match funcall() { + Ok(v) if v > 3 => Some(v), + _ => None, + }; + + let _ = match funcall() { + Err(_) => None, + Ok(3) => None, + Ok(v) => Some(v), + }; + + let _ = match funcall() { + _ => None, + Ok(v) => Some(v), + }; + + let _ = match funcall() { + Err(_) | Ok(3) => None, + Ok(v) => Some(v), + }; + + #[expect(clippy::redundant_pattern)] + let _ = match funcall() { + _v @ _ => None, + Ok(v) => Some(v), + }; + + // Content of `Option` and matching content of `Result` do + // not have the same type. + let _: Option<&dyn std::any::Any> = match Ok::<_, ()>(&1) { + Ok(v) => Some(v), + _ => None, + }; + + let _ = match Ok::<_, ()>(&1) { + _x => None, + Ok(v) => Some(v), + }; + + let _ = match Ok::<_, std::convert::Infallible>(1) { + Ok(3) => None, + Ok(v) => Some(v), + }; +} + +const fn cf(x: Result) -> Option { + // Do not lint in const code + match x { + Ok(v) => Some(v), + Err(_) => None, + } +} diff --git a/src/tools/clippy/tests/ui/manual_ok_err.stderr b/src/tools/clippy/tests/ui/manual_ok_err.stderr new file mode 100644 index 0000000000000..d0d5e2c81e96e --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_ok_err.stderr @@ -0,0 +1,95 @@ +error: manual implementation of `ok` + --> tests/ui/manual_ok_err.rs:8:13 + | +LL | let _ = match funcall() { + | _____________^ +LL | | +LL | | Ok(v) => Some(v), +LL | | Err(_) => None, +LL | | }; + | |_____^ help: replace with: `funcall().ok()` + | + = note: `-D clippy::manual-ok-err` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_ok_err)]` + +error: manual implementation of `ok` + --> tests/ui/manual_ok_err.rs:14:13 + | +LL | let _ = match funcall() { + | _____________^ +LL | | +LL | | Ok(v) => Some(v), +LL | | _v => None, +LL | | }; + | |_____^ help: replace with: `funcall().ok()` + +error: manual implementation of `err` + --> tests/ui/manual_ok_err.rs:20:13 + | +LL | let _ = match funcall() { + | _____________^ +LL | | +LL | | Err(v) => Some(v), +LL | | Ok(_) => None, +LL | | }; + | |_____^ help: replace with: `funcall().err()` + +error: manual implementation of `err` + --> tests/ui/manual_ok_err.rs:26:13 + | +LL | let _ = match funcall() { + | _____________^ +LL | | +LL | | Err(v) => Some(v), +LL | | _v => None, +LL | | }; + | |_____^ help: replace with: `funcall().err()` + +error: manual implementation of `ok` + --> tests/ui/manual_ok_err.rs:32:13 + | +LL | let _ = if let Ok(v) = funcall() { + | _____________^ +LL | | +LL | | Some(v) +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: replace with: `funcall().ok()` + +error: manual implementation of `err` + --> tests/ui/manual_ok_err.rs:39:13 + | +LL | let _ = if let Err(v) = funcall() { + | _____________^ +LL | | +LL | | Some(v) +LL | | } else { +LL | | None +LL | | }; + | |_____^ help: replace with: `funcall().err()` + +error: manual implementation of `ok` + --> tests/ui/manual_ok_err.rs:47:13 + | +LL | let _ = match funcall() { + | _____________^ +LL | | +LL | | Ok(v) => Some(v), +LL | | _v @ _ => None, +LL | | }; + | |_____^ help: replace with: `funcall().ok()` + +error: manual implementation of `ok` + --> tests/ui/manual_ok_err.rs:64:13 + | +LL | let _ = match -S { + | _____________^ +LL | | +LL | | Ok(v) => Some(v), +LL | | _ => None, +LL | | }; + | |_____^ help: replace with: `(-S).ok()` + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_repeat_n.fixed b/src/tools/clippy/tests/ui/manual_repeat_n.fixed new file mode 100644 index 0000000000000..4235b02a89e39 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_repeat_n.fixed @@ -0,0 +1,30 @@ +#![warn(clippy::manual_repeat_n)] + +use std::iter::repeat; + +fn main() { + let _ = std::iter::repeat_n(10, 3); + + let _ = std::iter::repeat_n(String::from("foo"), 4); + + for value in std::iter::repeat_n(5, 3) {} + + let _: Vec<_> = std::iter::repeat_n(String::from("bar"), 10).collect(); + + let _ = std::iter::repeat_n(vec![1, 2], 2); +} + +mod foo_lib { + pub fn iter() -> std::iter::Take> { + todo!() + } +} + +fn foo() { + let _ = match 1 { + 1 => foo_lib::iter(), + // Shouldn't lint because `external_lib::iter` doesn't return `std::iter::RepeatN`. + 2 => std::iter::repeat([1, 2].as_slice()).take(2), + _ => todo!(), + }; +} diff --git a/src/tools/clippy/tests/ui/manual_repeat_n.rs b/src/tools/clippy/tests/ui/manual_repeat_n.rs new file mode 100644 index 0000000000000..dbf9ac6a14afb --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_repeat_n.rs @@ -0,0 +1,30 @@ +#![warn(clippy::manual_repeat_n)] + +use std::iter::repeat; + +fn main() { + let _ = repeat(10).take(3); + + let _ = repeat(String::from("foo")).take(4); + + for value in std::iter::repeat(5).take(3) {} + + let _: Vec<_> = std::iter::repeat(String::from("bar")).take(10).collect(); + + let _ = repeat(vec![1, 2]).take(2); +} + +mod foo_lib { + pub fn iter() -> std::iter::Take> { + todo!() + } +} + +fn foo() { + let _ = match 1 { + 1 => foo_lib::iter(), + // Shouldn't lint because `external_lib::iter` doesn't return `std::iter::RepeatN`. + 2 => std::iter::repeat([1, 2].as_slice()).take(2), + _ => todo!(), + }; +} diff --git a/src/tools/clippy/tests/ui/manual_repeat_n.stderr b/src/tools/clippy/tests/ui/manual_repeat_n.stderr new file mode 100644 index 0000000000000..87395b3f8bf1b --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_repeat_n.stderr @@ -0,0 +1,35 @@ +error: this `repeat().take()` can be written more concisely + --> tests/ui/manual_repeat_n.rs:6:13 + | +LL | let _ = repeat(10).take(3); + | ^^^^^^^^^^^^^^^^^^ help: consider using `repeat_n()` instead: `std::iter::repeat_n(10, 3)` + | + = note: `-D clippy::manual-repeat-n` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_repeat_n)]` + +error: this `repeat().take()` can be written more concisely + --> tests/ui/manual_repeat_n.rs:8:13 + | +LL | let _ = repeat(String::from("foo")).take(4); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `repeat_n()` instead: `std::iter::repeat_n(String::from("foo"), 4)` + +error: this `repeat().take()` can be written more concisely + --> tests/ui/manual_repeat_n.rs:10:18 + | +LL | for value in std::iter::repeat(5).take(3) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `repeat_n()` instead: `std::iter::repeat_n(5, 3)` + +error: this `repeat().take()` can be written more concisely + --> tests/ui/manual_repeat_n.rs:12:21 + | +LL | let _: Vec<_> = std::iter::repeat(String::from("bar")).take(10).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `repeat_n()` instead: `std::iter::repeat_n(String::from("bar"), 10)` + +error: this `repeat().take()` can be written more concisely + --> tests/ui/manual_repeat_n.rs:14:13 + | +LL | let _ = repeat(vec![1, 2]).take(2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `repeat_n()` instead: `std::iter::repeat_n(vec![1, 2], 2)` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_str_repeat.fixed b/src/tools/clippy/tests/ui/manual_str_repeat.fixed index 5f2f1bd9916d8..da6f36f53b0d4 100644 --- a/src/tools/clippy/tests/ui/manual_str_repeat.fixed +++ b/src/tools/clippy/tests/ui/manual_str_repeat.fixed @@ -1,4 +1,4 @@ -#![allow(non_local_definitions)] +#![allow(non_local_definitions, clippy::manual_repeat_n)] #![warn(clippy::manual_str_repeat)] use std::borrow::Cow; diff --git a/src/tools/clippy/tests/ui/manual_str_repeat.rs b/src/tools/clippy/tests/ui/manual_str_repeat.rs index 3e3c7f4db4a27..686ed4fee7d1c 100644 --- a/src/tools/clippy/tests/ui/manual_str_repeat.rs +++ b/src/tools/clippy/tests/ui/manual_str_repeat.rs @@ -1,4 +1,4 @@ -#![allow(non_local_definitions)] +#![allow(non_local_definitions, clippy::manual_repeat_n)] #![warn(clippy::manual_str_repeat)] use std::borrow::Cow; diff --git a/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.fixed b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.fixed index cf520e71a64fc..18716e93d1e9c 100644 --- a/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.fixed +++ b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.fixed @@ -14,7 +14,7 @@ fn do_something_interesting(x: usize, y: usize) -> usize { todo!() } -macro_rules! gen { +macro_rules! r#gen { () => { (0..10).map(|_| do_something()); }; @@ -45,7 +45,7 @@ fn main() { std::iter::repeat_with(|| do_something()).take(1); std::iter::repeat_with(|| do_something()).take((1 << 4) - 0); // These should not be raised - gen!(); + r#gen!(); let lower = 2; let lower_fn = || 2; (lower..upper_fn()).map(|_| do_something()); // Ranges not starting at zero not yet handled diff --git a/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.rs b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.rs index 298eee9ca3fa1..596afd51e61f3 100644 --- a/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.rs +++ b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.rs @@ -14,7 +14,7 @@ fn do_something_interesting(x: usize, y: usize) -> usize { todo!() } -macro_rules! gen { +macro_rules! r#gen { () => { (0..10).map(|_| do_something()); }; @@ -45,7 +45,7 @@ fn main() { (9..=9).map(|_| do_something()); (1..=1 << 4).map(|_| do_something()); // These should not be raised - gen!(); + r#gen!(); let lower = 2; let lower_fn = || 2; (lower..upper_fn()).map(|_| do_something()); // Ranges not starting at zero not yet handled diff --git a/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges_nostd.fixed b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges_nostd.fixed new file mode 100644 index 0000000000000..65e59774905c4 --- /dev/null +++ b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges_nostd.fixed @@ -0,0 +1,8 @@ +#![warn(clippy::map_with_unused_argument_over_ranges)] +#![no_std] +extern crate alloc; +use alloc::vec::Vec; + +fn nostd(v: &mut [i32]) { + let _: Vec<_> = core::iter::repeat_n(3 + 1, 10).collect(); +} diff --git a/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges_nostd.rs b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges_nostd.rs new file mode 100644 index 0000000000000..dda7a69b33f96 --- /dev/null +++ b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges_nostd.rs @@ -0,0 +1,8 @@ +#![warn(clippy::map_with_unused_argument_over_ranges)] +#![no_std] +extern crate alloc; +use alloc::vec::Vec; + +fn nostd(v: &mut [i32]) { + let _: Vec<_> = (0..10).map(|_| 3 + 1).collect(); +} diff --git a/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges_nostd.stderr b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges_nostd.stderr new file mode 100644 index 0000000000000..d47f3d09175b2 --- /dev/null +++ b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges_nostd.stderr @@ -0,0 +1,15 @@ +error: map of a closure that does not depend on its parameter over a range + --> tests/ui/map_with_unused_argument_over_ranges_nostd.rs:7:21 + | +LL | let _: Vec<_> = (0..10).map(|_| 3 + 1).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::map-with-unused-argument-over-ranges` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::map_with_unused_argument_over_ranges)]` +help: remove the explicit range and use `repeat_n` + | +LL | let _: Vec<_> = core::iter::repeat_n(3 + 1, 10).collect(); + | ~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~ + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/match_bool.fixed b/src/tools/clippy/tests/ui/match_bool.fixed new file mode 100644 index 0000000000000..61a8e54fa10ce --- /dev/null +++ b/src/tools/clippy/tests/ui/match_bool.fixed @@ -0,0 +1,58 @@ +#![deny(clippy::match_bool)] +#![allow(clippy::nonminimal_bool, clippy::eq_op)] + +fn match_bool() { + let test: bool = true; + + if test { 0 } else { 42 }; + + let option = 1; + if option == 1 { 1 } else { 0 }; + + if !test { + println!("Noooo!"); + }; + + if !test { + println!("Noooo!"); + }; + + if !(test && test) { + println!("Noooo!"); + }; + + if !test { + println!("Noooo!"); + } else { + println!("Yes!"); + }; + + // Not linted + match option { + 1..=10 => 1, + 11..=20 => 2, + _ => 3, + }; + + // Don't lint + let _ = match test { + #[cfg(feature = "foo")] + true if option == 5 => 10, + true => 0, + false => 1, + }; + + let _ = if test && option == 5 { 10 } else { 1 }; + + let _ = if !test && option == 5 { 10 } else { 1 }; + + if test && option == 5 { println!("Hello") }; + + if !(test && option == 5) { println!("Hello") }; + + if !test && option == 5 { println!("Hello") }; + + if !(!test && option == 5) { println!("Hello") }; +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/match_bool.rs b/src/tools/clippy/tests/ui/match_bool.rs index f84af393e47fa..9c81d2917869b 100644 --- a/src/tools/clippy/tests/ui/match_bool.rs +++ b/src/tools/clippy/tests/ui/match_bool.rs @@ -1,24 +1,24 @@ -//@no-rustfix: overlapping suggestions #![deny(clippy::match_bool)] +#![allow(clippy::nonminimal_bool, clippy::eq_op)] fn match_bool() { let test: bool = true; match test { - //~^ ERROR: you seem to be trying to match on a boolean expression + //~^ ERROR: `match` on a boolean expression true => 0, false => 42, }; let option = 1; match option == 1 { - //~^ ERROR: you seem to be trying to match on a boolean expression + //~^ ERROR: `match` on a boolean expression true => 1, false => 0, }; match test { - //~^ ERROR: you seem to be trying to match on a boolean expression + //~^ ERROR: `match` on a boolean expression true => (), false => { println!("Noooo!"); @@ -26,7 +26,7 @@ fn match_bool() { }; match test { - //~^ ERROR: you seem to be trying to match on a boolean expression + //~^ ERROR: `match` on a boolean expression false => { println!("Noooo!"); }, @@ -34,11 +34,7 @@ fn match_bool() { }; match test && test { - //~^ ERROR: this boolean expression can be simplified - //~| NOTE: `-D clippy::nonminimal-bool` implied by `-D warnings` - //~| ERROR: you seem to be trying to match on a boolean expression - //~| ERROR: equal expressions as operands to `&&` - //~| NOTE: `#[deny(clippy::eq_op)]` on by default + //~^ ERROR: `match` on a boolean expression false => { println!("Noooo!"); }, @@ -46,7 +42,7 @@ fn match_bool() { }; match test { - //~^ ERROR: you seem to be trying to match on a boolean expression + //~^ ERROR: `match` on a boolean expression false => { println!("Noooo!"); }, @@ -69,6 +65,42 @@ fn match_bool() { true => 0, false => 1, }; + + let _ = match test { + //~^ ERROR: `match` on a boolean expression + true if option == 5 => 10, + _ => 1, + }; + + let _ = match test { + //~^ ERROR: `match` on a boolean expression + false if option == 5 => 10, + _ => 1, + }; + + match test { + //~^ ERROR: `match` on a boolean expression + true if option == 5 => println!("Hello"), + _ => (), + }; + + match test { + //~^ ERROR: `match` on a boolean expression + true if option == 5 => (), + _ => println!("Hello"), + }; + + match test { + //~^ ERROR: `match` on a boolean expression + false if option == 5 => println!("Hello"), + _ => (), + }; + + match test { + //~^ ERROR: `match` on a boolean expression + false if option == 5 => (), + _ => println!("Hello"), + }; } fn main() {} diff --git a/src/tools/clippy/tests/ui/match_bool.stderr b/src/tools/clippy/tests/ui/match_bool.stderr index fb24e67eceefd..a4e504a0a82e4 100644 --- a/src/tools/clippy/tests/ui/match_bool.stderr +++ b/src/tools/clippy/tests/ui/match_bool.stderr @@ -1,13 +1,4 @@ -error: this boolean expression can be simplified - --> tests/ui/match_bool.rs:36:11 - | -LL | match test && test { - | ^^^^^^^^^^^^ help: try: `test` - | - = note: `-D clippy::nonminimal-bool` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::nonminimal_bool)]` - -error: you seem to be trying to match on a boolean expression +error: `match` on a boolean expression --> tests/ui/match_bool.rs:7:5 | LL | / match test { @@ -18,12 +9,12 @@ LL | | }; | |_____^ help: consider using an `if`/`else` expression: `if test { 0 } else { 42 }` | note: the lint level is defined here - --> tests/ui/match_bool.rs:2:9 + --> tests/ui/match_bool.rs:1:9 | LL | #![deny(clippy::match_bool)] | ^^^^^^^^^^^^^^^^^^ -error: you seem to be trying to match on a boolean expression +error: `match` on a boolean expression --> tests/ui/match_bool.rs:14:5 | LL | / match option == 1 { @@ -33,7 +24,7 @@ LL | | false => 0, LL | | }; | |_____^ help: consider using an `if`/`else` expression: `if option == 1 { 1 } else { 0 }` -error: you seem to be trying to match on a boolean expression +error: `match` on a boolean expression --> tests/ui/match_bool.rs:20:5 | LL | / match test { @@ -52,7 +43,7 @@ LL + println!("Noooo!"); LL ~ }; | -error: you seem to be trying to match on a boolean expression +error: `match` on a boolean expression --> tests/ui/match_bool.rs:28:5 | LL | / match test { @@ -71,11 +62,14 @@ LL + println!("Noooo!"); LL ~ }; | -error: you seem to be trying to match on a boolean expression +error: `match` on a boolean expression --> tests/ui/match_bool.rs:36:5 | LL | / match test && test { -... | +LL | | +LL | | false => { +LL | | println!("Noooo!"); +LL | | }, LL | | _ => (), LL | | }; | |_____^ @@ -87,16 +81,8 @@ LL + println!("Noooo!"); LL ~ }; | -error: equal expressions as operands to `&&` - --> tests/ui/match_bool.rs:36:11 - | -LL | match test && test { - | ^^^^^^^^^^^^ - | - = note: `#[deny(clippy::eq_op)]` on by default - -error: you seem to be trying to match on a boolean expression - --> tests/ui/match_bool.rs:48:5 +error: `match` on a boolean expression + --> tests/ui/match_bool.rs:44:5 | LL | / match test { LL | | @@ -109,12 +95,74 @@ LL | | }; | help: consider using an `if`/`else` expression | -LL ~ if test { -LL + println!("Yes!"); -LL + } else { +LL ~ if !test { LL + println!("Noooo!"); +LL + } else { +LL + println!("Yes!"); LL ~ }; | -error: aborting due to 8 previous errors +error: `match` on a boolean expression + --> tests/ui/match_bool.rs:69:13 + | +LL | let _ = match test { + | _____________^ +LL | | +LL | | true if option == 5 => 10, +LL | | _ => 1, +LL | | }; + | |_____^ help: consider using an `if`/`else` expression: `if test && option == 5 { 10 } else { 1 }` + +error: `match` on a boolean expression + --> tests/ui/match_bool.rs:75:13 + | +LL | let _ = match test { + | _____________^ +LL | | +LL | | false if option == 5 => 10, +LL | | _ => 1, +LL | | }; + | |_____^ help: consider using an `if`/`else` expression: `if !test && option == 5 { 10 } else { 1 }` + +error: `match` on a boolean expression + --> tests/ui/match_bool.rs:81:5 + | +LL | / match test { +LL | | +LL | | true if option == 5 => println!("Hello"), +LL | | _ => (), +LL | | }; + | |_____^ help: consider using an `if`/`else` expression: `if test && option == 5 { println!("Hello") }` + +error: `match` on a boolean expression + --> tests/ui/match_bool.rs:87:5 + | +LL | / match test { +LL | | +LL | | true if option == 5 => (), +LL | | _ => println!("Hello"), +LL | | }; + | |_____^ help: consider using an `if`/`else` expression: `if !(test && option == 5) { println!("Hello") }` + +error: `match` on a boolean expression + --> tests/ui/match_bool.rs:93:5 + | +LL | / match test { +LL | | +LL | | false if option == 5 => println!("Hello"), +LL | | _ => (), +LL | | }; + | |_____^ help: consider using an `if`/`else` expression: `if !test && option == 5 { println!("Hello") }` + +error: `match` on a boolean expression + --> tests/ui/match_bool.rs:99:5 + | +LL | / match test { +LL | | +LL | | false if option == 5 => (), +LL | | _ => println!("Hello"), +LL | | }; + | |_____^ help: consider using an `if`/`else` expression: `if !(!test && option == 5) { println!("Hello") }` + +error: aborting due to 12 previous errors diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs index fdde68790a8c7..cdfdcd5007a82 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs @@ -210,3 +210,9 @@ mod with_ty_alias { let _: Foo = 1; } } + +// Do not lint because mutable references in const functions are unstable in 1.82 +#[clippy::msrv = "1.82"] +fn mut_add(x: &mut i32) { + *x += 1; +} diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed index dd9dedcdd0441..689060468c574 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed @@ -213,3 +213,8 @@ mod extern_fn { const extern "system-unwind" fn system_unwind() {} //~^ ERROR: this could be a `const fn` } + +const fn mut_add(x: &mut i32) { + //~^ ERROR: this could be a `const fn` + *x += 1; +} diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs index f974478540cd5..492c47d7e49b5 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs @@ -213,3 +213,8 @@ mod extern_fn { extern "system-unwind" fn system_unwind() {} //~^ ERROR: this could be a `const fn` } + +fn mut_add(x: &mut i32) { + //~^ ERROR: this could be a `const fn` + *x += 1; +} diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr index 33836bdfe9f8a..a06703e2ebf23 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr @@ -316,5 +316,19 @@ help: make the function `const` LL | const extern "system-unwind" fn system_unwind() {} | +++++ -error: aborting due to 24 previous errors +error: this could be a `const fn` + --> tests/ui/missing_const_for_fn/could_be_const.rs:217:1 + | +LL | / fn mut_add(x: &mut i32) { +LL | | +LL | | *x += 1; +LL | | } + | |_^ + | +help: make the function `const` + | +LL | const fn mut_add(x: &mut i32) { + | +++++ + +error: aborting due to 25 previous errors diff --git a/src/tools/clippy/tests/ui/needless_as_bytes.fixed b/src/tools/clippy/tests/ui/needless_as_bytes.fixed index 042342311fdf1..74b4ba5be7989 100644 --- a/src/tools/clippy/tests/ui/needless_as_bytes.fixed +++ b/src/tools/clippy/tests/ui/needless_as_bytes.fixed @@ -1,5 +1,6 @@ #![warn(clippy::needless_as_bytes)] #![allow(clippy::const_is_empty)] +#![feature(exact_size_is_empty)] struct S; @@ -7,6 +8,9 @@ impl S { fn as_bytes(&self) -> &[u8] { &[] } + fn bytes(&self) -> &[u8] { + &[] + } } fn main() { @@ -15,6 +19,11 @@ fn main() { println!("len = {}", "some string".len()); //~^ needless_as_bytes } + if "some string".is_empty() { + //~^ needless_as_bytes + println!("len = {}", "some string".len()); + //~^ needless_as_bytes + } let s = String::from("yet another string"); if s.is_empty() { @@ -22,6 +31,11 @@ fn main() { println!("len = {}", s.len()); //~^ needless_as_bytes } + if s.is_empty() { + //~^ needless_as_bytes + println!("len = {}", s.len()); + //~^ needless_as_bytes + } // Do not lint let _ = S.as_bytes().is_empty(); @@ -36,6 +50,18 @@ fn main() { }; } m!(1).as_bytes().len(); + let _ = S.bytes().is_empty(); + let _ = S.bytes().len(); + let _ = (&String::new() as &dyn Bytes).bytes().len(); + macro_rules! m { + (1) => { + "" + }; + (2) => { + "".bytes() + }; + } + m!(1).bytes().len(); m!(2).len(); } @@ -48,3 +74,13 @@ impl AsBytes for String { &[] } } + +pub trait Bytes { + fn bytes(&self) -> &[u8]; +} + +impl Bytes for String { + fn bytes(&self) -> &[u8] { + &[] + } +} diff --git a/src/tools/clippy/tests/ui/needless_as_bytes.rs b/src/tools/clippy/tests/ui/needless_as_bytes.rs index c481e041e0abf..ffcce60bbbef2 100644 --- a/src/tools/clippy/tests/ui/needless_as_bytes.rs +++ b/src/tools/clippy/tests/ui/needless_as_bytes.rs @@ -1,5 +1,6 @@ #![warn(clippy::needless_as_bytes)] #![allow(clippy::const_is_empty)] +#![feature(exact_size_is_empty)] struct S; @@ -7,6 +8,9 @@ impl S { fn as_bytes(&self) -> &[u8] { &[] } + fn bytes(&self) -> &[u8] { + &[] + } } fn main() { @@ -15,6 +19,11 @@ fn main() { println!("len = {}", "some string".as_bytes().len()); //~^ needless_as_bytes } + if "some string".bytes().is_empty() { + //~^ needless_as_bytes + println!("len = {}", "some string".bytes().len()); + //~^ needless_as_bytes + } let s = String::from("yet another string"); if s.as_bytes().is_empty() { @@ -22,6 +31,11 @@ fn main() { println!("len = {}", s.as_bytes().len()); //~^ needless_as_bytes } + if s.bytes().is_empty() { + //~^ needless_as_bytes + println!("len = {}", s.bytes().len()); + //~^ needless_as_bytes + } // Do not lint let _ = S.as_bytes().is_empty(); @@ -36,6 +50,18 @@ fn main() { }; } m!(1).as_bytes().len(); + let _ = S.bytes().is_empty(); + let _ = S.bytes().len(); + let _ = (&String::new() as &dyn Bytes).bytes().len(); + macro_rules! m { + (1) => { + "" + }; + (2) => { + "".bytes() + }; + } + m!(1).bytes().len(); m!(2).len(); } @@ -48,3 +74,13 @@ impl AsBytes for String { &[] } } + +pub trait Bytes { + fn bytes(&self) -> &[u8]; +} + +impl Bytes for String { + fn bytes(&self) -> &[u8] { + &[] + } +} diff --git a/src/tools/clippy/tests/ui/needless_as_bytes.stderr b/src/tools/clippy/tests/ui/needless_as_bytes.stderr index 3391238a142bf..138c6630ae7d9 100644 --- a/src/tools/clippy/tests/ui/needless_as_bytes.stderr +++ b/src/tools/clippy/tests/ui/needless_as_bytes.stderr @@ -1,5 +1,5 @@ -error: needless call to `as_bytes()` - --> tests/ui/needless_as_bytes.rs:13:8 +error: needless call to `as_bytes` + --> tests/ui/needless_as_bytes.rs:17:8 | LL | if "some string".as_bytes().is_empty() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: `is_empty()` can be called directly on strings: `"some string".is_empty()` @@ -7,23 +7,47 @@ LL | if "some string".as_bytes().is_empty() { = note: `-D clippy::needless-as-bytes` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::needless_as_bytes)]` -error: needless call to `as_bytes()` - --> tests/ui/needless_as_bytes.rs:15:30 +error: needless call to `as_bytes` + --> tests/ui/needless_as_bytes.rs:19:30 | LL | println!("len = {}", "some string".as_bytes().len()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: `len()` can be called directly on strings: `"some string".len()` -error: needless call to `as_bytes()` - --> tests/ui/needless_as_bytes.rs:20:8 +error: needless call to `bytes` + --> tests/ui/needless_as_bytes.rs:22:8 + | +LL | if "some string".bytes().is_empty() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: `is_empty()` can be called directly on strings: `"some string".is_empty()` + +error: needless call to `bytes` + --> tests/ui/needless_as_bytes.rs:24:30 + | +LL | println!("len = {}", "some string".bytes().len()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: `len()` can be called directly on strings: `"some string".len()` + +error: needless call to `as_bytes` + --> tests/ui/needless_as_bytes.rs:29:8 | LL | if s.as_bytes().is_empty() { | ^^^^^^^^^^^^^^^^^^^^^^^ help: `is_empty()` can be called directly on strings: `s.is_empty()` -error: needless call to `as_bytes()` - --> tests/ui/needless_as_bytes.rs:22:30 +error: needless call to `as_bytes` + --> tests/ui/needless_as_bytes.rs:31:30 | LL | println!("len = {}", s.as_bytes().len()); | ^^^^^^^^^^^^^^^^^^ help: `len()` can be called directly on strings: `s.len()` -error: aborting due to 4 previous errors +error: needless call to `bytes` + --> tests/ui/needless_as_bytes.rs:34:8 + | +LL | if s.bytes().is_empty() { + | ^^^^^^^^^^^^^^^^^^^^ help: `is_empty()` can be called directly on strings: `s.is_empty()` + +error: needless call to `bytes` + --> tests/ui/needless_as_bytes.rs:36:30 + | +LL | println!("len = {}", s.bytes().len()); + | ^^^^^^^^^^^^^^^ help: `len()` can be called directly on strings: `s.len()` + +error: aborting due to 8 previous errors diff --git a/src/tools/clippy/tests/ui/needless_late_init.fixed b/src/tools/clippy/tests/ui/needless_late_init.fixed index 6db870490445e..b4bd53ce7bf7f 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.fixed +++ b/src/tools/clippy/tests/ui/needless_late_init.fixed @@ -270,3 +270,14 @@ fn issue8911() -> u32 { 3 } + +macro_rules! issue13776_mac { + ($var:expr, $val:literal) => { + $var = $val; + }; +} + +fn issue13776() { + let x; + issue13776_mac!(x, 10); // should not lint +} diff --git a/src/tools/clippy/tests/ui/needless_late_init.rs b/src/tools/clippy/tests/ui/needless_late_init.rs index c1e86212a08b8..e25483625a68c 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.rs +++ b/src/tools/clippy/tests/ui/needless_late_init.rs @@ -270,3 +270,14 @@ fn issue8911() -> u32 { 3 } + +macro_rules! issue13776_mac { + ($var:expr, $val:literal) => { + $var = $val; + }; +} + +fn issue13776() { + let x; + issue13776_mac!(x, 10); // should not lint +} diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.fixed b/src/tools/clippy/tests/ui/needless_lifetimes.fixed index 8196d608abd21..86cf9a9cdb635 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.fixed +++ b/src/tools/clippy/tests/ui/needless_lifetimes.fixed @@ -6,6 +6,7 @@ clippy::boxed_local, clippy::extra_unused_type_parameters, clippy::needless_pass_by_value, + clippy::redundant_allocation, clippy::unnecessary_wraps, dyn_drop, clippy::get_first @@ -443,11 +444,20 @@ mod issue7296 { fn implicit_mut(&mut self) -> &() { &() } - - fn explicit<'a>(self: &'a Arc) -> &'a () { + #[clippy::msrv = "1.81"] + fn explicit(self: &Arc) -> &() { + &() + } + #[clippy::msrv = "1.81"] + fn explicit_mut(self: &mut Rc) -> &() { + &() + } + #[clippy::msrv = "1.80"] + fn explicit_older<'a>(self: &'a Arc) -> &'a () { &() } - fn explicit_mut<'a>(self: &'a mut Rc) -> &'a () { + #[clippy::msrv = "1.80"] + fn explicit_mut_older<'a>(self: &'a mut Rc) -> &'a () { &() } @@ -462,8 +472,16 @@ mod issue7296 { &() } - fn explicit<'a>(self: &'a Arc) -> &'a (); - fn explicit_provided<'a>(self: &'a Arc) -> &'a () { + #[clippy::msrv = "1.81"] + fn explicit(self: &Arc) -> &(); + #[clippy::msrv = "1.81"] + fn explicit_provided(self: &Arc) -> &() { + &() + } + #[clippy::msrv = "1.80"] + fn explicit_older<'a>(self: &'a Arc) -> &'a (); + #[clippy::msrv = "1.80"] + fn explicit_provided_older<'a>(self: &'a Arc) -> &'a () { &() } @@ -576,4 +594,85 @@ mod issue13749bis { impl<'a, T: 'a> Generic {} } +mod issue13923 { + struct Py<'py> { + data: &'py str, + } + + enum Content<'t, 'py> { + Py(Py<'py>), + T1(&'t str), + T2(&'t str), + } + + enum ContentString<'t> { + T1(&'t str), + T2(&'t str), + } + + impl<'t, 'py> ContentString<'t> { + // `'py` cannot be elided + fn map_content1(self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, 'py> { + match self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(content) => Content::T2(f(content)), + } + } + } + + impl<'t> ContentString<'t> { + // `'py` can be elided because of `&self` + fn map_content2(&self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, '_> { + match self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(content) => Content::T2(f(content)), + } + } + } + + impl<'t> ContentString<'t> { + // `'py` can be elided because of `&'_ self` + fn map_content3(&'_ self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, '_> { + match self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(content) => Content::T2(f(content)), + } + } + } + + impl<'t, 'py> ContentString<'t> { + // `'py` should not be elided as the default lifetime, even if working, could be named as `'t` + fn map_content4(self, f: impl FnOnce(&'t str) -> &'t str, o: &'t str) -> Content<'t, 'py> { + match self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(_) => Content::T2(o), + } + } + } + + impl<'t> ContentString<'t> { + // `'py` can be elided because of `&Self` + fn map_content5( + self: std::pin::Pin<&Self>, + f: impl FnOnce(&'t str) -> &'t str, + o: &'t str, + ) -> Content<'t, '_> { + match *self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(_) => Content::T2(o), + } + } + } + + struct Cx<'a, 'b> { + a: &'a u32, + b: &'b u32, + } + + // `'c` cannot be elided because we have several input lifetimes + fn one_explicit<'b>(x: Cx<'_, 'b>) -> &'b u32 { + x.b + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.rs b/src/tools/clippy/tests/ui/needless_lifetimes.rs index b55dd99c46d05..1ee0f4c6092ae 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.rs +++ b/src/tools/clippy/tests/ui/needless_lifetimes.rs @@ -6,6 +6,7 @@ clippy::boxed_local, clippy::extra_unused_type_parameters, clippy::needless_pass_by_value, + clippy::redundant_allocation, clippy::unnecessary_wraps, dyn_drop, clippy::get_first @@ -443,13 +444,22 @@ mod issue7296 { fn implicit_mut<'a>(&'a mut self) -> &'a () { &() } - + #[clippy::msrv = "1.81"] fn explicit<'a>(self: &'a Arc) -> &'a () { &() } + #[clippy::msrv = "1.81"] fn explicit_mut<'a>(self: &'a mut Rc) -> &'a () { &() } + #[clippy::msrv = "1.80"] + fn explicit_older<'a>(self: &'a Arc) -> &'a () { + &() + } + #[clippy::msrv = "1.80"] + fn explicit_mut_older<'a>(self: &'a mut Rc) -> &'a () { + &() + } fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a () { &() @@ -462,10 +472,18 @@ mod issue7296 { &() } + #[clippy::msrv = "1.81"] fn explicit<'a>(self: &'a Arc) -> &'a (); + #[clippy::msrv = "1.81"] fn explicit_provided<'a>(self: &'a Arc) -> &'a () { &() } + #[clippy::msrv = "1.80"] + fn explicit_older<'a>(self: &'a Arc) -> &'a (); + #[clippy::msrv = "1.80"] + fn explicit_provided_older<'a>(self: &'a Arc) -> &'a () { + &() + } fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a (); fn lifetime_elsewhere_provided<'a>(self: Box, here: &'a ()) -> &'a () { @@ -576,4 +594,85 @@ mod issue13749bis { impl<'a, T: 'a> Generic {} } +mod issue13923 { + struct Py<'py> { + data: &'py str, + } + + enum Content<'t, 'py> { + Py(Py<'py>), + T1(&'t str), + T2(&'t str), + } + + enum ContentString<'t> { + T1(&'t str), + T2(&'t str), + } + + impl<'t, 'py> ContentString<'t> { + // `'py` cannot be elided + fn map_content1(self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, 'py> { + match self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(content) => Content::T2(f(content)), + } + } + } + + impl<'t, 'py> ContentString<'t> { + // `'py` can be elided because of `&self` + fn map_content2(&self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, 'py> { + match self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(content) => Content::T2(f(content)), + } + } + } + + impl<'t, 'py> ContentString<'t> { + // `'py` can be elided because of `&'_ self` + fn map_content3(&'_ self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, 'py> { + match self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(content) => Content::T2(f(content)), + } + } + } + + impl<'t, 'py> ContentString<'t> { + // `'py` should not be elided as the default lifetime, even if working, could be named as `'t` + fn map_content4(self, f: impl FnOnce(&'t str) -> &'t str, o: &'t str) -> Content<'t, 'py> { + match self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(_) => Content::T2(o), + } + } + } + + impl<'t, 'py> ContentString<'t> { + // `'py` can be elided because of `&Self` + fn map_content5( + self: std::pin::Pin<&Self>, + f: impl FnOnce(&'t str) -> &'t str, + o: &'t str, + ) -> Content<'t, 'py> { + match *self { + Self::T1(content) => Content::T1(f(content)), + Self::T2(_) => Content::T2(o), + } + } + } + + struct Cx<'a, 'b> { + a: &'a u32, + b: &'b u32, + } + + // `'c` cannot be elided because we have several input lifetimes + fn one_explicit<'b>(x: Cx<'_, 'b>) -> &'b u32 { + &x.b + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_lifetimes.stderr b/src/tools/clippy/tests/ui/needless_lifetimes.stderr index e56c914cc86d6..465d529bf16c5 100644 --- a/src/tools/clippy/tests/ui/needless_lifetimes.stderr +++ b/src/tools/clippy/tests/ui/needless_lifetimes.stderr @@ -1,5 +1,5 @@ error: elided lifetime has a name - --> tests/ui/needless_lifetimes.rs:266:52 + --> tests/ui/needless_lifetimes.rs:267:52 | LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { | -- ^ this elided lifetime gets resolved as `'a` @@ -10,7 +10,7 @@ LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { = help: to override `-D warnings` add `#[allow(elided_named_lifetimes)]` error: the following explicit lifetimes could be elided: 'a, 'b - --> tests/ui/needless_lifetimes.rs:17:23 + --> tests/ui/needless_lifetimes.rs:18:23 | LL | fn distinct_lifetimes<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: u8) {} | ^^ ^^ ^^ ^^ @@ -24,7 +24,7 @@ LL + fn distinct_lifetimes(_x: &u8, _y: &u8, _z: u8) {} | error: the following explicit lifetimes could be elided: 'a, 'b - --> tests/ui/needless_lifetimes.rs:19:24 + --> tests/ui/needless_lifetimes.rs:20:24 | LL | fn distinct_and_static<'a, 'b>(_x: &'a u8, _y: &'b u8, _z: &'static u8) {} | ^^ ^^ ^^ ^^ @@ -36,7 +36,7 @@ LL + fn distinct_and_static(_x: &u8, _y: &u8, _z: &'static u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:29:15 + --> tests/ui/needless_lifetimes.rs:30:15 | LL | fn in_and_out<'a>(x: &'a u8, _y: u8) -> &'a u8 { | ^^ ^^ ^^ @@ -48,7 +48,7 @@ LL + fn in_and_out(x: &u8, _y: u8) -> &u8 { | error: the following explicit lifetimes could be elided: 'b - --> tests/ui/needless_lifetimes.rs:41:31 + --> tests/ui/needless_lifetimes.rs:42:31 | LL | fn multiple_in_and_out_2a<'a, 'b>(x: &'a u8, _y: &'b u8) -> &'a u8 { | ^^ ^^ @@ -60,7 +60,7 @@ LL + fn multiple_in_and_out_2a<'a>(x: &'a u8, _y: &u8) -> &'a u8 { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:48:27 + --> tests/ui/needless_lifetimes.rs:49:27 | LL | fn multiple_in_and_out_2b<'a, 'b>(_x: &'a u8, y: &'b u8) -> &'b u8 { | ^^ ^^ @@ -72,7 +72,7 @@ LL + fn multiple_in_and_out_2b<'b>(_x: &u8, y: &'b u8) -> &'b u8 { | error: the following explicit lifetimes could be elided: 'b - --> tests/ui/needless_lifetimes.rs:65:26 + --> tests/ui/needless_lifetimes.rs:66:26 | LL | fn deep_reference_1a<'a, 'b>(x: &'a u8, _y: &'b u8) -> Result<&'a u8, ()> { | ^^ ^^ @@ -84,7 +84,7 @@ LL + fn deep_reference_1a<'a>(x: &'a u8, _y: &u8) -> Result<&'a u8, ()> { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:72:22 + --> tests/ui/needless_lifetimes.rs:73:22 | LL | fn deep_reference_1b<'a, 'b>(_x: &'a u8, y: &'b u8) -> Result<&'b u8, ()> { | ^^ ^^ @@ -96,7 +96,7 @@ LL + fn deep_reference_1b<'b>(_x: &u8, y: &'b u8) -> Result<&'b u8, ()> { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:81:21 + --> tests/ui/needless_lifetimes.rs:82:21 | LL | fn deep_reference_3<'a>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> { | ^^ ^^ ^^ @@ -108,7 +108,7 @@ LL + fn deep_reference_3(x: &u8, _y: u8) -> Result<&u8, ()> { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:86:28 + --> tests/ui/needless_lifetimes.rs:87:28 | LL | fn where_clause_without_lt<'a, T>(x: &'a u8, _y: u8) -> Result<&'a u8, ()> | ^^ ^^ ^^ @@ -120,7 +120,7 @@ LL + fn where_clause_without_lt(x: &u8, _y: u8) -> Result<&u8, ()> | error: the following explicit lifetimes could be elided: 'a, 'b - --> tests/ui/needless_lifetimes.rs:98:21 + --> tests/ui/needless_lifetimes.rs:99:21 | LL | fn lifetime_param_2<'a, 'b>(_x: Ref<'a>, _y: &'b u8) {} | ^^ ^^ ^^ ^^ @@ -132,7 +132,7 @@ LL + fn lifetime_param_2(_x: Ref<'_>, _y: &u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:122:15 + --> tests/ui/needless_lifetimes.rs:123:15 | LL | fn fn_bound_2<'a, F, I>(_m: Lt<'a, I>, _f: F) -> Lt<'a, I> | ^^ ^^ ^^ @@ -144,7 +144,7 @@ LL + fn fn_bound_2(_m: Lt<'_, I>, _f: F) -> Lt<'_, I> | error: the following explicit lifetimes could be elided: 's - --> tests/ui/needless_lifetimes.rs:152:21 + --> tests/ui/needless_lifetimes.rs:153:21 | LL | fn self_and_out<'s>(&'s self) -> &'s u8 { | ^^ ^^ ^^ @@ -156,7 +156,7 @@ LL + fn self_and_out(&self) -> &u8 { | error: the following explicit lifetimes could be elided: 't - --> tests/ui/needless_lifetimes.rs:159:30 + --> tests/ui/needless_lifetimes.rs:160:30 | LL | fn self_and_in_out_1<'s, 't>(&'s self, _x: &'t u8) -> &'s u8 { | ^^ ^^ @@ -168,7 +168,7 @@ LL + fn self_and_in_out_1<'s>(&'s self, _x: &u8) -> &'s u8 { | error: the following explicit lifetimes could be elided: 's - --> tests/ui/needless_lifetimes.rs:166:26 + --> tests/ui/needless_lifetimes.rs:167:26 | LL | fn self_and_in_out_2<'s, 't>(&'s self, x: &'t u8) -> &'t u8 { | ^^ ^^ @@ -180,7 +180,7 @@ LL + fn self_and_in_out_2<'t>(&self, x: &'t u8) -> &'t u8 { | error: the following explicit lifetimes could be elided: 's, 't - --> tests/ui/needless_lifetimes.rs:170:29 + --> tests/ui/needless_lifetimes.rs:171:29 | LL | fn distinct_self_and_in<'s, 't>(&'s self, _x: &'t u8) {} | ^^ ^^ ^^ ^^ @@ -192,7 +192,7 @@ LL + fn distinct_self_and_in(&self, _x: &u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:189:19 + --> tests/ui/needless_lifetimes.rs:190:19 | LL | fn struct_with_lt<'a>(_foo: Foo<'a>) -> &'a str { | ^^ ^^ ^^ @@ -204,7 +204,7 @@ LL + fn struct_with_lt(_foo: Foo<'_>) -> &str { | error: the following explicit lifetimes could be elided: 'b - --> tests/ui/needless_lifetimes.rs:207:25 + --> tests/ui/needless_lifetimes.rs:208:25 | LL | fn struct_with_lt4a<'a, 'b>(_foo: &'a Foo<'b>) -> &'a str { | ^^ ^^ @@ -216,7 +216,7 @@ LL + fn struct_with_lt4a<'a>(_foo: &'a Foo<'_>) -> &'a str { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:215:21 + --> tests/ui/needless_lifetimes.rs:216:21 | LL | fn struct_with_lt4b<'a, 'b>(_foo: &'a Foo<'b>) -> &'b str { | ^^ ^^ @@ -228,7 +228,7 @@ LL + fn struct_with_lt4b<'b>(_foo: &Foo<'b>) -> &'b str { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:230:22 + --> tests/ui/needless_lifetimes.rs:231:22 | LL | fn trait_obj_elided2<'a>(_arg: &'a dyn Drop) -> &'a str { | ^^ ^^ ^^ @@ -240,7 +240,7 @@ LL + fn trait_obj_elided2(_arg: &dyn Drop) -> &str { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:236:18 + --> tests/ui/needless_lifetimes.rs:237:18 | LL | fn alias_with_lt<'a>(_foo: FooAlias<'a>) -> &'a str { | ^^ ^^ ^^ @@ -252,7 +252,7 @@ LL + fn alias_with_lt(_foo: FooAlias<'_>) -> &str { | error: the following explicit lifetimes could be elided: 'b - --> tests/ui/needless_lifetimes.rs:254:24 + --> tests/ui/needless_lifetimes.rs:255:24 | LL | fn alias_with_lt4a<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'a str { | ^^ ^^ @@ -264,7 +264,7 @@ LL + fn alias_with_lt4a<'a>(_foo: &'a FooAlias<'_>) -> &'a str { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:262:20 + --> tests/ui/needless_lifetimes.rs:263:20 | LL | fn alias_with_lt4b<'a, 'b>(_foo: &'a FooAlias<'b>) -> &'b str { | ^^ ^^ @@ -276,7 +276,7 @@ LL + fn alias_with_lt4b<'b>(_foo: &FooAlias<'b>) -> &'b str { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:266:30 + --> tests/ui/needless_lifetimes.rs:267:30 | LL | fn named_input_elided_output<'a>(_arg: &'a str) -> &str { | ^^ ^^ ^ @@ -288,7 +288,7 @@ LL + fn named_input_elided_output(_arg: &str) -> &str { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:274:19 + --> tests/ui/needless_lifetimes.rs:275:19 | LL | fn trait_bound_ok<'a, T: WithLifetime<'static>>(_: &'a u8, _: T) { | ^^ ^^ @@ -300,7 +300,7 @@ LL + fn trait_bound_ok>(_: &u8, _: T) { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:310:24 + --> tests/ui/needless_lifetimes.rs:311:24 | LL | fn out_return_type_lts<'a>(e: &'a str) -> Cow<'a> { | ^^ ^^ ^^ @@ -312,7 +312,7 @@ LL + fn out_return_type_lts(e: &str) -> Cow<'_> { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:317:24 + --> tests/ui/needless_lifetimes.rs:318:24 | LL | fn needless_lt<'a>(x: &'a u8) {} | ^^ ^^ @@ -324,7 +324,7 @@ LL + fn needless_lt(x: &u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:321:24 + --> tests/ui/needless_lifetimes.rs:322:24 | LL | fn needless_lt<'a>(_x: &'a u8) {} | ^^ ^^ @@ -336,7 +336,7 @@ LL + fn needless_lt(_x: &u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:332:10 + --> tests/ui/needless_lifetimes.rs:333:10 | LL | impl<'a> Foo for Baz<'a> {} | ^^ ^^ @@ -348,7 +348,7 @@ LL + impl Foo for Baz<'_> {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:334:16 + --> tests/ui/needless_lifetimes.rs:335:16 | LL | fn baz<'a>(&'a self) -> impl Foo + 'a { | ^^ ^^ ^^ @@ -360,7 +360,7 @@ LL + fn baz(&self) -> impl Foo + '_ { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:366:55 + --> tests/ui/needless_lifetimes.rs:367:55 | LL | fn impl_trait_elidable_nested_anonymous_lifetimes<'a>(i: &'a i32, f: impl Fn(&i32) -> &i32) -> &'a i32 { | ^^ ^^ ^^ @@ -372,7 +372,7 @@ LL + fn impl_trait_elidable_nested_anonymous_lifetimes(i: &i32, f: impl Fn(& | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:375:26 + --> tests/ui/needless_lifetimes.rs:376:26 | LL | fn generics_elidable<'a, T: Fn(&i32) -> &i32>(i: &'a i32, f: T) -> &'a i32 { | ^^ ^^ ^^ @@ -384,7 +384,7 @@ LL + fn generics_elidable &i32>(i: &i32, f: T) -> &i32 { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:387:30 + --> tests/ui/needless_lifetimes.rs:388:30 | LL | fn where_clause_elidable<'a, T>(i: &'a i32, f: T) -> &'a i32 | ^^ ^^ ^^ @@ -396,7 +396,7 @@ LL + fn where_clause_elidable(i: &i32, f: T) -> &i32 | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:402:28 + --> tests/ui/needless_lifetimes.rs:403:28 | LL | fn pointer_fn_elidable<'a>(i: &'a i32, f: fn(&i32) -> &i32) -> &'a i32 { | ^^ ^^ ^^ @@ -408,7 +408,7 @@ LL + fn pointer_fn_elidable(i: &i32, f: fn(&i32) -> &i32) -> &i32 { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:415:28 + --> tests/ui/needless_lifetimes.rs:416:28 | LL | fn nested_fn_pointer_3<'a>(_: &'a i32) -> fn(fn(&i32) -> &i32) -> i32 { | ^^ ^^ @@ -420,7 +420,7 @@ LL + fn nested_fn_pointer_3(_: &i32) -> fn(fn(&i32) -> &i32) -> i32 { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:418:28 + --> tests/ui/needless_lifetimes.rs:419:28 | LL | fn nested_fn_pointer_4<'a>(_: &'a i32) -> impl Fn(fn(&i32)) { | ^^ ^^ @@ -432,7 +432,7 @@ LL + fn nested_fn_pointer_4(_: &i32) -> impl Fn(fn(&i32)) { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:440:21 + --> tests/ui/needless_lifetimes.rs:441:21 | LL | fn implicit<'a>(&'a self) -> &'a () { | ^^ ^^ ^^ @@ -444,7 +444,7 @@ LL + fn implicit(&self) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:443:25 + --> tests/ui/needless_lifetimes.rs:444:25 | LL | fn implicit_mut<'a>(&'a mut self) -> &'a () { | ^^ ^^ ^^ @@ -456,7 +456,31 @@ LL + fn implicit_mut(&mut self) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:454:31 + --> tests/ui/needless_lifetimes.rs:448:21 + | +LL | fn explicit<'a>(self: &'a Arc) -> &'a () { + | ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - fn explicit<'a>(self: &'a Arc) -> &'a () { +LL + fn explicit(self: &Arc) -> &() { + | + +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/needless_lifetimes.rs:452:25 + | +LL | fn explicit_mut<'a>(self: &'a mut Rc) -> &'a () { + | ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - fn explicit_mut<'a>(self: &'a mut Rc) -> &'a () { +LL + fn explicit_mut(self: &mut Rc) -> &() { + | + +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/needless_lifetimes.rs:464:31 | LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a () { | ^^ ^^ ^^ @@ -468,7 +492,7 @@ LL + fn lifetime_elsewhere(self: Box, here: &()) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:460:21 + --> tests/ui/needless_lifetimes.rs:470:21 | LL | fn implicit<'a>(&'a self) -> &'a (); | ^^ ^^ ^^ @@ -480,7 +504,7 @@ LL + fn implicit(&self) -> &(); | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:461:30 + --> tests/ui/needless_lifetimes.rs:471:30 | LL | fn implicit_provided<'a>(&'a self) -> &'a () { | ^^ ^^ ^^ @@ -492,7 +516,31 @@ LL + fn implicit_provided(&self) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:470:31 + --> tests/ui/needless_lifetimes.rs:476:21 + | +LL | fn explicit<'a>(self: &'a Arc) -> &'a (); + | ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - fn explicit<'a>(self: &'a Arc) -> &'a (); +LL + fn explicit(self: &Arc) -> &(); + | + +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/needless_lifetimes.rs:478:30 + | +LL | fn explicit_provided<'a>(self: &'a Arc) -> &'a () { + | ^^ ^^ ^^ + | +help: elide the lifetimes + | +LL - fn explicit_provided<'a>(self: &'a Arc) -> &'a () { +LL + fn explicit_provided(self: &Arc) -> &() { + | + +error: the following explicit lifetimes could be elided: 'a + --> tests/ui/needless_lifetimes.rs:488:31 | LL | fn lifetime_elsewhere<'a>(self: Box, here: &'a ()) -> &'a (); | ^^ ^^ ^^ @@ -504,7 +552,7 @@ LL + fn lifetime_elsewhere(self: Box, here: &()) -> &(); | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:471:40 + --> tests/ui/needless_lifetimes.rs:489:40 | LL | fn lifetime_elsewhere_provided<'a>(self: Box, here: &'a ()) -> &'a () { | ^^ ^^ ^^ @@ -516,7 +564,7 @@ LL + fn lifetime_elsewhere_provided(self: Box, here: &()) -> &() { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:480:12 + --> tests/ui/needless_lifetimes.rs:498:12 | LL | fn foo<'a>(x: &'a u8, y: &'_ u8) {} | ^^ ^^ @@ -528,7 +576,7 @@ LL + fn foo(x: &u8, y: &'_ u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:482:12 + --> tests/ui/needless_lifetimes.rs:500:12 | LL | fn bar<'a>(x: &'a u8, y: &'_ u8, z: &'_ u8) {} | ^^ ^^ @@ -540,7 +588,7 @@ LL + fn bar(x: &u8, y: &'_ u8, z: &'_ u8) {} | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:489:18 + --> tests/ui/needless_lifetimes.rs:507:18 | LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { | ^^ ^^ ^^ @@ -552,7 +600,7 @@ LL + fn one_input(x: &u8) -> &u8 { | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:494:42 + --> tests/ui/needless_lifetimes.rs:512:42 | LL | fn multiple_inputs_output_not_elided<'a, 'b>(x: &'a u8, y: &'b u8, z: &'b u8) -> &'b u8 { | ^^ ^^ @@ -564,7 +612,7 @@ LL + fn multiple_inputs_output_not_elided<'b>(x: &u8, y: &'b u8, z: &'b u8) | error: the following explicit lifetimes could be elided: 'a - --> tests/ui/needless_lifetimes.rs:510:22 + --> tests/ui/needless_lifetimes.rs:528:22 | LL | fn one_input<'a>(x: &'a u8) -> &'a u8 { | ^^ ^^ ^^ @@ -576,5 +624,64 @@ LL - fn one_input<'a>(x: &'a u8) -> &'a u8 { LL + fn one_input(x: &u8) -> &u8 { | -error: aborting due to 48 previous errors +error: the following explicit lifetimes could be elided: 'py + --> tests/ui/needless_lifetimes.rs:623:14 + | +LL | impl<'t, 'py> ContentString<'t> { + | ^^^ +LL | // `'py` can be elided because of `&self` +LL | fn map_content2(&self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, 'py> { + | ^^^ + | +help: elide the lifetimes + | +LL ~ impl<'t> ContentString<'t> { +LL | // `'py` can be elided because of `&self` +LL ~ fn map_content2(&self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, '_> { + | + +error: the following explicit lifetimes could be elided: 'py + --> tests/ui/needless_lifetimes.rs:633:14 + | +LL | impl<'t, 'py> ContentString<'t> { + | ^^^ +LL | // `'py` can be elided because of `&'_ self` +LL | fn map_content3(&'_ self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, 'py> { + | ^^^ + | +help: elide the lifetimes + | +LL ~ impl<'t> ContentString<'t> { +LL | // `'py` can be elided because of `&'_ self` +LL ~ fn map_content3(&'_ self, f: impl FnOnce(&'t str) -> &'t str) -> Content<'t, '_> { + | + +error: the following explicit lifetimes could be elided: 'py + --> tests/ui/needless_lifetimes.rs:653:14 + | +LL | impl<'t, 'py> ContentString<'t> { + | ^^^ +... +LL | ) -> Content<'t, 'py> { + | ^^^ + | +help: elide the lifetimes + | +LL ~ impl<'t> ContentString<'t> { +LL | // `'py` can be elided because of `&Self` +... +LL | o: &'t str, +LL ~ ) -> Content<'t, '_> { + | + +error: this expression creates a reference which is immediately dereferenced by the compiler + --> tests/ui/needless_lifetimes.rs:674:9 + | +LL | &x.b + | ^^^^ help: change this to: `x.b` + | + = note: `-D clippy::needless-borrow` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::needless_borrow)]` + +error: aborting due to 56 previous errors diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/auxiliary/lazy_static.rs b/src/tools/clippy/tests/ui/non_std_lazy_static/auxiliary/lazy_static.rs new file mode 100644 index 0000000000000..85fb4e66079fb --- /dev/null +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/auxiliary/lazy_static.rs @@ -0,0 +1,20 @@ +//! **FAKE** lazy_static crate. + +#[macro_export] +macro_rules! lazy_static { + (static ref $N:ident : $T:ty = $e:expr; $($t:tt)*) => { + static $N : &::core::marker::PhantomData<$T> = &::core::marker::PhantomData; + + $crate::lazy_static! { $($t)* } + }; + () => () +} + +#[macro_export] +macro_rules! external { + () => { + $crate::lazy_static! { + static ref LZ_DERP: u32 = 12; + } + }; +} diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/auxiliary/once_cell.rs b/src/tools/clippy/tests/ui/non_std_lazy_static/auxiliary/once_cell.rs new file mode 100644 index 0000000000000..e860a0f7572c2 --- /dev/null +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/auxiliary/once_cell.rs @@ -0,0 +1,55 @@ +//! **FAKE** once_cell crate. + +pub mod sync { + use std::marker::PhantomData; + + pub struct Lazy T> { + cell: PhantomData, + init: F, + } + unsafe impl Sync for Lazy {} + impl Lazy { + pub const fn new(f: F) -> Lazy { + Lazy { + cell: PhantomData, + init: f, + } + } + + pub fn into_value(this: Lazy) -> Result { + unimplemented!() + } + + pub fn force(_this: &Lazy) -> &T { + unimplemented!() + } + + pub fn force_mut(_this: &mut Lazy) -> &mut T { + unimplemented!() + } + + pub fn get(_this: &Lazy) -> Option<&T> { + unimplemented!() + } + + pub fn get_mut(_this: &mut Lazy) -> Option<&mut T> { + unimplemented!() + } + } +} +pub mod race { + pub struct OnceBox(T); + + impl OnceBox { + pub fn get(&self) -> Option<&T> { + Some(&self.0) + } + } +} + +#[macro_export] +macro_rules! external { + () => { + static OC_DERP: $crate::sync::Lazy = $crate::sync::Lazy::new(|| 12); + }; +} diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed new file mode 100644 index 0000000000000..f7c56b6fffe81 --- /dev/null +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.fixed @@ -0,0 +1,72 @@ +//@aux-build:once_cell.rs +//@aux-build:lazy_static.rs + +#![warn(clippy::non_std_lazy_statics)] +#![allow(static_mut_refs)] + +use once_cell::sync::Lazy; + +fn main() {} + +static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); +//~^ ERROR: this type has been superceded by `LazyLock` in the standard library +static LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| { + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + let x = "bar"; + x.to_uppercase() +}); +static LAZY_BAZ: std::sync::LazyLock = { std::sync::LazyLock::new(|| "baz".to_uppercase()) }; +//~^ ERROR: this type has been superceded by `LazyLock` in the standard library +static LAZY_QUX: std::sync::LazyLock = { + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + if "qux".len() == 3 { + std::sync::LazyLock::new(|| "qux".to_uppercase()) + } else if "qux".is_ascii() { + std::sync::LazyLock::new(|| "qux".to_lowercase()) + } else { + std::sync::LazyLock::new(|| "qux".to_string()) + } +}; + +fn non_static() { + let _: Lazy = Lazy::new(|| 1); + let _: Lazy = Lazy::new(|| String::from("hello")); + #[allow(clippy::declare_interior_mutable_const)] + const DONT_DO_THIS: Lazy = Lazy::new(|| 1); +} + +mod once_cell_lazy_with_fns { + use once_cell::sync::Lazy; + + static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + static LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| "bar".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + static mut LAZY_BAZ: std::sync::LazyLock = std::sync::LazyLock::new(|| "baz".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + + fn calling_replaceable_fns() { + let _ = std::sync::LazyLock::force(&LAZY_FOO); + let _ = std::sync::LazyLock::force(&LAZY_BAR); + unsafe { + let _ = std::sync::LazyLock::force(&LAZY_BAZ); + } + } +} + +#[clippy::msrv = "1.79"] +mod msrv_not_meet { + use lazy_static::lazy_static; + use once_cell::sync::Lazy; + + static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); + + lazy_static! { + static ref LAZY_BAZ: f64 = 12.159 * 548; + } +} + +mod external_macros { + once_cell::external!(); + lazy_static::external!(); +} diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs new file mode 100644 index 0000000000000..90bc428137cea --- /dev/null +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs @@ -0,0 +1,72 @@ +//@aux-build:once_cell.rs +//@aux-build:lazy_static.rs + +#![warn(clippy::non_std_lazy_statics)] +#![allow(static_mut_refs)] + +use once_cell::sync::Lazy; + +fn main() {} + +static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); +//~^ ERROR: this type has been superceded by `LazyLock` in the standard library +static LAZY_BAR: Lazy = Lazy::new(|| { + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + let x = "bar"; + x.to_uppercase() +}); +static LAZY_BAZ: Lazy = { Lazy::new(|| "baz".to_uppercase()) }; +//~^ ERROR: this type has been superceded by `LazyLock` in the standard library +static LAZY_QUX: Lazy = { + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + if "qux".len() == 3 { + Lazy::new(|| "qux".to_uppercase()) + } else if "qux".is_ascii() { + Lazy::new(|| "qux".to_lowercase()) + } else { + Lazy::new(|| "qux".to_string()) + } +}; + +fn non_static() { + let _: Lazy = Lazy::new(|| 1); + let _: Lazy = Lazy::new(|| String::from("hello")); + #[allow(clippy::declare_interior_mutable_const)] + const DONT_DO_THIS: Lazy = Lazy::new(|| 1); +} + +mod once_cell_lazy_with_fns { + use once_cell::sync::Lazy; + + static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + static LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + static mut LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + + fn calling_replaceable_fns() { + let _ = Lazy::force(&LAZY_FOO); + let _ = Lazy::force(&LAZY_BAR); + unsafe { + let _ = Lazy::force(&LAZY_BAZ); + } + } +} + +#[clippy::msrv = "1.79"] +mod msrv_not_meet { + use lazy_static::lazy_static; + use once_cell::sync::Lazy; + + static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); + + lazy_static! { + static ref LAZY_BAZ: f64 = 12.159 * 548; + } +} + +mod external_macros { + once_cell::external!(); + lazy_static::external!(); +} diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr new file mode 100644 index 0000000000000..f956f4b8d52a4 --- /dev/null +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.stderr @@ -0,0 +1,100 @@ +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:11:18 + | +LL | static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); + | ^^^^ + | + = note: `-D clippy::non-std-lazy-statics` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::non_std_lazy_statics)]` +help: use `std::sync::LazyLock` instead + | +LL | static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); + | ~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:13:18 + | +LL | static LAZY_BAR: Lazy = Lazy::new(|| { + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL | static LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| { + | ~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:18:18 + | +LL | static LAZY_BAZ: Lazy = { Lazy::new(|| "baz".to_uppercase()) }; + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL | static LAZY_BAZ: std::sync::LazyLock = { std::sync::LazyLock::new(|| "baz".to_uppercase()) }; + | ~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:20:18 + | +LL | static LAZY_QUX: Lazy = { + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL ~ static LAZY_QUX: std::sync::LazyLock = { +LL | +LL | if "qux".len() == 3 { +LL ~ std::sync::LazyLock::new(|| "qux".to_uppercase()) +LL | } else if "qux".is_ascii() { +LL ~ std::sync::LazyLock::new(|| "qux".to_lowercase()) +LL | } else { +LL ~ std::sync::LazyLock::new(|| "qux".to_string()) + | + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:41:22 + | +LL | static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL ~ static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); +LL | +... +LL | fn calling_replaceable_fns() { +LL ~ let _ = std::sync::LazyLock::force(&LAZY_FOO); + | + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:43:22 + | +LL | static LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL ~ static LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| "bar".to_uppercase()); +LL | +... +LL | let _ = Lazy::force(&LAZY_FOO); +LL ~ let _ = std::sync::LazyLock::force(&LAZY_BAR); + | + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_fixable.rs:45:26 + | +LL | static mut LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL ~ static mut LAZY_BAZ: std::sync::LazyLock = std::sync::LazyLock::new(|| "baz".to_uppercase()); +LL | +... +LL | unsafe { +LL ~ let _ = std::sync::LazyLock::force(&LAZY_BAZ); + | + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_no_std.rs b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_no_std.rs new file mode 100644 index 0000000000000..6208612c69830 --- /dev/null +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_no_std.rs @@ -0,0 +1,20 @@ +//@aux-build:once_cell.rs +//@aux-build:lazy_static.rs + +#![warn(clippy::non_std_lazy_statics)] +#![no_std] + +use lazy_static::lazy_static; +use once_cell::sync::Lazy; + +fn main() {} + +static LAZY_FOO: Lazy = Lazy::new(|| 42); +static LAZY_BAR: Lazy = Lazy::new(|| { + let x: i32 = 0; + x.saturating_add(100) +}); + +lazy_static! { + static ref LAZY_BAZ: f64 = 12.159 * 548; +} diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_other_once_cell.rs b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_other_once_cell.rs new file mode 100644 index 0000000000000..8701a4b7729ad --- /dev/null +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_other_once_cell.rs @@ -0,0 +1,12 @@ +//@aux-build:once_cell.rs + +#![warn(clippy::non_std_lazy_statics)] + +// Should not error, since we used a type besides `sync::Lazy` +fn use_once_cell_race(x: once_cell::race::OnceBox) { + let _foo = x.get(); +} + +use once_cell::sync::Lazy; + +static LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs new file mode 100644 index 0000000000000..34f8dd1ccb2ea --- /dev/null +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs @@ -0,0 +1,43 @@ +//@aux-build:once_cell.rs +//@aux-build:lazy_static.rs +//@no-rustfix + +#![warn(clippy::non_std_lazy_statics)] +#![allow(static_mut_refs)] + +mod once_cell_lazy { + use once_cell::sync::Lazy; + + static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + static mut LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + static mut LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); + //~^ ERROR: this type has been superceded by `LazyLock` in the standard library + + fn calling_irreplaceable_fns() { + let _ = Lazy::get(&LAZY_FOO); + + unsafe { + let _ = Lazy::get_mut(&mut LAZY_BAR); + let _ = Lazy::force_mut(&mut LAZY_BAZ); + } + } +} + +mod lazy_static_lazy_static { + use lazy_static::lazy_static; + + lazy_static! { + static ref LAZY_FOO: String = "foo".to_uppercase(); + } + //~^^^ ERROR: this macro has been superceded by `std::sync::LazyLock` + lazy_static! { + static ref LAZY_BAR: String = "bar".to_uppercase(); + static ref LAZY_BAZ: String = "baz".to_uppercase(); + } + //~^^^^ ERROR: this macro has been superceded by `std::sync::LazyLock` + //~| ERROR: this macro has been superceded by `std::sync::LazyLock` +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr new file mode 100644 index 0000000000000..66dc435f9823f --- /dev/null +++ b/src/tools/clippy/tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.stderr @@ -0,0 +1,66 @@ +error: this macro has been superceded by `std::sync::LazyLock` + --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:31:5 + | +LL | / lazy_static! { +LL | | static ref LAZY_FOO: String = "foo".to_uppercase(); +LL | | } + | |_____^ + | + = note: `-D clippy::non-std-lazy-statics` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::non_std_lazy_statics)]` + +error: this macro has been superceded by `std::sync::LazyLock` + --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:35:5 + | +LL | / lazy_static! { +LL | | static ref LAZY_BAR: String = "bar".to_uppercase(); +LL | | static ref LAZY_BAZ: String = "baz".to_uppercase(); +LL | | } + | |_____^ + +error: this macro has been superceded by `std::sync::LazyLock` + --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:35:5 + | +LL | / lazy_static! { +LL | | static ref LAZY_BAR: String = "bar".to_uppercase(); +LL | | static ref LAZY_BAZ: String = "baz".to_uppercase(); +LL | | } + | |_____^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:11:22 + | +LL | static LAZY_FOO: Lazy = Lazy::new(|| "foo".to_uppercase()); + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL | static LAZY_FOO: std::sync::LazyLock = std::sync::LazyLock::new(|| "foo".to_uppercase()); + | ~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:13:26 + | +LL | static mut LAZY_BAR: Lazy = Lazy::new(|| "bar".to_uppercase()); + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL | static mut LAZY_BAR: std::sync::LazyLock = std::sync::LazyLock::new(|| "bar".to_uppercase()); + | ~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ + +error: this type has been superceded by `LazyLock` in the standard library + --> tests/ui/non_std_lazy_static/non_std_lazy_static_unfixable.rs:15:26 + | +LL | static mut LAZY_BAZ: Lazy = Lazy::new(|| "baz".to_uppercase()); + | ^^^^ + | +help: use `std::sync::LazyLock` instead + | +LL | static mut LAZY_BAZ: std::sync::LazyLock = std::sync::LazyLock::new(|| "baz".to_uppercase()); + | ~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/obfuscated_if_else.fixed b/src/tools/clippy/tests/ui/obfuscated_if_else.fixed index c5ee569800abd..bfe1c5e10cf8d 100644 --- a/src/tools/clippy/tests/ui/obfuscated_if_else.fixed +++ b/src/tools/clippy/tests/ui/obfuscated_if_else.fixed @@ -1,5 +1,18 @@ #![warn(clippy::obfuscated_if_else)] +#![allow(clippy::unnecessary_lazy_evaluations, clippy::unit_arg, clippy::unused_unit)] fn main() { if true { "a" } else { "b" }; + if true { "a" } else { "b" }; + + let a = 1; + if a == 1 { "a" } else { "b" }; + if a == 1 { "a" } else { "b" }; + + let partial = (a == 1).then_some("a"); + partial.unwrap_or("b"); // not lint + + let mut a = 0; + if true { a += 1 } else { () }; + if true { () } else { a += 2 }; } diff --git a/src/tools/clippy/tests/ui/obfuscated_if_else.rs b/src/tools/clippy/tests/ui/obfuscated_if_else.rs index 2b60c855a555a..0ded2a2ceedfb 100644 --- a/src/tools/clippy/tests/ui/obfuscated_if_else.rs +++ b/src/tools/clippy/tests/ui/obfuscated_if_else.rs @@ -1,5 +1,18 @@ #![warn(clippy::obfuscated_if_else)] +#![allow(clippy::unnecessary_lazy_evaluations, clippy::unit_arg, clippy::unused_unit)] fn main() { true.then_some("a").unwrap_or("b"); + true.then(|| "a").unwrap_or("b"); + + let a = 1; + (a == 1).then_some("a").unwrap_or("b"); + (a == 1).then(|| "a").unwrap_or("b"); + + let partial = (a == 1).then_some("a"); + partial.unwrap_or("b"); // not lint + + let mut a = 0; + true.then_some(a += 1).unwrap_or(()); + true.then_some(()).unwrap_or(a += 2); } diff --git a/src/tools/clippy/tests/ui/obfuscated_if_else.stderr b/src/tools/clippy/tests/ui/obfuscated_if_else.stderr index d4c2f9b331a82..9ce1f475c4803 100644 --- a/src/tools/clippy/tests/ui/obfuscated_if_else.stderr +++ b/src/tools/clippy/tests/ui/obfuscated_if_else.stderr @@ -1,5 +1,5 @@ -error: use of `.then_some(..).unwrap_or(..)` can be written more clearly with `if .. else ..` - --> tests/ui/obfuscated_if_else.rs:4:5 +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:5:5 | LL | true.then_some("a").unwrap_or("b"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { "a" } else { "b" }` @@ -7,5 +7,35 @@ LL | true.then_some("a").unwrap_or("b"); = note: `-D clippy::obfuscated-if-else` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::obfuscated_if_else)]` -error: aborting due to 1 previous error +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:6:5 + | +LL | true.then(|| "a").unwrap_or("b"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { "a" } else { "b" }` + +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:9:5 + | +LL | (a == 1).then_some("a").unwrap_or("b"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if a == 1 { "a" } else { "b" }` + +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:10:5 + | +LL | (a == 1).then(|| "a").unwrap_or("b"); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if a == 1 { "a" } else { "b" }` + +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:16:5 + | +LL | true.then_some(a += 1).unwrap_or(()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { a += 1 } else { () }` + +error: this method chain can be written more clearly with `if .. else ..` + --> tests/ui/obfuscated_if_else.rs:17:5 + | +LL | true.then_some(()).unwrap_or(a += 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `if true { () } else { a += 2 }` + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/precedence.stderr b/src/tools/clippy/tests/ui/precedence.stderr index 0d63e827d66ea..329422cb8a69b 100644 --- a/src/tools/clippy/tests/ui/precedence.stderr +++ b/src/tools/clippy/tests/ui/precedence.stderr @@ -1,4 +1,4 @@ -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:16:5 | LL | 1 << 2 + 3; @@ -7,61 +7,61 @@ LL | 1 << 2 + 3; = note: `-D clippy::precedence` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::precedence)]` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:17:5 | LL | 1 + 2 << 3; | ^^^^^^^^^^ help: consider parenthesizing your expression: `(1 + 2) << 3` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:18:5 | LL | 4 >> 1 + 1; | ^^^^^^^^^^ help: consider parenthesizing your expression: `4 >> (1 + 1)` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:19:5 | LL | 1 + 3 >> 2; | ^^^^^^^^^^ help: consider parenthesizing your expression: `(1 + 3) >> 2` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:20:5 | LL | 1 ^ 1 - 1; | ^^^^^^^^^ help: consider parenthesizing your expression: `1 ^ (1 - 1)` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:21:5 | LL | 3 | 2 - 1; | ^^^^^^^^^ help: consider parenthesizing your expression: `3 | (2 - 1)` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:22:5 | LL | 3 & 5 - 2; | ^^^^^^^^^ help: consider parenthesizing your expression: `3 & (5 - 2)` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:23:5 | LL | 0x0F00 & 0x00F0 << 4; | ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0x00F0 << 4)` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:24:5 | LL | 0x0F00 & 0xF000 >> 4; | ^^^^^^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `0x0F00 & (0xF000 >> 4)` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:25:5 | LL | 0x0F00 << 1 ^ 3; | ^^^^^^^^^^^^^^^ help: consider parenthesizing your expression: `(0x0F00 << 1) ^ 3` -error: operator precedence can trip the unwary +error: operator precedence might not be obvious --> tests/ui/precedence.rs:26:5 | LL | 0x0F00 << 1 | 2; diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.fixed b/src/tools/clippy/tests/ui/redundant_pub_crate.fixed index e1d845721a9c9..8882a4d50a5f8 100644 --- a/src/tools/clippy/tests/ui/redundant_pub_crate.fixed +++ b/src/tools/clippy/tests/ui/redundant_pub_crate.fixed @@ -1,3 +1,4 @@ +//@aux-build:proc_macros.rs #![allow(dead_code)] #![warn(clippy::redundant_pub_crate)] @@ -113,4 +114,10 @@ mod issue_8732 { pub(crate) use some_macro; // ok: macro exports are exempt } +proc_macros::external! { + mod priv_mod { + pub(crate) fn dummy() {} + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.rs b/src/tools/clippy/tests/ui/redundant_pub_crate.rs index 4d7f44892d0c5..5c8cab9be161d 100644 --- a/src/tools/clippy/tests/ui/redundant_pub_crate.rs +++ b/src/tools/clippy/tests/ui/redundant_pub_crate.rs @@ -1,3 +1,4 @@ +//@aux-build:proc_macros.rs #![allow(dead_code)] #![warn(clippy::redundant_pub_crate)] @@ -113,4 +114,10 @@ mod issue_8732 { pub(crate) use some_macro; // ok: macro exports are exempt } +proc_macros::external! { + mod priv_mod { + pub(crate) fn dummy() {} + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/redundant_pub_crate.stderr b/src/tools/clippy/tests/ui/redundant_pub_crate.stderr index 8f1005ab9b73c..699e19b1abcf1 100644 --- a/src/tools/clippy/tests/ui/redundant_pub_crate.stderr +++ b/src/tools/clippy/tests/ui/redundant_pub_crate.stderr @@ -1,5 +1,5 @@ error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:6:5 + --> tests/ui/redundant_pub_crate.rs:7:5 | LL | pub(crate) fn g() {} // private due to m1 | ----------^^^^^ @@ -10,7 +10,7 @@ LL | pub(crate) fn g() {} // private due to m1 = help: to override `-D warnings` add `#[allow(clippy::redundant_pub_crate)]` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:11:9 + --> tests/ui/redundant_pub_crate.rs:12:9 | LL | pub(crate) fn g() {} // private due to m1_1 and m1 | ----------^^^^^ @@ -18,7 +18,7 @@ LL | pub(crate) fn g() {} // private due to m1_1 and m1 | help: consider using: `pub` error: pub(crate) module inside private module - --> tests/ui/redundant_pub_crate.rs:15:5 + --> tests/ui/redundant_pub_crate.rs:16:5 | LL | pub(crate) mod m1_2 { | ----------^^^^^^^^^ @@ -26,7 +26,7 @@ LL | pub(crate) mod m1_2 { | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:18:9 + --> tests/ui/redundant_pub_crate.rs:19:9 | LL | pub(crate) fn g() {} // private due to m1_2 and m1 | ----------^^^^^ @@ -34,7 +34,7 @@ LL | pub(crate) fn g() {} // private due to m1_2 and m1 | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:24:9 + --> tests/ui/redundant_pub_crate.rs:25:9 | LL | pub(crate) fn g() {} // private due to m1 | ----------^^^^^ @@ -42,7 +42,7 @@ LL | pub(crate) fn g() {} // private due to m1 | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:31:5 + --> tests/ui/redundant_pub_crate.rs:32:5 | LL | pub(crate) fn g() {} // already crate visible due to m2 | ----------^^^^^ @@ -50,7 +50,7 @@ LL | pub(crate) fn g() {} // already crate visible due to m2 | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:36:9 + --> tests/ui/redundant_pub_crate.rs:37:9 | LL | pub(crate) fn g() {} // private due to m2_1 | ----------^^^^^ @@ -58,7 +58,7 @@ LL | pub(crate) fn g() {} // private due to m2_1 | help: consider using: `pub` error: pub(crate) module inside private module - --> tests/ui/redundant_pub_crate.rs:40:5 + --> tests/ui/redundant_pub_crate.rs:41:5 | LL | pub(crate) mod m2_2 { | ----------^^^^^^^^^ @@ -66,7 +66,7 @@ LL | pub(crate) mod m2_2 { | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:43:9 + --> tests/ui/redundant_pub_crate.rs:44:9 | LL | pub(crate) fn g() {} // already crate visible due to m2_2 and m2 | ----------^^^^^ @@ -74,7 +74,7 @@ LL | pub(crate) fn g() {} // already crate visible due to m2_2 and m2 | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:49:9 + --> tests/ui/redundant_pub_crate.rs:50:9 | LL | pub(crate) fn g() {} // already crate visible due to m2 | ----------^^^^^ @@ -82,7 +82,7 @@ LL | pub(crate) fn g() {} // already crate visible due to m2 | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:61:9 + --> tests/ui/redundant_pub_crate.rs:62:9 | LL | pub(crate) fn g() {} // private due to m3_1 | ----------^^^^^ @@ -90,7 +90,7 @@ LL | pub(crate) fn g() {} // private due to m3_1 | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:68:9 + --> tests/ui/redundant_pub_crate.rs:69:9 | LL | pub(crate) fn g() {} // already crate visible due to m3_2 | ----------^^^^^ @@ -98,7 +98,7 @@ LL | pub(crate) fn g() {} // already crate visible due to m3_2 | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:81:5 + --> tests/ui/redundant_pub_crate.rs:82:5 | LL | pub(crate) fn g() {} // private: not re-exported by `pub use m4::*` | ----------^^^^^ @@ -106,7 +106,7 @@ LL | pub(crate) fn g() {} // private: not re-exported by `pub use m4::*` | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:86:9 + --> tests/ui/redundant_pub_crate.rs:87:9 | LL | pub(crate) fn g() {} // private due to m4_1 | ----------^^^^^ @@ -114,7 +114,7 @@ LL | pub(crate) fn g() {} // private due to m4_1 | help: consider using: `pub` error: pub(crate) module inside private module - --> tests/ui/redundant_pub_crate.rs:90:5 + --> tests/ui/redundant_pub_crate.rs:91:5 | LL | pub(crate) mod m4_2 { | ----------^^^^^^^^^ @@ -122,7 +122,7 @@ LL | pub(crate) mod m4_2 { | help: consider using: `pub` error: pub(crate) function inside private module - --> tests/ui/redundant_pub_crate.rs:93:9 + --> tests/ui/redundant_pub_crate.rs:94:9 | LL | pub(crate) fn g() {} // private due to m4_2 | ----------^^^^^ diff --git a/src/tools/clippy/tests/ui/repeat_vec_with_capacity_nostd.fixed b/src/tools/clippy/tests/ui/repeat_vec_with_capacity_nostd.fixed new file mode 100644 index 0000000000000..ef316f1def41c --- /dev/null +++ b/src/tools/clippy/tests/ui/repeat_vec_with_capacity_nostd.fixed @@ -0,0 +1,10 @@ +#![warn(clippy::repeat_vec_with_capacity)] +#![allow(clippy::manual_repeat_n)] +#![no_std] +use core::iter; +extern crate alloc; +use alloc::vec::Vec; + +fn nostd() { + let _: Vec> = core::iter::repeat_with(|| Vec::with_capacity(42)).take(123).collect(); +} diff --git a/src/tools/clippy/tests/ui/repeat_vec_with_capacity_nostd.rs b/src/tools/clippy/tests/ui/repeat_vec_with_capacity_nostd.rs new file mode 100644 index 0000000000000..83b418a566749 --- /dev/null +++ b/src/tools/clippy/tests/ui/repeat_vec_with_capacity_nostd.rs @@ -0,0 +1,10 @@ +#![warn(clippy::repeat_vec_with_capacity)] +#![allow(clippy::manual_repeat_n)] +#![no_std] +use core::iter; +extern crate alloc; +use alloc::vec::Vec; + +fn nostd() { + let _: Vec> = iter::repeat(Vec::with_capacity(42)).take(123).collect(); +} diff --git a/src/tools/clippy/tests/ui/repeat_vec_with_capacity_nostd.stderr b/src/tools/clippy/tests/ui/repeat_vec_with_capacity_nostd.stderr new file mode 100644 index 0000000000000..39364d09b9613 --- /dev/null +++ b/src/tools/clippy/tests/ui/repeat_vec_with_capacity_nostd.stderr @@ -0,0 +1,16 @@ +error: repeating `Vec::with_capacity` using `iter::repeat`, which does not retain capacity + --> tests/ui/repeat_vec_with_capacity_nostd.rs:9:27 + | +LL | let _: Vec> = iter::repeat(Vec::with_capacity(42)).take(123).collect(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: none of the yielded `Vec`s will have the requested capacity + = note: `-D clippy::repeat-vec-with-capacity` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::repeat_vec_with_capacity)]` +help: if you intended to create an iterator that yields `Vec`s with an initial capacity, try + | +LL | let _: Vec> = core::iter::repeat_with(|| Vec::with_capacity(42)).take(123).collect(); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to 1 previous error + diff --git a/src/tools/clippy/tests/ui/same_item_push.rs b/src/tools/clippy/tests/ui/same_item_push.rs index df9c2817f5083..87fd59ad31794 100644 --- a/src/tools/clippy/tests/ui/same_item_push.rs +++ b/src/tools/clippy/tests/ui/same_item_push.rs @@ -21,33 +21,43 @@ fn main() { let item = 2; for _ in 5..=20 { vec.push(item); - //~^ ERROR: it looks like the same item is being pushed into this Vec + //~^ ERROR: it looks like the same item is being pushed into this `Vec` } let mut vec: Vec = Vec::new(); for _ in 0..15 { let item = 2; vec.push(item); - //~^ ERROR: it looks like the same item is being pushed into this Vec + //~^ ERROR: it looks like the same item is being pushed into this `Vec` } let mut vec: Vec = Vec::new(); for _ in 0..15 { vec.push(13); - //~^ ERROR: it looks like the same item is being pushed into this Vec + //~^ ERROR: it looks like the same item is being pushed into this `Vec` } let mut vec = Vec::new(); for _ in 0..20 { vec.push(VALUE); - //~^ ERROR: it looks like the same item is being pushed into this Vec + //~^ ERROR: it looks like the same item is being pushed into this `Vec` } let mut vec = Vec::new(); let item = VALUE; for _ in 0..20 { vec.push(item); - //~^ ERROR: it looks like the same item is being pushed into this Vec + //~^ ERROR: it looks like the same item is being pushed into this `Vec` + } + + #[clippy::msrv = "1.81"] + fn older_msrv() { + let mut vec = Vec::new(); + let item = VALUE; + for _ in 0..20 { + vec.push(item); + //~^ ERROR: it looks like the same item is being pushed into this `Vec` + } } // ** non-linted cases ** diff --git a/src/tools/clippy/tests/ui/same_item_push.stderr b/src/tools/clippy/tests/ui/same_item_push.stderr index eb296ed4ce4ae..e3fa4f9cbcec2 100644 --- a/src/tools/clippy/tests/ui/same_item_push.stderr +++ b/src/tools/clippy/tests/ui/same_item_push.stderr @@ -1,44 +1,58 @@ -error: it looks like the same item is being pushed into this Vec +error: it looks like the same item is being pushed into this `Vec` --> tests/ui/same_item_push.rs:23:9 | LL | vec.push(item); | ^^^ | - = help: consider using vec![item;SIZE] or vec.resize(NEW_SIZE, item) + = help: consider using `vec![item;SIZE]` + = help: or `vec.extend(std::iter::repeat_n(item, SIZE))` = note: `-D clippy::same-item-push` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::same_item_push)]` -error: it looks like the same item is being pushed into this Vec +error: it looks like the same item is being pushed into this `Vec` --> tests/ui/same_item_push.rs:30:9 | LL | vec.push(item); | ^^^ | - = help: consider using vec![item;SIZE] or vec.resize(NEW_SIZE, item) + = help: consider using `vec![item;SIZE]` + = help: or `vec.extend(std::iter::repeat_n(item, SIZE))` -error: it looks like the same item is being pushed into this Vec +error: it looks like the same item is being pushed into this `Vec` --> tests/ui/same_item_push.rs:36:9 | LL | vec.push(13); | ^^^ | - = help: consider using vec![13;SIZE] or vec.resize(NEW_SIZE, 13) + = help: consider using `vec![13;SIZE]` + = help: or `vec.extend(std::iter::repeat_n(13, SIZE))` -error: it looks like the same item is being pushed into this Vec +error: it looks like the same item is being pushed into this `Vec` --> tests/ui/same_item_push.rs:42:9 | LL | vec.push(VALUE); | ^^^ | - = help: consider using vec![VALUE;SIZE] or vec.resize(NEW_SIZE, VALUE) + = help: consider using `vec![VALUE;SIZE]` + = help: or `vec.extend(std::iter::repeat_n(VALUE, SIZE))` -error: it looks like the same item is being pushed into this Vec +error: it looks like the same item is being pushed into this `Vec` --> tests/ui/same_item_push.rs:49:9 | LL | vec.push(item); | ^^^ | - = help: consider using vec![item;SIZE] or vec.resize(NEW_SIZE, item) + = help: consider using `vec![item;SIZE]` + = help: or `vec.extend(std::iter::repeat_n(item, SIZE))` -error: aborting due to 5 previous errors +error: it looks like the same item is being pushed into this `Vec` + --> tests/ui/same_item_push.rs:58:13 + | +LL | vec.push(item); + | ^^^ + | + = help: consider using `vec![item;SIZE]` + = help: or `vec.resize(NEW_SIZE, item)` + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/short_circuit_statement.fixed b/src/tools/clippy/tests/ui/short_circuit_statement.fixed index a9930ef4dbb61..a2bf07ac60523 100644 --- a/src/tools/clippy/tests/ui/short_circuit_statement.fixed +++ b/src/tools/clippy/tests/ui/short_circuit_statement.fixed @@ -3,8 +3,35 @@ fn main() { if f() { g(); } + //~^ ERROR: boolean short circuit operator in statement if !f() { g(); } + //~^ ERROR: boolean short circuit operator in statement if 1 != 2 { g(); } + //~^ ERROR: boolean short circuit operator in statement + if f() || g() { H * 2; } + //~^ ERROR: boolean short circuit operator in statement + if !(f() || g()) { H * 2; } + //~^ ERROR: boolean short circuit operator in statement + + macro_rules! mac { + ($f:ident or $g:ident) => { + $f() || $g() + }; + ($f:ident and $g:ident) => { + $f() && $g() + }; + () => { + f() && g() + }; + } + + if mac!() { mac!(); } + //~^ ERROR: boolean short circuit operator in statement + if !mac!() { mac!(); } + //~^ ERROR: boolean short circuit operator in statement + + // Do not lint if the expression comes from a macro + mac!(); } fn f() -> bool { @@ -14,3 +41,12 @@ fn f() -> bool { fn g() -> bool { false } + +struct H; + +impl std::ops::Mul for H { + type Output = bool; + fn mul(self, other: u32) -> Self::Output { + true + } +} diff --git a/src/tools/clippy/tests/ui/short_circuit_statement.rs b/src/tools/clippy/tests/ui/short_circuit_statement.rs index 71f7c7f2abf77..bdba546ad8f63 100644 --- a/src/tools/clippy/tests/ui/short_circuit_statement.rs +++ b/src/tools/clippy/tests/ui/short_circuit_statement.rs @@ -3,8 +3,35 @@ fn main() { f() && g(); + //~^ ERROR: boolean short circuit operator in statement f() || g(); + //~^ ERROR: boolean short circuit operator in statement 1 == 2 || g(); + //~^ ERROR: boolean short circuit operator in statement + (f() || g()) && (H * 2); + //~^ ERROR: boolean short circuit operator in statement + (f() || g()) || (H * 2); + //~^ ERROR: boolean short circuit operator in statement + + macro_rules! mac { + ($f:ident or $g:ident) => { + $f() || $g() + }; + ($f:ident and $g:ident) => { + $f() && $g() + }; + () => { + f() && g() + }; + } + + mac!() && mac!(); + //~^ ERROR: boolean short circuit operator in statement + mac!() || mac!(); + //~^ ERROR: boolean short circuit operator in statement + + // Do not lint if the expression comes from a macro + mac!(); } fn f() -> bool { @@ -14,3 +41,12 @@ fn f() -> bool { fn g() -> bool { false } + +struct H; + +impl std::ops::Mul for H { + type Output = bool; + fn mul(self, other: u32) -> Self::Output { + true + } +} diff --git a/src/tools/clippy/tests/ui/short_circuit_statement.stderr b/src/tools/clippy/tests/ui/short_circuit_statement.stderr index e7a8f2ca60ca1..ecf6676405b4e 100644 --- a/src/tools/clippy/tests/ui/short_circuit_statement.stderr +++ b/src/tools/clippy/tests/ui/short_circuit_statement.stderr @@ -8,16 +8,40 @@ LL | f() && g(); = help: to override `-D warnings` add `#[allow(clippy::short_circuit_statement)]` error: boolean short circuit operator in statement may be clearer using an explicit test - --> tests/ui/short_circuit_statement.rs:6:5 + --> tests/ui/short_circuit_statement.rs:7:5 | LL | f() || g(); | ^^^^^^^^^^^ help: replace it with: `if !f() { g(); }` error: boolean short circuit operator in statement may be clearer using an explicit test - --> tests/ui/short_circuit_statement.rs:7:5 + --> tests/ui/short_circuit_statement.rs:9:5 | LL | 1 == 2 || g(); | ^^^^^^^^^^^^^^ help: replace it with: `if 1 != 2 { g(); }` -error: aborting due to 3 previous errors +error: boolean short circuit operator in statement may be clearer using an explicit test + --> tests/ui/short_circuit_statement.rs:11:5 + | +LL | (f() || g()) && (H * 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `if f() || g() { H * 2; }` + +error: boolean short circuit operator in statement may be clearer using an explicit test + --> tests/ui/short_circuit_statement.rs:13:5 + | +LL | (f() || g()) || (H * 2); + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `if !(f() || g()) { H * 2; }` + +error: boolean short circuit operator in statement may be clearer using an explicit test + --> tests/ui/short_circuit_statement.rs:28:5 + | +LL | mac!() && mac!(); + | ^^^^^^^^^^^^^^^^^ help: replace it with: `if mac!() { mac!(); }` + +error: boolean short circuit operator in statement may be clearer using an explicit test + --> tests/ui/short_circuit_statement.rs:30:5 + | +LL | mac!() || mac!(); + | ^^^^^^^^^^^^^^^^^ help: replace it with: `if !mac!() { mac!(); }` + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs index 8468d1d7c7d42..39d550398d705 100644 --- a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs +++ b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs @@ -832,4 +832,36 @@ fn should_trigger_lint_in_while_let() { } } +async fn foo_async(mutex: &Mutex) -> Option> { + Some(mutex.lock().unwrap()) +} + +async fn should_trigger_lint_for_async(mutex: Mutex) -> i32 { + match *foo_async(&mutex).await.unwrap() { + n if n < 10 => n, + _ => 10, + } +} + +async fn should_not_trigger_lint_in_async_expansion(mutex: Mutex) -> i32 { + match foo_async(&mutex).await { + Some(guard) => *guard, + _ => 0, + } +} + +fn should_trigger_lint_in_match_expr() { + let mutex = Mutex::new(State {}); + + // Should trigger lint because the lifetime of the temporary MutexGuard is surprising because it + // is preserved until the end of the match, but there is no clear indication that this is the + // case. + let _ = match mutex.lock().unwrap().foo() { + //~^ ERROR: temporary with significant `Drop` in `match` scrutinee will live until the + //~| NOTE: this might lead to deadlocks or other unexpected behavior + true => 0, + false => 1, + }; +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr index 62030cbe70e7f..f99d862aa6b25 100644 --- a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr +++ b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr @@ -568,5 +568,37 @@ LL | } | = note: this might lead to deadlocks or other unexpected behavior -error: aborting due to 29 previous errors +error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression + --> tests/ui/significant_drop_in_scrutinee.rs:840:11 + | +LL | match *foo_async(&mutex).await.unwrap() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | } + | - temporary lives until here + | + = note: this might lead to deadlocks or other unexpected behavior +help: try moving the temporary above the match + | +LL ~ let value = *foo_async(&mutex).await.unwrap(); +LL ~ match value { + | + +error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression + --> tests/ui/significant_drop_in_scrutinee.rs:859:19 + | +LL | let _ = match mutex.lock().unwrap().foo() { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | }; + | - temporary lives until here + | + = note: this might lead to deadlocks or other unexpected behavior +help: try moving the temporary above the match + | +LL ~ let value = mutex.lock().unwrap().foo(); +LL ~ let _ = match value { + | + +error: aborting due to 31 previous errors diff --git a/src/tools/clippy/tests/ui/sliced_string_as_bytes.fixed b/src/tools/clippy/tests/ui/sliced_string_as_bytes.fixed new file mode 100644 index 0000000000000..469ad27a99b9f --- /dev/null +++ b/src/tools/clippy/tests/ui/sliced_string_as_bytes.fixed @@ -0,0 +1,34 @@ +#![allow(unused)] +#![warn(clippy::sliced_string_as_bytes)] + +use std::ops::{Index, Range}; + +struct Foo; + +struct Bar; + +impl Bar { + fn as_bytes(&self) -> &[u8] { + &[0, 1, 2, 3] + } +} + +impl Index> for Foo { + type Output = Bar; + + fn index(&self, _: Range) -> &Self::Output { + &Bar + } +} + +fn main() { + let s = "Lorem ipsum"; + let string: String = "dolor sit amet".to_owned(); + + let bytes = &s.as_bytes()[1..5]; + let bytes = &string.as_bytes()[1..]; + let bytes = &"consectetur adipiscing".as_bytes()[..=5]; + + let f = Foo; + let bytes = f[0..4].as_bytes(); +} diff --git a/src/tools/clippy/tests/ui/sliced_string_as_bytes.rs b/src/tools/clippy/tests/ui/sliced_string_as_bytes.rs new file mode 100644 index 0000000000000..4a4605e5a1aea --- /dev/null +++ b/src/tools/clippy/tests/ui/sliced_string_as_bytes.rs @@ -0,0 +1,34 @@ +#![allow(unused)] +#![warn(clippy::sliced_string_as_bytes)] + +use std::ops::{Index, Range}; + +struct Foo; + +struct Bar; + +impl Bar { + fn as_bytes(&self) -> &[u8] { + &[0, 1, 2, 3] + } +} + +impl Index> for Foo { + type Output = Bar; + + fn index(&self, _: Range) -> &Self::Output { + &Bar + } +} + +fn main() { + let s = "Lorem ipsum"; + let string: String = "dolor sit amet".to_owned(); + + let bytes = s[1..5].as_bytes(); + let bytes = string[1..].as_bytes(); + let bytes = "consectetur adipiscing"[..=5].as_bytes(); + + let f = Foo; + let bytes = f[0..4].as_bytes(); +} diff --git a/src/tools/clippy/tests/ui/sliced_string_as_bytes.stderr b/src/tools/clippy/tests/ui/sliced_string_as_bytes.stderr new file mode 100644 index 0000000000000..1342f4c01a484 --- /dev/null +++ b/src/tools/clippy/tests/ui/sliced_string_as_bytes.stderr @@ -0,0 +1,23 @@ +error: calling `as_bytes` after slicing a string + --> tests/ui/sliced_string_as_bytes.rs:28:17 + | +LL | let bytes = s[1..5].as_bytes(); + | ^^^^^^^^^^^^^^^^^^ help: try: `&s.as_bytes()[1..5]` + | + = note: `-D clippy::sliced-string-as-bytes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::sliced_string_as_bytes)]` + +error: calling `as_bytes` after slicing a string + --> tests/ui/sliced_string_as_bytes.rs:29:17 + | +LL | let bytes = string[1..].as_bytes(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&string.as_bytes()[1..]` + +error: calling `as_bytes` after slicing a string + --> tests/ui/sliced_string_as_bytes.rs:30:17 + | +LL | let bytes = "consectetur adipiscing"[..=5].as_bytes(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&"consectetur adipiscing".as_bytes()[..=5]` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.fixed b/src/tools/clippy/tests/ui/slow_vector_initialization.fixed new file mode 100644 index 0000000000000..a8570366e646d --- /dev/null +++ b/src/tools/clippy/tests/ui/slow_vector_initialization.fixed @@ -0,0 +1,86 @@ +#![allow(clippy::useless_vec, clippy::manual_repeat_n)] + +use std::iter::repeat; +fn main() { + resize_vector(); + extend_vector(); + mixed_extend_resize_vector(); + from_empty_vec(); +} + +fn extend_vector() { + // Extend with constant expression + let len = 300; + let mut vec1 = vec![0; len]; + + // Extend with len expression + let mut vec2 = vec![0; len - 10]; + + // Extend with mismatching expression should not be warned + let mut vec3 = Vec::with_capacity(24322); + vec3.extend(repeat(0).take(2)); + + let mut vec4 = vec![0; len]; +} + +fn mixed_extend_resize_vector() { + // Mismatching len + let mut mismatching_len = Vec::with_capacity(30); + mismatching_len.extend(repeat(0).take(40)); + + // Slow initialization + let mut resized_vec = vec![0; 30]; + + let mut extend_vec = vec![0; 30]; +} + +fn resize_vector() { + // Resize with constant expression + let len = 300; + let mut vec1 = vec![0; len]; + + // Resize mismatch len + let mut vec2 = Vec::with_capacity(200); + vec2.resize(10, 0); + + // Resize with len expression + let mut vec3 = vec![0; len - 10]; + + let mut vec4 = vec![0; len]; + + // Reinitialization should be warned + vec1 = vec![0; 10]; +} + +fn from_empty_vec() { + // Resize with constant expression + let len = 300; + let mut vec1 = vec![0; len]; + + // Resize with len expression + let mut vec3 = vec![0; len - 10]; + + // Reinitialization should be warned + vec1 = vec![0; 10]; + + vec1 = vec![0; 10]; + + macro_rules! x { + () => { + vec![] + }; + } + + // `vec![]` comes from another macro, don't warn + vec1 = x!(); + vec1.resize(10, 0); +} + +fn do_stuff(vec: &mut [u8]) {} + +fn extend_vector_with_manipulations_between() { + let len = 300; + let mut vec1: Vec = Vec::with_capacity(len); + do_stuff(&mut vec1); + vec1.extend(repeat(0).take(len)); +} diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.rs b/src/tools/clippy/tests/ui/slow_vector_initialization.rs index 2ba87f4125000..4b30fad409e3a 100644 --- a/src/tools/clippy/tests/ui/slow_vector_initialization.rs +++ b/src/tools/clippy/tests/ui/slow_vector_initialization.rs @@ -1,4 +1,5 @@ -//@no-rustfix +#![allow(clippy::useless_vec, clippy::manual_repeat_n)] + use std::iter::repeat; fn main() { resize_vector(); diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.stderr b/src/tools/clippy/tests/ui/slow_vector_initialization.stderr index 7f4b9f7b67a41..4a25cafcddf26 100644 --- a/src/tools/clippy/tests/ui/slow_vector_initialization.stderr +++ b/src/tools/clippy/tests/ui/slow_vector_initialization.stderr @@ -1,5 +1,5 @@ error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:13:20 + --> tests/ui/slow_vector_initialization.rs:14:20 | LL | let mut vec1 = Vec::with_capacity(len); | ____________________^ @@ -11,7 +11,7 @@ LL | | vec1.extend(repeat(0).take(len)); = help: to override `-D warnings` add `#[allow(clippy::slow_vector_initialization)]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:19:20 + --> tests/ui/slow_vector_initialization.rs:20:20 | LL | let mut vec2 = Vec::with_capacity(len - 10); | ____________________^ @@ -20,7 +20,7 @@ LL | | vec2.extend(repeat(0).take(len - 10)); | |_________________________________________^ help: consider replacing this with: `vec![0; len - 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:27:20 + --> tests/ui/slow_vector_initialization.rs:28:20 | LL | let mut vec4 = Vec::with_capacity(len); | ____________________^ @@ -29,7 +29,7 @@ LL | | vec4.extend(repeat(0).take(vec4.capacity())); | |________________________________________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:38:27 + --> tests/ui/slow_vector_initialization.rs:39:27 | LL | let mut resized_vec = Vec::with_capacity(30); | ___________________________^ @@ -38,7 +38,7 @@ LL | | resized_vec.resize(30, 0); | |_____________________________^ help: consider replacing this with: `vec![0; 30]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:42:26 + --> tests/ui/slow_vector_initialization.rs:43:26 | LL | let mut extend_vec = Vec::with_capacity(30); | __________________________^ @@ -47,7 +47,7 @@ LL | | extend_vec.extend(repeat(0).take(30)); | |_________________________________________^ help: consider replacing this with: `vec![0; 30]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:50:20 + --> tests/ui/slow_vector_initialization.rs:51:20 | LL | let mut vec1 = Vec::with_capacity(len); | ____________________^ @@ -56,7 +56,7 @@ LL | | vec1.resize(len, 0); | |_______________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:59:20 + --> tests/ui/slow_vector_initialization.rs:60:20 | LL | let mut vec3 = Vec::with_capacity(len - 10); | ____________________^ @@ -65,7 +65,7 @@ LL | | vec3.resize(len - 10, 0); | |____________________________^ help: consider replacing this with: `vec![0; len - 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:63:20 + --> tests/ui/slow_vector_initialization.rs:64:20 | LL | let mut vec4 = Vec::with_capacity(len); | ____________________^ @@ -74,7 +74,7 @@ LL | | vec4.resize(vec4.capacity(), 0); | |___________________________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:68:12 + --> tests/ui/slow_vector_initialization.rs:69:12 | LL | vec1 = Vec::with_capacity(10); | ____________^ @@ -83,7 +83,7 @@ LL | | vec1.resize(10, 0); | |______________________^ help: consider replacing this with: `vec![0; 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:76:20 + --> tests/ui/slow_vector_initialization.rs:77:20 | LL | let mut vec1 = Vec::new(); | ____________________^ @@ -92,7 +92,7 @@ LL | | vec1.resize(len, 0); | |_______________________^ help: consider replacing this with: `vec![0; len]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:81:20 + --> tests/ui/slow_vector_initialization.rs:82:20 | LL | let mut vec3 = Vec::new(); | ____________________^ @@ -101,7 +101,7 @@ LL | | vec3.resize(len - 10, 0); | |____________________________^ help: consider replacing this with: `vec![0; len - 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:86:12 + --> tests/ui/slow_vector_initialization.rs:87:12 | LL | vec1 = Vec::new(); | ____________^ @@ -110,7 +110,7 @@ LL | | vec1.resize(10, 0); | |______________________^ help: consider replacing this with: `vec![0; 10]` error: slow zero-filling initialization - --> tests/ui/slow_vector_initialization.rs:90:12 + --> tests/ui/slow_vector_initialization.rs:91:12 | LL | vec1 = vec![]; | ____________^ diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.fixed b/src/tools/clippy/tests/ui/unnecessary_map_or.fixed index efea28e7045c8..5a6e77a06b8f5 100644 --- a/src/tools/clippy/tests/ui/unnecessary_map_or.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_map_or.fixed @@ -82,3 +82,20 @@ fn msrv_1_81() { // is_none_or added in 1.82.0 let _ = Some(5).map_or(true, |n| n == if 2 > 1 { n } else { 0 }); } + +fn with_refs(o: &mut Option) -> bool { + o.is_none_or(|n| n > 5) || (o as &Option).is_none_or(|n| n < 5) +} + +struct S; + +impl std::ops::Deref for S { + type Target = Option; + fn deref(&self) -> &Self::Target { + &Some(0) + } +} + +fn with_deref(o: &S) -> bool { + o.is_none_or(|n| n > 5) +} diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.rs b/src/tools/clippy/tests/ui/unnecessary_map_or.rs index 05a0ca816ef6d..5ba63121659fc 100644 --- a/src/tools/clippy/tests/ui/unnecessary_map_or.rs +++ b/src/tools/clippy/tests/ui/unnecessary_map_or.rs @@ -85,3 +85,20 @@ fn msrv_1_81() { // is_none_or added in 1.82.0 let _ = Some(5).map_or(true, |n| n == if 2 > 1 { n } else { 0 }); } + +fn with_refs(o: &mut Option) -> bool { + o.map_or(true, |n| n > 5) || (o as &Option).map_or(true, |n| n < 5) +} + +struct S; + +impl std::ops::Deref for S { + type Target = Option; + fn deref(&self) -> &Self::Target { + &Some(0) + } +} + +fn with_deref(o: &S) -> bool { + o.map_or(true, |n| n > 5) +} diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.stderr b/src/tools/clippy/tests/ui/unnecessary_map_or.stderr index 2b78996d5f3e3..2ae327f0bf88e 100644 --- a/src/tools/clippy/tests/ui/unnecessary_map_or.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_map_or.stderr @@ -2,16 +2,25 @@ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:13:13 | LL | let _ = Some(5).map_or(false, |n| n == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `Some(5) == Some(5)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `-D clippy::unnecessary-map-or` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::unnecessary_map_or)]` +help: use a standard comparison instead + | +LL | let _ = Some(5) == Some(5); + | ~~~~~~~~~~~~~~~~~~ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:14:13 | LL | let _ = Some(5).map_or(true, |n| n != 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `Some(5) != Some(5)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a standard comparison instead + | +LL | let _ = Some(5) != Some(5); + | ~~~~~~~~~~~~~~~~~~ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:15:13 @@ -21,7 +30,12 @@ LL | let _ = Some(5).map_or(false, |n| { LL | | let _ = 1; LL | | n == 5 LL | | }); - | |______^ help: use a standard comparison instead: `Some(5) == Some(5)` + | |______^ + | +help: use a standard comparison instead + | +LL | let _ = Some(5) == Some(5); + | ~~~~~~~~~~~~~~~~~~ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:19:13 @@ -35,113 +49,243 @@ LL | | }); | help: use is_some_and instead | -LL ~ let _ = Some(5).is_some_and(|n| { -LL + let _ = n; -LL + 6 >= 5 -LL ~ }); +LL - let _ = Some(5).map_or(false, |n| { +LL + let _ = Some(5).is_some_and(|n| { | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:23:13 | LL | let _ = Some(vec![5]).map_or(false, |n| n == [5]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(vec![5]).is_some_and(|n| n == [5])` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_some_and instead + | +LL - let _ = Some(vec![5]).map_or(false, |n| n == [5]); +LL + let _ = Some(vec![5]).is_some_and(|n| n == [5]); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:24:13 | LL | let _ = Some(vec![1]).map_or(false, |n| vec![2] == n); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(vec![1]).is_some_and(|n| vec![2] == n)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_some_and instead + | +LL - let _ = Some(vec![1]).map_or(false, |n| vec![2] == n); +LL + let _ = Some(vec![1]).is_some_and(|n| vec![2] == n); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:25:13 | LL | let _ = Some(5).map_or(false, |n| n == n); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(|n| n == n)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_some_and instead + | +LL - let _ = Some(5).map_or(false, |n| n == n); +LL + let _ = Some(5).is_some_and(|n| n == n); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:26:13 | LL | let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(|n| n == if 2 > 1 { n } else { 0 })` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_some_and instead + | +LL - let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 }); +LL + let _ = Some(5).is_some_and(|n| n == if 2 > 1 { n } else { 0 }); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:27:13 | LL | let _ = Ok::, i32>(vec![5]).map_or(false, |n| n == [5]); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `Ok::, i32>(vec![5]).is_ok_and(|n| n == [5])` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_ok_and instead + | +LL - let _ = Ok::, i32>(vec![5]).map_or(false, |n| n == [5]); +LL + let _ = Ok::, i32>(vec![5]).is_ok_and(|n| n == [5]); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:28:13 | LL | let _ = Ok::(5).map_or(false, |n| n == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `Ok::(5) == Ok(5)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a standard comparison instead + | +LL | let _ = Ok::(5) == Ok(5); + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:29:13 | LL | let _ = Some(5).map_or(false, |n| n == 5).then(|| 1); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a standard comparison instead + | +LL | let _ = (Some(5) == Some(5)).then(|| 1); + | ~~~~~~~~~~~~~~~~~~~~ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:30:13 | LL | let _ = Some(5).map_or(true, |n| n == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_none_or instead: `Some(5).is_none_or(|n| n == 5)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_none_or instead + | +LL - let _ = Some(5).map_or(true, |n| n == 5); +LL + let _ = Some(5).is_none_or(|n| n == 5); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:31:13 | LL | let _ = Some(5).map_or(true, |n| 5 == n); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_none_or instead: `Some(5).is_none_or(|n| 5 == n)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_none_or instead + | +LL - let _ = Some(5).map_or(true, |n| 5 == n); +LL + let _ = Some(5).is_none_or(|n| 5 == n); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:32:14 | LL | let _ = !Some(5).map_or(false, |n| n == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a standard comparison instead + | +LL | let _ = !(Some(5) == Some(5)); + | ~~~~~~~~~~~~~~~~~~~~ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:33:13 | LL | let _ = Some(5).map_or(false, |n| n == 5) || false; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a standard comparison instead + | +LL | let _ = (Some(5) == Some(5)) || false; + | ~~~~~~~~~~~~~~~~~~~~ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:34:13 | LL | let _ = Some(5).map_or(false, |n| n == 5) as usize; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a standard comparison instead + | +LL | let _ = (Some(5) == Some(5)) as usize; + | ~~~~~~~~~~~~~~~~~~~~ error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:58:13 | LL | let _ = r.map_or(false, |x| x == 7); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `r.is_ok_and(|x| x == 7)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_ok_and instead + | +LL - let _ = r.map_or(false, |x| x == 7); +LL + let _ = r.is_ok_and(|x| x == 7); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:63:13 | LL | let _ = r.map_or(false, func); - | ^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `r.is_ok_and(func)` + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_ok_and instead + | +LL - let _ = r.map_or(false, func); +LL + let _ = r.is_ok_and(func); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:64:13 | LL | let _ = Some(5).map_or(false, func); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(func)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_some_and instead + | +LL - let _ = Some(5).map_or(false, func); +LL + let _ = Some(5).is_some_and(func); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:65:13 | LL | let _ = Some(5).map_or(true, func); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_none_or instead: `Some(5).is_none_or(func)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_none_or instead + | +LL - let _ = Some(5).map_or(true, func); +LL + let _ = Some(5).is_none_or(func); + | error: this `map_or` can be simplified --> tests/ui/unnecessary_map_or.rs:70:13 | LL | let _ = r.map_or(false, |x| x == 8); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `r == Ok(8)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use a standard comparison instead + | +LL | let _ = r == Ok(8); + | ~~~~~~~~~~ + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:90:5 + | +LL | o.map_or(true, |n| n > 5) || (o as &Option).map_or(true, |n| n < 5) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_none_or instead + | +LL - o.map_or(true, |n| n > 5) || (o as &Option).map_or(true, |n| n < 5) +LL + o.is_none_or(|n| n > 5) || (o as &Option).map_or(true, |n| n < 5) + | + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:90:34 + | +LL | o.map_or(true, |n| n > 5) || (o as &Option).map_or(true, |n| n < 5) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_none_or instead + | +LL - o.map_or(true, |n| n > 5) || (o as &Option).map_or(true, |n| n < 5) +LL + o.map_or(true, |n| n > 5) || (o as &Option).is_none_or(|n| n < 5) + | + +error: this `map_or` can be simplified + --> tests/ui/unnecessary_map_or.rs:103:5 + | +LL | o.map_or(true, |n| n > 5) + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use is_none_or instead + | +LL - o.map_or(true, |n| n > 5) +LL + o.is_none_or(|n| n > 5) + | -error: aborting due to 21 previous errors +error: aborting due to 24 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2021.fixed b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2021.fixed new file mode 100644 index 0000000000000..7a3b79553dea8 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2021.fixed @@ -0,0 +1,57 @@ +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 + +#![warn(clippy::unnecessary_semicolon)] +#![feature(postfix_match)] +#![allow(clippy::single_match)] + +fn no_lint(mut x: u32) -> Option { + Some(())?; + + { + let y = 3; + dbg!(x + y) + }; + + { + let (mut a, mut b) = (10, 20); + (a, b) = (b + 1, a + 1); + } + + Some(0) +} + +fn main() { + let mut a = 3; + if a == 2 { + println!("This is weird"); + } + //~^ ERROR: unnecessary semicolon + + a.match { + 3 => println!("three"), + _ => println!("not three"), + } + //~^ ERROR: unnecessary semicolon +} + +// This is a problem in edition 2021 and below +fn borrow_issue() { + let v = std::cell::RefCell::new(Some(vec![1])); + match &*v.borrow() { + Some(v) => { + dbg!(v); + }, + None => {}, + }; +} + +fn no_borrow_issue(a: u32, b: u32) { + match Some(a + b) { + Some(v) => { + dbg!(v); + }, + None => {}, + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2021.stderr b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2021.stderr new file mode 100644 index 0000000000000..ccff33084172e --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2021.stderr @@ -0,0 +1,23 @@ +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:29:6 + | +LL | }; + | ^ help: remove + | + = note: `-D clippy::unnecessary-semicolon` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_semicolon)]` + +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:35:6 + | +LL | }; + | ^ help: remove + +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:56:6 + | +LL | }; + | ^ help: remove + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2024.fixed b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2024.fixed new file mode 100644 index 0000000000000..d186d5e7ebc4e --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2024.fixed @@ -0,0 +1,57 @@ +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 + +#![warn(clippy::unnecessary_semicolon)] +#![feature(postfix_match)] +#![allow(clippy::single_match)] + +fn no_lint(mut x: u32) -> Option { + Some(())?; + + { + let y = 3; + dbg!(x + y) + }; + + { + let (mut a, mut b) = (10, 20); + (a, b) = (b + 1, a + 1); + } + + Some(0) +} + +fn main() { + let mut a = 3; + if a == 2 { + println!("This is weird"); + } + //~^ ERROR: unnecessary semicolon + + a.match { + 3 => println!("three"), + _ => println!("not three"), + } + //~^ ERROR: unnecessary semicolon +} + +// This is a problem in edition 2021 and below +fn borrow_issue() { + let v = std::cell::RefCell::new(Some(vec![1])); + match &*v.borrow() { + Some(v) => { + dbg!(v); + }, + None => {}, + } +} + +fn no_borrow_issue(a: u32, b: u32) { + match Some(a + b) { + Some(v) => { + dbg!(v); + }, + None => {}, + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2024.stderr b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2024.stderr new file mode 100644 index 0000000000000..4e526af2147db --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2024.stderr @@ -0,0 +1,29 @@ +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:29:6 + | +LL | }; + | ^ help: remove + | + = note: `-D clippy::unnecessary-semicolon` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_semicolon)]` + +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:35:6 + | +LL | }; + | ^ help: remove + +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:47:6 + | +LL | }; + | ^ help: remove + +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:56:6 + | +LL | }; + | ^ help: remove + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_semicolon.fixed b/src/tools/clippy/tests/ui/unnecessary_semicolon.fixed new file mode 100644 index 0000000000000..36d5c7806fe84 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_semicolon.fixed @@ -0,0 +1,32 @@ +#![warn(clippy::unnecessary_semicolon)] +#![feature(postfix_match)] + +fn no_lint(mut x: u32) -> Option { + Some(())?; + + { + let y = 3; + dbg!(x + y) + }; + + { + let (mut a, mut b) = (10, 20); + (a, b) = (b + 1, a + 1); + } + + Some(0) +} + +fn main() { + let mut a = 3; + if a == 2 { + println!("This is weird"); + } + //~^ ERROR: unnecessary semicolon + + a.match { + 3 => println!("three"), + _ => println!("not three"), + } + //~^ ERROR: unnecessary semicolon +} diff --git a/src/tools/clippy/tests/ui/unnecessary_semicolon.rs b/src/tools/clippy/tests/ui/unnecessary_semicolon.rs new file mode 100644 index 0000000000000..3028c5b27b34d --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_semicolon.rs @@ -0,0 +1,57 @@ +//@revisions: edition2021 edition2024 +//@[edition2021] edition:2021 +//@[edition2024] edition:2024 + +#![warn(clippy::unnecessary_semicolon)] +#![feature(postfix_match)] +#![allow(clippy::single_match)] + +fn no_lint(mut x: u32) -> Option { + Some(())?; + + { + let y = 3; + dbg!(x + y) + }; + + { + let (mut a, mut b) = (10, 20); + (a, b) = (b + 1, a + 1); + } + + Some(0) +} + +fn main() { + let mut a = 3; + if a == 2 { + println!("This is weird"); + }; + //~^ ERROR: unnecessary semicolon + + a.match { + 3 => println!("three"), + _ => println!("not three"), + }; + //~^ ERROR: unnecessary semicolon +} + +// This is a problem in edition 2021 and below +fn borrow_issue() { + let v = std::cell::RefCell::new(Some(vec![1])); + match &*v.borrow() { + Some(v) => { + dbg!(v); + }, + None => {}, + }; +} + +fn no_borrow_issue(a: u32, b: u32) { + match Some(a + b) { + Some(v) => { + dbg!(v); + }, + None => {}, + }; +} diff --git a/src/tools/clippy/tests/ui/unnecessary_semicolon.stderr b/src/tools/clippy/tests/ui/unnecessary_semicolon.stderr new file mode 100644 index 0000000000000..e6bf36e81e886 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_semicolon.stderr @@ -0,0 +1,17 @@ +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:24:6 + | +LL | }; + | ^ help: remove + | + = note: `-D clippy::unnecessary-semicolon` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_semicolon)]` + +error: unnecessary semicolon + --> tests/ui/unnecessary_semicolon.rs:30:6 + | +LL | }; + | ^ help: remove + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed index fdcac8fb08dcf..027dac419375b 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed @@ -585,3 +585,9 @@ fn borrow_checks() { HashSet::::new().foo::<&str>(&"".to_owned()); HashSet::::new().get(&1.to_string()); } + +fn issue13624() -> impl IntoIterator { + let cow: Cow<'_, Vec> = Cow::Owned(vec![String::from("foo")]); + + cow.into_owned().into_iter() +} diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs index 10a9727a9a798..b89f3d552f84d 100644 --- a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs +++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs @@ -585,3 +585,9 @@ fn borrow_checks() { HashSet::::new().foo::<&str>(&"".to_owned()); HashSet::::new().get(&1.to_string()); } + +fn issue13624() -> impl IntoIterator { + let cow: Cow<'_, Vec> = Cow::Owned(vec![String::from("foo")]); + + cow.into_owned().into_iter() +} diff --git a/src/tools/clippy/tests/ui/unneeded_struct_pattern.fixed b/src/tools/clippy/tests/ui/unneeded_struct_pattern.fixed new file mode 100644 index 0000000000000..5bd269896a6b1 --- /dev/null +++ b/src/tools/clippy/tests/ui/unneeded_struct_pattern.fixed @@ -0,0 +1,177 @@ +//@aux-build:non-exhaustive-enum.rs +#![allow( + clippy::manual_unwrap_or_default, + clippy::manual_unwrap_or, + clippy::redundant_pattern_matching +)] +#![warn(clippy::unneeded_struct_pattern)] + +extern crate non_exhaustive_enum; +use non_exhaustive_enum::*; + +fn noop() {} + +fn main() { + match Some(114514) { + Some(v) => v, + None => 0, + }; + + match Some(1919810) { + Some(v) => v, + None => 0, + }; + + match Some(123456) { + Some(v) => v, + None => 0, + }; + + match Some(Some(123456)) { + Some(Some(v)) => v, + Some(None) => 0, + None => 0, + }; + + if let None = Some(0) {} + if let None = Some(0) {} + if let Some(None) = Some(Some(0)) {} + let None = Some(0) else { panic!() }; + let None = Some(0) else { panic!() }; + let Some(None) = Some(Some(0)) else { panic!() }; + + enum Custom { + HasFields { + field: i32, + }, + HasBracketsNoFields {}, + NoBrackets, + #[non_exhaustive] + NoBracketsNonExhaustive, + Init, + }; + + match Custom::Init { + Custom::HasFields { field: value } => value, + Custom::HasBracketsNoFields {} => 0, + Custom::NoBrackets => 0, //~ ERROR: struct pattern is not needed for a unit variant + Custom::NoBracketsNonExhaustive => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match Custom::Init { + Custom::HasFields { field: value } => value, + Custom::HasBracketsNoFields { .. } => 0, + Custom::NoBrackets => 0, //~ ERROR: struct pattern is not needed for a unit variant + Custom::NoBracketsNonExhaustive => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match Custom::Init { + Custom::NoBrackets if true => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match Custom::Init { + Custom::NoBrackets | Custom::NoBracketsNonExhaustive => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + if let Custom::HasFields { field: value } = Custom::Init { + noop(); + } + if let Custom::HasBracketsNoFields {} = Custom::Init { + noop(); + } + if let Custom::HasBracketsNoFields { .. } = Custom::Init { + noop(); + } + if let Custom::NoBrackets = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + if let Custom::NoBrackets = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + if let Custom::NoBrackets | Custom::NoBracketsNonExhaustive = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + if let Custom::NoBracketsNonExhaustive = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + if let Custom::NoBracketsNonExhaustive = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + + let Custom::HasFields { field: value } = Custom::Init else { + panic!() + }; + + let Custom::HasBracketsNoFields {} = Custom::Init else { + panic!() + }; + + let Custom::HasBracketsNoFields { .. } = Custom::Init else { + panic!() + }; + let Custom::NoBrackets = Custom::Init else { panic!() }; //~ ERROR: struct pattern is not needed for a unit variant + + let Custom::NoBrackets = Custom::Init else { + //~^ ERROR: struct pattern is not needed for a unit variant + panic!() + }; + let Custom::NoBracketsNonExhaustive = Custom::Init else { + //~^ ERROR: struct pattern is not needed for a unit variant + panic!() + }; + let Custom::NoBracketsNonExhaustive = Custom::Init else { + //~^ ERROR: struct pattern is not needed for a unit variant + panic!() + }; + + enum Refutable { + Variant, + } + + fn pat_in_fn_param_1(Refutable::Variant: Refutable) {} //~ ERROR: struct pattern is not needed for a unit variant + fn pat_in_fn_param_2(Refutable::Variant: Refutable) {} //~ ERROR: struct pattern is not needed for a unit variant + + for Refutable::Variant in [] {} //~ ERROR: struct pattern is not needed for a unit variant + for Refutable::Variant in [] {} //~ ERROR: struct pattern is not needed for a unit variant +} + +fn external_crate() { + use ExtNonExhaustiveVariant::*; + + match ExhaustiveUnit { + // Expected + ExhaustiveUnit => 0, + _ => 0, + }; + + match ExhaustiveUnit { + // Exhaustive variant + ExhaustiveUnit => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match ExhaustiveUnit { + // Exhaustive variant + ExhaustiveUnit => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match ExhaustiveUnit { + ExhaustiveUnit => 0, + // vvvvv Non-exhaustive variants, should all be ignored + Unit { .. } => 0, + Tuple { 0: field, .. } => field, + StructNoField { .. } => 0, + Struct { field, .. } => field, + _ => 0, + }; +} diff --git a/src/tools/clippy/tests/ui/unneeded_struct_pattern.rs b/src/tools/clippy/tests/ui/unneeded_struct_pattern.rs new file mode 100644 index 0000000000000..c7658617ad376 --- /dev/null +++ b/src/tools/clippy/tests/ui/unneeded_struct_pattern.rs @@ -0,0 +1,177 @@ +//@aux-build:non-exhaustive-enum.rs +#![allow( + clippy::manual_unwrap_or_default, + clippy::manual_unwrap_or, + clippy::redundant_pattern_matching +)] +#![warn(clippy::unneeded_struct_pattern)] + +extern crate non_exhaustive_enum; +use non_exhaustive_enum::*; + +fn noop() {} + +fn main() { + match Some(114514) { + Some(v) => v, + None {} => 0, + }; + + match Some(1919810) { + Some(v) => v, + None { .. } => 0, + }; + + match Some(123456) { + Some(v) => v, + None => 0, + }; + + match Some(Some(123456)) { + Some(Some(v)) => v, + Some(None {}) => 0, + None {} => 0, + }; + + if let None {} = Some(0) {} + if let None { .. } = Some(0) {} + if let Some(None {}) = Some(Some(0)) {} + let None {} = Some(0) else { panic!() }; + let None { .. } = Some(0) else { panic!() }; + let Some(None {}) = Some(Some(0)) else { panic!() }; + + enum Custom { + HasFields { + field: i32, + }, + HasBracketsNoFields {}, + NoBrackets, + #[non_exhaustive] + NoBracketsNonExhaustive, + Init, + }; + + match Custom::Init { + Custom::HasFields { field: value } => value, + Custom::HasBracketsNoFields {} => 0, + Custom::NoBrackets {} => 0, //~ ERROR: struct pattern is not needed for a unit variant + Custom::NoBracketsNonExhaustive {} => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match Custom::Init { + Custom::HasFields { field: value } => value, + Custom::HasBracketsNoFields { .. } => 0, + Custom::NoBrackets { .. } => 0, //~ ERROR: struct pattern is not needed for a unit variant + Custom::NoBracketsNonExhaustive { .. } => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match Custom::Init { + Custom::NoBrackets {} if true => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match Custom::Init { + Custom::NoBrackets {} | Custom::NoBracketsNonExhaustive {} => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + if let Custom::HasFields { field: value } = Custom::Init { + noop(); + } + if let Custom::HasBracketsNoFields {} = Custom::Init { + noop(); + } + if let Custom::HasBracketsNoFields { .. } = Custom::Init { + noop(); + } + if let Custom::NoBrackets {} = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + if let Custom::NoBrackets { .. } = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + if let Custom::NoBrackets {} | Custom::NoBracketsNonExhaustive {} = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + if let Custom::NoBracketsNonExhaustive {} = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + if let Custom::NoBracketsNonExhaustive { .. } = Custom::Init { + //~^ ERROR: struct pattern is not needed for a unit variant + noop(); + } + + let Custom::HasFields { field: value } = Custom::Init else { + panic!() + }; + + let Custom::HasBracketsNoFields {} = Custom::Init else { + panic!() + }; + + let Custom::HasBracketsNoFields { .. } = Custom::Init else { + panic!() + }; + let Custom::NoBrackets {} = Custom::Init else { panic!() }; //~ ERROR: struct pattern is not needed for a unit variant + + let Custom::NoBrackets { .. } = Custom::Init else { + //~^ ERROR: struct pattern is not needed for a unit variant + panic!() + }; + let Custom::NoBracketsNonExhaustive {} = Custom::Init else { + //~^ ERROR: struct pattern is not needed for a unit variant + panic!() + }; + let Custom::NoBracketsNonExhaustive { .. } = Custom::Init else { + //~^ ERROR: struct pattern is not needed for a unit variant + panic!() + }; + + enum Refutable { + Variant, + } + + fn pat_in_fn_param_1(Refutable::Variant {}: Refutable) {} //~ ERROR: struct pattern is not needed for a unit variant + fn pat_in_fn_param_2(Refutable::Variant { .. }: Refutable) {} //~ ERROR: struct pattern is not needed for a unit variant + + for Refutable::Variant {} in [] {} //~ ERROR: struct pattern is not needed for a unit variant + for Refutable::Variant { .. } in [] {} //~ ERROR: struct pattern is not needed for a unit variant +} + +fn external_crate() { + use ExtNonExhaustiveVariant::*; + + match ExhaustiveUnit { + // Expected + ExhaustiveUnit => 0, + _ => 0, + }; + + match ExhaustiveUnit { + // Exhaustive variant + ExhaustiveUnit { .. } => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match ExhaustiveUnit { + // Exhaustive variant + ExhaustiveUnit {} => 0, //~ ERROR: struct pattern is not needed for a unit variant + _ => 0, + }; + + match ExhaustiveUnit { + ExhaustiveUnit => 0, + // vvvvv Non-exhaustive variants, should all be ignored + Unit { .. } => 0, + Tuple { 0: field, .. } => field, + StructNoField { .. } => 0, + Struct { field, .. } => field, + _ => 0, + }; +} diff --git a/src/tools/clippy/tests/ui/unneeded_struct_pattern.stderr b/src/tools/clippy/tests/ui/unneeded_struct_pattern.stderr new file mode 100644 index 0000000000000..3a7f595838020 --- /dev/null +++ b/src/tools/clippy/tests/ui/unneeded_struct_pattern.stderr @@ -0,0 +1,203 @@ +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:17:13 + | +LL | None {} => 0, + | ^^^ help: remove the struct pattern + | + = note: `-D clippy::unneeded-struct-pattern` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unneeded_struct_pattern)]` + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:22:13 + | +LL | None { .. } => 0, + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:32:18 + | +LL | Some(None {}) => 0, + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:33:13 + | +LL | None {} => 0, + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:36:16 + | +LL | if let None {} = Some(0) {} + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:37:16 + | +LL | if let None { .. } = Some(0) {} + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:38:21 + | +LL | if let Some(None {}) = Some(Some(0)) {} + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:39:13 + | +LL | let None {} = Some(0) else { panic!() }; + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:40:13 + | +LL | let None { .. } = Some(0) else { panic!() }; + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:41:18 + | +LL | let Some(None {}) = Some(Some(0)) else { panic!() }; + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:57:27 + | +LL | Custom::NoBrackets {} => 0, + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:58:40 + | +LL | Custom::NoBracketsNonExhaustive {} => 0, + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:65:27 + | +LL | Custom::NoBrackets { .. } => 0, + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:66:40 + | +LL | Custom::NoBracketsNonExhaustive { .. } => 0, + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:71:27 + | +LL | Custom::NoBrackets {} if true => 0, + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:76:27 + | +LL | Custom::NoBrackets {} | Custom::NoBracketsNonExhaustive {} => 0, + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:76:64 + | +LL | Custom::NoBrackets {} | Custom::NoBracketsNonExhaustive {} => 0, + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:89:30 + | +LL | if let Custom::NoBrackets {} = Custom::Init { + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:93:30 + | +LL | if let Custom::NoBrackets { .. } = Custom::Init { + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:97:30 + | +LL | if let Custom::NoBrackets {} | Custom::NoBracketsNonExhaustive {} = Custom::Init { + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:97:67 + | +LL | if let Custom::NoBrackets {} | Custom::NoBracketsNonExhaustive {} = Custom::Init { + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:101:43 + | +LL | if let Custom::NoBracketsNonExhaustive {} = Custom::Init { + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:105:43 + | +LL | if let Custom::NoBracketsNonExhaustive { .. } = Custom::Init { + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:121:27 + | +LL | let Custom::NoBrackets {} = Custom::Init else { panic!() }; + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:123:27 + | +LL | let Custom::NoBrackets { .. } = Custom::Init else { + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:127:40 + | +LL | let Custom::NoBracketsNonExhaustive {} = Custom::Init else { + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:131:40 + | +LL | let Custom::NoBracketsNonExhaustive { .. } = Custom::Init else { + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:140:44 + | +LL | fn pat_in_fn_param_1(Refutable::Variant {}: Refutable) {} + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:141:44 + | +LL | fn pat_in_fn_param_2(Refutable::Variant { .. }: Refutable) {} + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:143:27 + | +LL | for Refutable::Variant {} in [] {} + | ^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:144:27 + | +LL | for Refutable::Variant { .. } in [] {} + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:158:23 + | +LL | ExhaustiveUnit { .. } => 0, + | ^^^^^^^ help: remove the struct pattern + +error: struct pattern is not needed for a unit variant + --> tests/ui/unneeded_struct_pattern.rs:164:23 + | +LL | ExhaustiveUnit {} => 0, + | ^^^ help: remove the struct pattern + +error: aborting due to 33 previous errors + diff --git a/src/tools/clippy/tests/ui/useless_conversion.fixed b/src/tools/clippy/tests/ui/useless_conversion.fixed index 2f7edd92bb7c7..697d437b3885c 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.fixed +++ b/src/tools/clippy/tests/ui/useless_conversion.fixed @@ -342,3 +342,56 @@ fn gen_identity(x: [T; 3]) -> Vec { x.into_iter().collect() //~^ useless_conversion } + +mod issue11819 { + fn takes_into_iter(_: impl IntoIterator) {} + + pub struct MyStruct { + my_field: T, + } + + impl MyStruct { + pub fn with_ref<'a>(&'a mut self) + where + &'a T: IntoIterator, + { + takes_into_iter(&self.my_field); + //~^ useless_conversion + } + + pub fn with_ref_mut<'a>(&'a mut self) + where + &'a mut T: IntoIterator, + { + takes_into_iter(&mut self.my_field); + //~^ useless_conversion + } + + pub fn with_deref(&mut self) + where + T: std::ops::Deref, + Y: IntoIterator + Copy, + { + takes_into_iter(*self.my_field); + //~^ useless_conversion + } + + pub fn with_reborrow<'a, Y: 'a>(&'a mut self) + where + T: std::ops::Deref, + &'a Y: IntoIterator, + { + takes_into_iter(&*self.my_field); + //~^ useless_conversion + } + + pub fn with_reborrow_mut<'a, Y: 'a>(&'a mut self) + where + T: std::ops::Deref + std::ops::DerefMut, + &'a mut Y: IntoIterator, + { + takes_into_iter(&mut *self.my_field); + //~^ useless_conversion + } + } +} diff --git a/src/tools/clippy/tests/ui/useless_conversion.rs b/src/tools/clippy/tests/ui/useless_conversion.rs index eacdf77f90520..4d8ad61a8c995 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.rs +++ b/src/tools/clippy/tests/ui/useless_conversion.rs @@ -342,3 +342,56 @@ fn gen_identity(x: [T; 3]) -> Vec { x.into_iter().map(Into::into).collect() //~^ useless_conversion } + +mod issue11819 { + fn takes_into_iter(_: impl IntoIterator) {} + + pub struct MyStruct { + my_field: T, + } + + impl MyStruct { + pub fn with_ref<'a>(&'a mut self) + where + &'a T: IntoIterator, + { + takes_into_iter(self.my_field.into_iter()); + //~^ useless_conversion + } + + pub fn with_ref_mut<'a>(&'a mut self) + where + &'a mut T: IntoIterator, + { + takes_into_iter(self.my_field.into_iter()); + //~^ useless_conversion + } + + pub fn with_deref(&mut self) + where + T: std::ops::Deref, + Y: IntoIterator + Copy, + { + takes_into_iter(self.my_field.into_iter()); + //~^ useless_conversion + } + + pub fn with_reborrow<'a, Y: 'a>(&'a mut self) + where + T: std::ops::Deref, + &'a Y: IntoIterator, + { + takes_into_iter(self.my_field.into_iter()); + //~^ useless_conversion + } + + pub fn with_reborrow_mut<'a, Y: 'a>(&'a mut self) + where + T: std::ops::Deref + std::ops::DerefMut, + &'a mut Y: IntoIterator, + { + takes_into_iter(self.my_field.into_iter()); + //~^ useless_conversion + } + } +} diff --git a/src/tools/clippy/tests/ui/useless_conversion.stderr b/src/tools/clippy/tests/ui/useless_conversion.stderr index 6aeb382902ba1..ed50f3071862e 100644 --- a/src/tools/clippy/tests/ui/useless_conversion.stderr +++ b/src/tools/clippy/tests/ui/useless_conversion.stderr @@ -122,7 +122,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:189:7 | LL | b(vec![1, 2].into_iter()); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` + | ^^^^^^^^^^------------ + | | + | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:179:13 @@ -134,7 +136,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:190:7 | LL | c(vec![1, 2].into_iter()); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` + | ^^^^^^^^^^------------ + | | + | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:180:18 @@ -146,7 +150,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:191:7 | LL | d(vec![1, 2].into_iter()); - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `vec![1, 2]` + | ^^^^^^^^^^------------ + | | + | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:183:12 @@ -158,7 +164,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:194:7 | LL | b(vec![1, 2].into_iter().into_iter()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`s: `vec![1, 2]` + | ^^^^^^^^^^------------------------ + | | + | help: consider removing the `.into_iter()`s | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:179:13 @@ -170,7 +178,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:195:7 | LL | b(vec![1, 2].into_iter().into_iter().into_iter()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`s: `vec![1, 2]` + | ^^^^^^^^^^------------------------------------ + | | + | help: consider removing the `.into_iter()`s | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:179:13 @@ -182,7 +192,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:241:24 | LL | foo2::([1, 2, 3].into_iter()); - | ^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2, 3]` + | ^^^^^^^^^------------ + | | + | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:220:12 @@ -194,7 +206,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:249:14 | LL | foo3([1, 2, 3].into_iter()); - | ^^^^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2, 3]` + | ^^^^^^^^^------------ + | | + | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:229:12 @@ -206,7 +220,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:258:16 | LL | S1.foo([1, 2].into_iter()); - | ^^^^^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `[1, 2]` + | ^^^^^^------------ + | | + | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:255:27 @@ -218,7 +234,9 @@ error: explicit call to `.into_iter()` in function argument accepting `IntoItera --> tests/ui/useless_conversion.rs:277:44 | LL | v0.into_iter().interleave_shortest(v1.into_iter()); - | ^^^^^^^^^^^^^^ help: consider removing the `.into_iter()`: `v1` + | ^^------------ + | | + | help: consider removing the `.into_iter()` | note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` --> tests/ui/useless_conversion.rs:264:20 @@ -274,5 +292,90 @@ error: useless conversion to the same type: `T` LL | x.into_iter().map(Into::into).collect() | ^^^^^^^^^^^^^^^^ help: consider removing -error: aborting due to 36 previous errors +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> tests/ui/useless_conversion.rs:358:29 + | +LL | takes_into_iter(self.my_field.into_iter()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> tests/ui/useless_conversion.rs:347:32 + | +LL | fn takes_into_iter(_: impl IntoIterator) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider removing the `.into_iter()` + | +LL - takes_into_iter(self.my_field.into_iter()); +LL + takes_into_iter(&self.my_field); + | + +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> tests/ui/useless_conversion.rs:366:29 + | +LL | takes_into_iter(self.my_field.into_iter()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> tests/ui/useless_conversion.rs:347:32 + | +LL | fn takes_into_iter(_: impl IntoIterator) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider removing the `.into_iter()` + | +LL - takes_into_iter(self.my_field.into_iter()); +LL + takes_into_iter(&mut self.my_field); + | + +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> tests/ui/useless_conversion.rs:375:29 + | +LL | takes_into_iter(self.my_field.into_iter()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> tests/ui/useless_conversion.rs:347:32 + | +LL | fn takes_into_iter(_: impl IntoIterator) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider removing the `.into_iter()` + | +LL - takes_into_iter(self.my_field.into_iter()); +LL + takes_into_iter(*self.my_field); + | + +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> tests/ui/useless_conversion.rs:384:29 + | +LL | takes_into_iter(self.my_field.into_iter()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> tests/ui/useless_conversion.rs:347:32 + | +LL | fn takes_into_iter(_: impl IntoIterator) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider removing the `.into_iter()` + | +LL - takes_into_iter(self.my_field.into_iter()); +LL + takes_into_iter(&*self.my_field); + | + +error: explicit call to `.into_iter()` in function argument accepting `IntoIterator` + --> tests/ui/useless_conversion.rs:393:29 + | +LL | takes_into_iter(self.my_field.into_iter()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: this parameter accepts any `IntoIterator`, so you don't need to call `.into_iter()` + --> tests/ui/useless_conversion.rs:347:32 + | +LL | fn takes_into_iter(_: impl IntoIterator) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ +help: consider removing the `.into_iter()` + | +LL - takes_into_iter(self.my_field.into_iter()); +LL + takes_into_iter(&mut *self.my_field); + | + +error: aborting due to 41 previous errors diff --git a/src/tools/clippy/tests/ui/useless_nonzero_new_unchecked.fixed b/src/tools/clippy/tests/ui/useless_nonzero_new_unchecked.fixed new file mode 100644 index 0000000000000..03b34afa54ec9 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_nonzero_new_unchecked.fixed @@ -0,0 +1,52 @@ +#![warn(clippy::useless_nonzero_new_unchecked)] + +use std::num::{NonZero, NonZeroUsize}; + +#[clippy::msrv = "1.83"] +const fn func() -> NonZeroUsize { + const { NonZeroUsize::new(3).unwrap() } + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context +} + +#[clippy::msrv = "1.82"] +const fn func_older() -> NonZeroUsize { + unsafe { NonZeroUsize::new_unchecked(3) } +} + +const fn func_performance_hit_if_linted() -> NonZeroUsize { + unsafe { NonZeroUsize::new_unchecked(3) } +} + +const fn func_may_panic_at_run_time_if_linted(x: usize) -> NonZeroUsize { + unsafe { NonZeroUsize::new_unchecked(x) } +} + +macro_rules! uns { + ($expr:expr) => { + unsafe { $expr } + }; +} + +macro_rules! nzu { + () => { + NonZeroUsize::new_unchecked(1) + }; +} + +fn main() { + const _A: NonZeroUsize = NonZeroUsize::new(3).unwrap(); + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context + + static _B: NonZero = NonZero::::new(42).unwrap(); + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context + + const _C: usize = unsafe { NonZeroUsize::new(3).unwrap().get() }; + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context + + const AUX: usize = 3; + const _D: NonZeroUsize = NonZeroUsize::new(AUX).unwrap(); + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context + + const _X: NonZeroUsize = uns!(NonZeroUsize::new_unchecked(3)); + const _Y: NonZeroUsize = unsafe { nzu!() }; +} diff --git a/src/tools/clippy/tests/ui/useless_nonzero_new_unchecked.rs b/src/tools/clippy/tests/ui/useless_nonzero_new_unchecked.rs new file mode 100644 index 0000000000000..d450e3a03ec58 --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_nonzero_new_unchecked.rs @@ -0,0 +1,52 @@ +#![warn(clippy::useless_nonzero_new_unchecked)] + +use std::num::{NonZero, NonZeroUsize}; + +#[clippy::msrv = "1.83"] +const fn func() -> NonZeroUsize { + const { unsafe { NonZeroUsize::new_unchecked(3) } } + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context +} + +#[clippy::msrv = "1.82"] +const fn func_older() -> NonZeroUsize { + unsafe { NonZeroUsize::new_unchecked(3) } +} + +const fn func_performance_hit_if_linted() -> NonZeroUsize { + unsafe { NonZeroUsize::new_unchecked(3) } +} + +const fn func_may_panic_at_run_time_if_linted(x: usize) -> NonZeroUsize { + unsafe { NonZeroUsize::new_unchecked(x) } +} + +macro_rules! uns { + ($expr:expr) => { + unsafe { $expr } + }; +} + +macro_rules! nzu { + () => { + NonZeroUsize::new_unchecked(1) + }; +} + +fn main() { + const _A: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(3) }; + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context + + static _B: NonZero = unsafe { NonZero::::new_unchecked(42) }; + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context + + const _C: usize = unsafe { NonZeroUsize::new_unchecked(3).get() }; + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context + + const AUX: usize = 3; + const _D: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(AUX) }; + //~^ ERROR: `Option::unwrap()` can be safely used in a `const` context + + const _X: NonZeroUsize = uns!(NonZeroUsize::new_unchecked(3)); + const _Y: NonZeroUsize = unsafe { nzu!() }; +} diff --git a/src/tools/clippy/tests/ui/useless_nonzero_new_unchecked.stderr b/src/tools/clippy/tests/ui/useless_nonzero_new_unchecked.stderr new file mode 100644 index 0000000000000..adb146167633e --- /dev/null +++ b/src/tools/clippy/tests/ui/useless_nonzero_new_unchecked.stderr @@ -0,0 +1,37 @@ +error: `NonZeroUsize::new()` and `Option::unwrap()` can be safely used in a `const` context + --> tests/ui/useless_nonzero_new_unchecked.rs:7:13 + | +LL | const { unsafe { NonZeroUsize::new_unchecked(3) } } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use instead: `NonZeroUsize::new(3).unwrap()` + | + = note: `-D clippy::useless-nonzero-new-unchecked` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::useless_nonzero_new_unchecked)]` + +error: `NonZeroUsize::new()` and `Option::unwrap()` can be safely used in a `const` context + --> tests/ui/useless_nonzero_new_unchecked.rs:37:30 + | +LL | const _A: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(3) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use instead: `NonZeroUsize::new(3).unwrap()` + +error: `NonZero::::new()` and `Option::unwrap()` can be safely used in a `const` context + --> tests/ui/useless_nonzero_new_unchecked.rs:40:30 + | +LL | static _B: NonZero = unsafe { NonZero::::new_unchecked(42) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use instead: `NonZero::::new(42).unwrap()` + +error: `NonZeroUsize::new()` and `Option::unwrap()` can be safely used in a `const` context + --> tests/ui/useless_nonzero_new_unchecked.rs:43:32 + | +LL | const _C: usize = unsafe { NonZeroUsize::new_unchecked(3).get() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use instead: `NonZeroUsize::new(3).unwrap()` + | + = note: the fixed expression does not require an `unsafe` context + +error: `NonZeroUsize::new()` and `Option::unwrap()` can be safely used in a `const` context + --> tests/ui/useless_nonzero_new_unchecked.rs:47:30 + | +LL | const _D: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(AUX) }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use instead: `NonZeroUsize::new(AUX).unwrap()` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/versioncheck.rs b/src/tools/clippy/tests/versioncheck.rs index e29898f068d37..ed357137095ba 100644 --- a/src/tools/clippy/tests/versioncheck.rs +++ b/src/tools/clippy/tests/versioncheck.rs @@ -88,5 +88,5 @@ fn check_that_clippy_has_the_same_major_version_as_rustc() { _ => { panic!("Failed to parse rustc version: {vsplit:?}"); }, - }; + } } diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index eadfd7107c77b..3d35116ebc178 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -29,7 +29,6 @@ users_on_vacation = [ "*" = [ "@Manishearth", "@llogiq", - "@xFrednet", "@Alexendoo", "@dswij", "@Jarcho", diff --git a/src/tools/clippy/util/gh-pages/script.js b/src/tools/clippy/util/gh-pages/script.js index c2197b89c566e..34d76ad642ecd 100644 --- a/src/tools/clippy/util/gh-pages/script.js +++ b/src/tools/clippy/util/gh-pages/script.js @@ -341,8 +341,8 @@ window.filters = { || !filters.levels_filter[lint.level] || !filters.applicabilities_filter[lint.applicability] || !(filters.version_filter["="] === null || lint.version === filters.version_filter["="]) - || !(filters.version_filter["≥"] === null || lint.version > filters.version_filter["≥"]) - || !(filters.version_filter["≤"] === null || lint.version < filters.version_filter["≤"]) + || !(filters.version_filter["≥"] === null || lint.version >= filters.version_filter["≥"]) + || !(filters.version_filter["≤"] === null || lint.version <= filters.version_filter["≤"]) ); if (lint.filteredOut || lint.searchFilteredOut) { lint.elem.style.display = "none"; diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index 5583030b490ae..fb3fc621565e3 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -14,7 +14,9 @@ function endgroup { begingroup "Building Miri" # Global configuration -export RUSTFLAGS="-D warnings" +# We are getting some odd linker warnings on macOS, make sure they do not fail the build. +# (See .) +export RUSTFLAGS="-D warnings -A linker-messages" export CARGO_INCREMENTAL=0 export CARGO_EXTRA_FLAGS="--locked" diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index fa5dbb99e8170..0d405f532fcd8 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -01706e1a34c87656fcbfce198608f4cd2ac6461a +2f0ad2a71e4a4528bb80bcb24bf8fa4e50cb87c2 diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index a2e9c63f79e13..988a0be632774 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -29,11 +29,12 @@ use std::num::NonZero; use std::ops::Range; use std::path::PathBuf; use std::str::FromStr; -use std::sync::atomic::{AtomicI32, Ordering}; use std::sync::Once; +use std::sync::atomic::{AtomicI32, AtomicU32, Ordering}; use miri::{ - BacktraceStyle, BorrowTrackerMethod, MiriConfig, MiriEntryFnType,ProvenanceMode, RetagFields, ValidationMode, + BacktraceStyle, BorrowTrackerMethod, MiriConfig, MiriEntryFnType, ProvenanceMode, RetagFields, + ValidationMode, }; use rustc_abi::ExternAbi; use rustc_data_structures::sync; @@ -182,7 +183,8 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { if let Some(many_seeds) = self.many_seeds.take() { assert!(config.seed.is_none()); let exit_code = sync::IntoDynSyncSend(AtomicI32::new(rustc_driver::EXIT_SUCCESS)); - sync::par_for_each_in(many_seeds.seeds, |seed| { + let num_failed = sync::IntoDynSyncSend(AtomicU32::new(0)); + sync::par_for_each_in(many_seeds.seeds.clone(), |seed| { let mut config = config.clone(); config.seed = Some(seed.into()); eprintln!("Trying seed: {seed}"); @@ -196,8 +198,13 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { std::process::exit(return_code); } exit_code.store(return_code, Ordering::Relaxed); + num_failed.fetch_add(1, Ordering::Relaxed); } }); + let num_failed = num_failed.0.into_inner(); + if num_failed > 0 { + eprintln!("{num_failed}/{total} SEEDS FAILED", total = many_seeds.seeds.count()); + } std::process::exit(exit_code.0.into_inner()); } else { let return_code = miri::eval_entry(tcx, entry_def_id, entry_type, config) @@ -716,10 +723,9 @@ fn main() { // Ensure we have parallelism for many-seeds mode. if many_seeds.is_some() && !rustc_args.iter().any(|arg| arg.starts_with("-Zthreads=")) { - rustc_args.push(format!( - "-Zthreads={}", - std::thread::available_parallelism().map_or(1, |n| n.get()) - )); + // Clamp to 8 threads; things get a lot less efficient beyond that due to lock contention. + let threads = std::thread::available_parallelism().map_or(1, |n| n.get()).min(8); + rustc_args.push(format!("-Zthreads={threads}")); } let many_seeds = many_seeds.map(|seeds| ManySeedsConfig { seeds, keep_going: many_seeds_keep_going }); diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index ca8dbdac125d0..c5538351d7dd1 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -38,9 +38,10 @@ fn try_resolve_did(tcx: TyCtxt<'_>, path: &[&str], namespace: Option) item: DefId, name: &'a str, ) -> impl Iterator + 'a { + let name = Symbol::intern(name); tcx.module_children(item) .iter() - .filter(move |item| item.ident.name.as_str() == name) + .filter(move |item| item.ident.name == name) .map(move |item| item.res.def_id()) } diff --git a/src/tools/miri/test-cargo-miri/no-std-smoke/src/main.rs b/src/tools/miri/test-cargo-miri/no-std-smoke/src/main.rs index d9f1b27bf553e..aa3faf83f920a 100644 --- a/src/tools/miri/test-cargo-miri/no-std-smoke/src/main.rs +++ b/src/tools/miri/test-cargo-miri/no-std-smoke/src/main.rs @@ -1,6 +1,7 @@ // Copied from tests/pass/no-std.rs #![no_std] +#![no_main] // Plumbing to let us use `writeln!` to host stdout: diff --git a/src/tools/miri/tests/fail/intrinsics/cttz_nonzero.rs b/src/tools/miri/tests/fail/intrinsics/cttz_nonzero.rs index a1f7a5881d40f..e046a30e73424 100644 --- a/src/tools/miri/tests/fail/intrinsics/cttz_nonzero.rs +++ b/src/tools/miri/tests/fail/intrinsics/cttz_nonzero.rs @@ -1,4 +1,3 @@ - #![feature(intrinsics)] mod rusti { diff --git a/src/tools/miri/tests/fail/intrinsics/float_to_int_32_nanneg.rs b/src/tools/miri/tests/fail/intrinsics/float_to_int_32_nanneg.rs index a1c307efc9390..ee8b45c0aaee0 100644 --- a/src/tools/miri/tests/fail/intrinsics/float_to_int_32_nanneg.rs +++ b/src/tools/miri/tests/fail/intrinsics/float_to_int_32_nanneg.rs @@ -4,7 +4,6 @@ #[rustc_intrinsic] unsafe fn float_to_int_unchecked(_value: Float) -> Int; - fn main() { unsafe { float_to_int_unchecked::(-f32::NAN); //~ ERROR: cannot be represented in target type `u32` diff --git a/src/tools/miri/tests/fail/intrinsics/float_to_int_64_infneg1.rs b/src/tools/miri/tests/fail/intrinsics/float_to_int_64_infneg1.rs index 284b429230d09..93236bf4874b6 100644 --- a/src/tools/miri/tests/fail/intrinsics/float_to_int_64_infneg1.rs +++ b/src/tools/miri/tests/fail/intrinsics/float_to_int_64_infneg1.rs @@ -4,7 +4,6 @@ #[rustc_intrinsic] unsafe fn float_to_int_unchecked(_value: Float) -> Int; - fn main() { unsafe { float_to_int_unchecked::(f64::NEG_INFINITY); //~ ERROR: cannot be represented in target type `u128` diff --git a/src/tools/miri/tests/pass-dep/concurrency/linux-futex.rs b/src/tools/miri/tests/pass-dep/concurrency/linux-futex.rs index 3adeb89ecec72..0ca13b5039dde 100644 --- a/src/tools/miri/tests/pass-dep/concurrency/linux-futex.rs +++ b/src/tools/miri/tests/pass-dep/concurrency/linux-futex.rs @@ -235,7 +235,7 @@ fn concurrent_wait_wake() { static mut DATA: i32 = 0; static WOKEN: AtomicI32 = AtomicI32::new(0); - let rounds = 50; + let rounds = 64; for _ in 0..rounds { unsafe { DATA = 0 }; // Reset // Suppose the main thread is holding a lock implemented using futex... @@ -267,8 +267,7 @@ fn concurrent_wait_wake() { } }); // Increase the chance that the other thread actually goes to sleep. - // (5 yields in a loop seem to make that happen around 40% of the time.) - for _ in 0..5 { + for _ in 0..6 { thread::yield_now(); } diff --git a/src/tools/miri/tests/pass/disjoint-array-accesses.rs b/src/tools/miri/tests/pass/disjoint-array-accesses.rs new file mode 100644 index 0000000000000..50d0ea52ad4a1 --- /dev/null +++ b/src/tools/miri/tests/pass/disjoint-array-accesses.rs @@ -0,0 +1,35 @@ +// This is a regression test for issue #135671 where a MIR refactor about arrays and their lengths +// unexpectedly caused borrowck errors for disjoint borrows of array elements, for which we had no +// tests. This is a collection of a few code samples from that issue. + +//@revisions: stack tree +//@[tree]compile-flags: -Zmiri-tree-borrows + +struct Test { + a: i32, + b: i32, +} + +fn one() { + let inputs: &mut [_] = &mut [Test { a: 0, b: 0 }]; + let a = &mut inputs[0].a; + let b = &mut inputs[0].b; + + *a = 0; + *b = 1; +} + +fn two() { + let slice = &mut [(0, 0)][..]; + std::mem::swap(&mut slice[0].0, &mut slice[0].1); +} + +fn three(a: &mut [(i32, i32)], i: usize, j: usize) -> (&mut i32, &mut i32) { + (&mut a[i].0, &mut a[j].1) +} + +fn main() { + one(); + two(); + three(&mut [(1, 2), (3, 4)], 0, 1); +} diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pause-without-sse2.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pause-without-sse2.rs index 4d5ddd75f385a..6ca53c0eb6fca 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pause-without-sse2.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-pause-without-sse2.rs @@ -1,5 +1,5 @@ -// We're testing x86 target specific features -//@only-target: x86_64 i686 +// We're testing x86-32 target specific features. SSE always exists on x86-64. +//@only-target: i686 //@compile-flags: -C target-feature=-sse2 #[cfg(target_arch = "x86")] diff --git a/src/tools/opt-dist/src/main.rs b/src/tools/opt-dist/src/main.rs index aa05b5f0e76ca..04de3493ea29c 100644 --- a/src/tools/opt-dist/src/main.rs +++ b/src/tools/opt-dist/src/main.rs @@ -148,18 +148,15 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec)> let is_aarch64 = target_triple.starts_with("aarch64"); - let mut skip_tests = vec![ - // Fails because of linker errors, as of June 2023. - "tests/ui/process/nofile-limit.rs".to_string(), - ]; - - if is_aarch64 { - skip_tests.extend([ + let skip_tests = if is_aarch64 { + vec![ // Those tests fail only inside of Docker on aarch64, as of December 2024 "tests/ui/consts/promoted_running_out_of_memory_issue-130687.rs".to_string(), "tests/ui/consts/large_const_alloc.rs".to_string(), - ]); - } + ] + } else { + vec![] + }; let checkout_dir = Utf8PathBuf::from("/checkout"); let env = EnvironmentBuilder::default() @@ -191,10 +188,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec)> .build_dir(checkout_dir) .shared_llvm(false) .use_bolt(false) - .skipped_tests(vec![ - // Fails as of June 2023. - "tests\\codegen\\vec-shrink-panik.rs".to_string(), - ]) + .skipped_tests(vec![]) .build()?; (env, shared.build_args) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index f92668a6a9786..2dfca7c4803e2 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -846,7 +846,6 @@ dependencies = [ "dashmap", "hashbrown", "rustc-hash 2.0.0", - "sptr", "triomphe", ] @@ -1927,12 +1926,6 @@ dependencies = [ "vfs", ] -[[package]] -name = "sptr" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" - [[package]] name = "stdx" version = "0.0.0" diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 1029844cd3ab4..c42ae171d8668 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -4,7 +4,7 @@ exclude = ["crates/proc-macro-srv/proc-macro-test/imp"] resolver = "2" [workspace.package] -rust-version = "1.83" +rust-version = "1.84" edition = "2021" license = "MIT OR Apache-2.0" authors = ["rust-analyzer team"] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index 1327bb3ab59c0..16c7b5ca00a07 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -1381,6 +1381,9 @@ impl ExprCollector<'_> { } } ast::Stmt::Item(ast::Item::MacroDef(macro_)) => { + if self.check_cfg(¯o_).is_none() { + return; + } let Some(name) = macro_.name() else { statements.push(Statement::Item(Item::Other)); return; @@ -1390,6 +1393,9 @@ impl ExprCollector<'_> { self.collect_macro_def(statements, macro_id); } ast::Stmt::Item(ast::Item::MacroRules(macro_)) => { + if self.check_cfg(¯o_).is_none() { + return; + } let Some(name) = macro_.name() else { statements.push(Statement::Item(Item::Other)); return; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs index 68c7173d1e409..994ba2aa069d3 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs @@ -229,7 +229,7 @@ impl ExprCollector<'_> { }; for piece in unverified_pieces { match piece { - rustc_parse_format::Piece::String(_) => {} + rustc_parse_format::Piece::Lit(_) => {} rustc_parse_format::Piece::NextArgument(arg) => { // let span = arg_spans.next(); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs index f483efa85179b..e136dd18a55e5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs @@ -475,7 +475,7 @@ fn outer() { block scope::tests name: _ - outer: v + outer: vg crate outer: v diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index 5d67902c8ac12..c30ad0163b9db 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs @@ -445,6 +445,10 @@ fn find_in_dep( }; cov_mark::hit!(partially_imported); if info.is_unstable { + if !ctx.cfg.allow_unstable { + // the item is unstable and we are not allowed to use unstable items + continue; + } choice.stability = Unstable; } @@ -670,6 +674,7 @@ mod tests { prefer_prelude: bool, prefer_absolute: bool, prefer_no_std: bool, + allow_unstable: bool, expect: Expect, ) { let (db, pos) = TestDB::with_position(ra_fixture); @@ -711,7 +716,7 @@ mod tests { module, prefix, ignore_local_imports, - ImportPathConfig { prefer_no_std, prefer_prelude, prefer_absolute }, + ImportPathConfig { prefer_no_std, prefer_prelude, prefer_absolute, allow_unstable }, ); format_to!( res, @@ -732,7 +737,7 @@ mod tests { path: &str, expect: Expect, ) { - check_found_path_(ra_fixture, path, false, false, false, expect); + check_found_path_(ra_fixture, path, false, false, false, false, expect); } fn check_found_path_prelude( @@ -740,7 +745,7 @@ mod tests { path: &str, expect: Expect, ) { - check_found_path_(ra_fixture, path, true, false, false, expect); + check_found_path_(ra_fixture, path, true, false, false, false, expect); } fn check_found_path_absolute( @@ -748,7 +753,7 @@ mod tests { path: &str, expect: Expect, ) { - check_found_path_(ra_fixture, path, false, true, false, expect); + check_found_path_(ra_fixture, path, false, true, false, false, expect); } fn check_found_path_prefer_no_std( @@ -756,7 +761,15 @@ mod tests { path: &str, expect: Expect, ) { - check_found_path_(ra_fixture, path, false, false, true, expect); + check_found_path_(ra_fixture, path, false, false, true, false, expect); + } + + fn check_found_path_prefer_no_std_allow_unstable( + #[rust_analyzer::rust_fixture] ra_fixture: &str, + path: &str, + expect: Expect, + ) { + check_found_path_(ra_fixture, path, false, false, true, true, expect); } #[test] @@ -1951,7 +1964,7 @@ pub mod ops { #[test] fn respect_unstable_modules() { - check_found_path_prefer_no_std( + check_found_path_prefer_no_std_allow_unstable( r#" //- /main.rs crate:main deps:std,core extern crate std; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs index e64e498c17074..28c824fd31d73 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/format_args.rs @@ -287,7 +287,7 @@ pub(crate) fn parse( for piece in pieces { match piece { - parse::Piece::String(s) => { + parse::Piece::Lit(s) => { unfinished_literal.push_str(s); } parse::Piece::NextArgument(arg) => { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs index ac262950f13c6..34635997bdff3 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs @@ -10,7 +10,6 @@ use rustc_hash::FxHashSet; use smallvec::SmallVec; use span::Edition; use stdx::{format_to, TupleExt}; -use syntax::ToSmolStr; use triomphe::Arc; use crate::{ @@ -88,9 +87,9 @@ impl ImportMap { .iter() // We've only collected items, whose name cannot be tuple field so unwrapping is fine. .flat_map(|(&item, (info, _))| { - info.iter().enumerate().map(move |(idx, info)| { - (item, info.name.unescaped().display(db.upcast()).to_smolstr(), idx as u32) - }) + info.iter() + .enumerate() + .map(move |(idx, info)| (item, info.name.as_str(), idx as u32)) }) .collect(); importables.sort_by(|(_, l_info, _), (_, r_info, _)| { @@ -168,7 +167,8 @@ impl ImportMap { let attr_id = if let Some(import) = import { match import { ImportOrExternCrate::ExternCrate(id) => Some(id.into()), - ImportOrExternCrate::Import(id) => Some(id.import.into()), + ImportOrExternCrate::Import(id) => Some(id.use_.into()), + ImportOrExternCrate::Glob(id) => Some(id.use_.into()), } } else { match item { @@ -441,7 +441,7 @@ pub fn search_dependencies( } fn search_maps( - db: &dyn DefDatabase, + _db: &dyn DefDatabase, import_maps: &[Arc], mut stream: fst::map::Union<'_>, query: &Query, @@ -464,11 +464,7 @@ fn search_maps( .then(|| (item, &import_infos[info_idx as usize])) }) .filter(|&(_, info)| { - query.search_mode.check( - &query.query, - query.case_sensitive, - &info.name.unescaped().display(db.upcast()).to_smolstr(), - ) + query.search_mode.check(&query.query, query.case_sensitive, info.name.as_str()) }); res.extend(iter.map(TupleExt::head)); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index 0fec7674109bc..65a39c565611b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -31,21 +31,62 @@ pub struct PerNsGlobImports { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ImportOrExternCrate { + Glob(GlobId), Import(ImportId), ExternCrate(ExternCrateId), } +impl From for ImportOrExternCrate { + fn from(value: ImportOrGlob) -> Self { + match value { + ImportOrGlob::Glob(it) => ImportOrExternCrate::Glob(it), + ImportOrGlob::Import(it) => ImportOrExternCrate::Import(it), + } + } +} + +impl ImportOrExternCrate { + pub fn import_or_glob(self) -> Option { + match self { + ImportOrExternCrate::Import(it) => Some(ImportOrGlob::Import(it)), + ImportOrExternCrate::Glob(it) => Some(ImportOrGlob::Glob(it)), + _ => None, + } + } + + pub fn import(self) -> Option { + match self { + ImportOrExternCrate::Import(it) => Some(it), + _ => None, + } + } + + pub fn glob(self) -> Option { + match self { + ImportOrExternCrate::Glob(id) => Some(id), + _ => None, + } + } + + pub fn use_(self) -> Option { + match self { + ImportOrExternCrate::Glob(id) => Some(id.use_), + ImportOrExternCrate::Import(id) => Some(id.use_), + _ => None, + } + } +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub(crate) enum ImportType { +pub enum ImportOrGlob { + Glob(GlobId), Import(ImportId), - Glob(UseId), - ExternCrate(ExternCrateId), } -impl ImportOrExternCrate { +impl ImportOrGlob { pub fn into_import(self) -> Option { match self { - ImportOrExternCrate::Import(it) => Some(it), + ImportOrGlob::Import(it) => Some(it), _ => None, } } @@ -54,12 +95,39 @@ impl ImportOrExternCrate { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ImportOrDef { Import(ImportId), + Glob(GlobId), ExternCrate(ExternCrateId), Def(ModuleDefId), } + +impl From for ImportOrDef { + fn from(value: ImportOrExternCrate) -> Self { + match value { + ImportOrExternCrate::Import(it) => ImportOrDef::Import(it), + ImportOrExternCrate::Glob(it) => ImportOrDef::Glob(it), + ImportOrExternCrate::ExternCrate(it) => ImportOrDef::ExternCrate(it), + } + } +} + +impl From for ImportOrDef { + fn from(value: ImportOrGlob) -> Self { + match value { + ImportOrGlob::Import(it) => ImportOrDef::Import(it), + ImportOrGlob::Glob(it) => ImportOrDef::Glob(it), + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] pub struct ImportId { - pub import: UseId, + pub use_: UseId, + pub idx: Idx, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] +pub struct GlobId { + pub use_: UseId, pub idx: Idx, } @@ -96,8 +164,8 @@ pub struct ItemScope { // the resolutions of the imports of this scope use_imports_types: FxHashMap, - use_imports_values: FxHashMap, - use_imports_macros: FxHashMap, + use_imports_values: FxHashMap, + use_imports_macros: FxHashMap, use_decls: Vec, extern_crate_decls: Vec, @@ -162,7 +230,7 @@ impl ItemScope { .map(move |name| (name, self.get(name))) } - pub fn values(&self) -> impl Iterator)> + '_ { + pub fn values(&self) -> impl Iterator)> + '_ { self.values.iter().map(|(n, &i)| (n, i)) } @@ -172,7 +240,7 @@ impl ItemScope { self.types.iter().map(|(n, &i)| (n, i)) } - pub fn macros(&self) -> impl Iterator)> + '_ { + pub fn macros(&self) -> impl Iterator)> + '_ { self.macros.iter().map(|(n, &i)| (n, i)) } @@ -180,9 +248,10 @@ impl ItemScope { self.use_imports_types .keys() .copied() - .filter_map(ImportOrExternCrate::into_import) + .filter_map(ImportOrExternCrate::import_or_glob) .chain(self.use_imports_values.keys().copied()) .chain(self.use_imports_macros.keys().copied()) + .filter_map(ImportOrGlob::into_import) .sorted() .dedup() } @@ -192,10 +261,10 @@ impl ItemScope { let mut def_map; let mut scope = self; - while let Some(&m) = scope.use_imports_macros.get(&import) { + while let Some(&m) = scope.use_imports_macros.get(&ImportOrGlob::Import(import)) { match m { ImportOrDef::Import(i) => { - let module_id = i.import.lookup(db).container; + let module_id = i.use_.lookup(db).container; def_map = module_id.def_map(db); scope = &def_map[module_id.local_id].scope; import = i; @@ -211,7 +280,7 @@ impl ItemScope { while let Some(&m) = scope.use_imports_types.get(&ImportOrExternCrate::Import(import)) { match m { ImportOrDef::Import(i) => { - let module_id = i.import.lookup(db).container; + let module_id = i.use_.lookup(db).container; def_map = module_id.def_map(db); scope = &def_map[module_id.local_id].scope; import = i; @@ -224,10 +293,10 @@ impl ItemScope { } } let mut scope = self; - while let Some(&m) = scope.use_imports_values.get(&import) { + while let Some(&m) = scope.use_imports_values.get(&ImportOrGlob::Import(import)) { match m { ImportOrDef::Import(i) => { - let module_id = i.import.lookup(db).container; + let module_id = i.use_.lookup(db).container; def_map = module_id.def_map(db); scope = &def_map[module_id.local_id].scope; import = i; @@ -488,9 +557,13 @@ impl ItemScope { self.unnamed_trait_imports.get(&tr).map(|trait_| trait_.vis) } - pub(crate) fn push_unnamed_trait(&mut self, tr: TraitId, vis: Visibility) { - // FIXME: import - self.unnamed_trait_imports.insert(tr, Item { def: (), vis, import: None }); + pub(crate) fn push_unnamed_trait( + &mut self, + tr: TraitId, + vis: Visibility, + import: Option, + ) { + self.unnamed_trait_imports.insert(tr, Item { def: (), vis, import }); } pub(crate) fn push_res_with_import( @@ -498,7 +571,7 @@ impl ItemScope { glob_imports: &mut PerNsGlobImports, lookup: (LocalModuleId, Name), def: PerNs, - import: Option, + import: Option, ) -> bool { let mut changed = false; @@ -509,41 +582,22 @@ impl ItemScope { match existing { Entry::Vacant(entry) => { match import { - Some(ImportType::Glob(_)) => { + Some(ImportOrExternCrate::Glob(_)) => { glob_imports.types.insert(lookup.clone()); } _ => _ = glob_imports.types.remove(&lookup), } - let import = match import { - Some(ImportType::ExternCrate(extern_crate)) => { - Some(ImportOrExternCrate::ExternCrate(extern_crate)) - } - Some(ImportType::Import(import)) => { - Some(ImportOrExternCrate::Import(import)) - } - None | Some(ImportType::Glob(_)) => None, - }; let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { - self.use_imports_types.insert( - import, - match prev { - Some(ImportOrExternCrate::Import(import)) => { - ImportOrDef::Import(import) - } - Some(ImportOrExternCrate::ExternCrate(import)) => { - ImportOrDef::ExternCrate(import) - } - None => ImportOrDef::Def(fld.def), - }, - ); + self.use_imports_types + .insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into)); } entry.insert(fld); changed = true; } Entry::Occupied(mut entry) => { match import { - Some(ImportType::Glob(..)) => { + Some(ImportOrExternCrate::Glob(..)) => { // Multiple globs may import the same item and they may // override visibility from previously resolved globs. This is // currently handled by `DefCollector`, because we need to @@ -552,28 +606,11 @@ impl ItemScope { } _ => { if glob_imports.types.remove(&lookup) { - let import = match import { - Some(ImportType::ExternCrate(extern_crate)) => { - Some(ImportOrExternCrate::ExternCrate(extern_crate)) - } - Some(ImportType::Import(import)) => { - Some(ImportOrExternCrate::Import(import)) - } - None | Some(ImportType::Glob(_)) => None, - }; let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_types.insert( import, - match prev { - Some(ImportOrExternCrate::Import(import)) => { - ImportOrDef::Import(import) - } - Some(ImportOrExternCrate::ExternCrate(import)) => { - ImportOrDef::ExternCrate(import) - } - None => ImportOrDef::Def(fld.def), - }, + prev.map_or(ImportOrDef::Def(fld.def), Into::into), ); } cov_mark::hit!(import_shadowed); @@ -591,44 +628,31 @@ impl ItemScope { match existing { Entry::Vacant(entry) => { match import { - Some(ImportType::Glob(_)) => { + Some(ImportOrExternCrate::Glob(_)) => { glob_imports.values.insert(lookup.clone()); } _ => _ = glob_imports.values.remove(&lookup), } - let import = match import { - Some(ImportType::Import(import)) => Some(import), - _ => None, - }; + let import = import.and_then(ImportOrExternCrate::import_or_glob); let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { - self.use_imports_values.insert( - import, - match prev { - Some(import) => ImportOrDef::Import(import), - None => ImportOrDef::Def(fld.def), - }, - ); + self.use_imports_values + .insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into)); } entry.insert(fld); changed = true; } - Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => { + Entry::Occupied(mut entry) + if !matches!(import, Some(ImportOrExternCrate::Glob(..))) => + { if glob_imports.values.remove(&lookup) { cov_mark::hit!(import_shadowed); - let import = match import { - Some(ImportType::Import(import)) => Some(import), - _ => None, - }; + + let import = import.and_then(ImportOrExternCrate::import_or_glob); let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { - self.use_imports_values.insert( - import, - match prev { - Some(import) => ImportOrDef::Import(import), - None => ImportOrDef::Def(fld.def), - }, - ); + self.use_imports_values + .insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into)); } entry.insert(fld); changed = true; @@ -643,43 +667,33 @@ impl ItemScope { match existing { Entry::Vacant(entry) => { match import { - Some(ImportType::Glob(_)) => { + Some(ImportOrExternCrate::Glob(_)) => { glob_imports.macros.insert(lookup.clone()); } _ => _ = glob_imports.macros.remove(&lookup), } - let import = match import { - Some(ImportType::Import(import)) => Some(import), - _ => None, - }; + let import = import.and_then(ImportOrExternCrate::import_or_glob); let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_macros.insert( import, - match prev { - Some(import) => ImportOrDef::Import(import), - None => ImportOrDef::Def(fld.def.into()), - }, + prev.map_or_else(|| ImportOrDef::Def(fld.def.into()), Into::into), ); } entry.insert(fld); changed = true; } - Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => { + Entry::Occupied(mut entry) + if !matches!(import, Some(ImportOrExternCrate::Glob(..))) => + { if glob_imports.macros.remove(&lookup) { cov_mark::hit!(import_shadowed); - let import = match import { - Some(ImportType::Import(import)) => Some(import), - _ => None, - }; + let import = import.and_then(ImportOrExternCrate::import_or_glob); let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_macros.insert( import, - match prev { - Some(import) => ImportOrDef::Import(import), - None => ImportOrDef::Def(fld.def.into()), - }, + prev.map_or_else(|| ImportOrDef::Def(fld.def.into()), Into::into), ); } entry.insert(fld); @@ -704,16 +718,27 @@ impl ItemScope { .map(|def| &mut def.vis) .chain(self.values.values_mut().map(|def| &mut def.vis)) .chain(self.unnamed_trait_imports.values_mut().map(|def| &mut def.vis)) - .for_each(|vis| { - *vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit) + .for_each(|vis| match vis { + &mut Visibility::Module(_, visibility_explicitness) => { + *vis = Visibility::Module(this_module, visibility_explicitness) + } + Visibility::Public => { + *vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit) + } }); for mac in self.macros.values_mut() { if matches!(mac.def, MacroId::ProcMacroId(_) if mac.import.is_none()) { continue; } - - mac.vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit); + match mac.vis { + Visibility::Module(_, visibility_explicitness) => { + mac.vis = Visibility::Module(this_module, visibility_explicitness) + } + Visibility::Public => { + mac.vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit) + } + } } } @@ -732,20 +757,25 @@ impl ItemScope { buf.push_str(" t"); match import { Some(ImportOrExternCrate::Import(_)) => buf.push('i'), + Some(ImportOrExternCrate::Glob(_)) => buf.push('g'), Some(ImportOrExternCrate::ExternCrate(_)) => buf.push('e'), None => (), } } if let Some(Item { import, .. }) = def.values { buf.push_str(" v"); - if import.is_some() { - buf.push('i'); + match import { + Some(ImportOrGlob::Import(_)) => buf.push('i'), + Some(ImportOrGlob::Glob(_)) => buf.push('g'), + None => (), } } if let Some(Item { import, .. }) = def.macros { buf.push_str(" m"); - if import.is_some() { - buf.push('i'); + match import { + Some(ImportOrGlob::Import(_)) => buf.push('i'), + Some(ImportOrGlob::Glob(_)) => buf.push('g'), + None => (), } } if def.is_none() { @@ -828,7 +858,7 @@ impl PerNs { match def { ModuleDefId::ModuleId(_) => PerNs::types(def, v, import), ModuleDefId::FunctionId(_) => { - PerNs::values(def, v, import.and_then(ImportOrExternCrate::into_import)) + PerNs::values(def, v, import.and_then(ImportOrExternCrate::import_or_glob)) } ModuleDefId::AdtId(adt) => match adt { AdtId::UnionId(_) => PerNs::types(def, v, import), @@ -843,14 +873,14 @@ impl PerNs { }, ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v, import), ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => { - PerNs::values(def, v, import.and_then(ImportOrExternCrate::into_import)) + PerNs::values(def, v, import.and_then(ImportOrExternCrate::import_or_glob)) } ModuleDefId::TraitId(_) => PerNs::types(def, v, import), ModuleDefId::TraitAliasId(_) => PerNs::types(def, v, import), ModuleDefId::TypeAliasId(_) => PerNs::types(def, v, import), ModuleDefId::BuiltinType(_) => PerNs::types(def, v, import), ModuleDefId::MacroId(mac) => { - PerNs::macros(mac, v, import.and_then(ImportOrExternCrate::into_import)) + PerNs::macros(mac, v, import.and_then(ImportOrExternCrate::import_or_glob)) } } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index afdc49a2dc59a..e83ce6dc42cee 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -372,6 +372,7 @@ language_item_table! { DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0); DerefTarget, sym::deref_target, deref_target, Target::AssocTy, GenericRequirement::None; Receiver, sym::receiver, receiver_trait, Target::Trait, GenericRequirement::None; + ReceiverTarget, sym::receiver_target, receiver_target, Target::AssocTy, GenericRequirement::None; Fn, sym::fn_, fn_trait, Target::Trait, GenericRequirement::Exact(1); FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 84c105a0a3467..c78818c642ceb 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -114,6 +114,9 @@ pub struct ImportPathConfig { pub prefer_prelude: bool, /// If true, prefer abs path (starting with `::`) where it is available. pub prefer_absolute: bool, + /// If true, paths containing `#[unstable]` segments may be returned, but only if if there is no + /// stable path. This does not check, whether the item itself that is being imported is `#[unstable]`. + pub allow_unstable: bool, } #[derive(Debug)] @@ -910,6 +913,7 @@ pub enum AssocItemId { ConstId(ConstId), TypeAliasId(TypeAliasId), } + // FIXME: not every function, ... is actually an assoc item. maybe we should make // sure that you can only turn actual assoc items into AssocItemIds. This would // require not implementing From, and instead having some checked way of diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 1e4b42dff5fb7..06276335b7188 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -28,7 +28,7 @@ use triomphe::Arc; use crate::{ attr::Attrs, db::DefDatabase, - item_scope::{ImportId, ImportOrExternCrate, ImportType, PerNsGlobImports}, + item_scope::{GlobId, ImportId, ImportOrExternCrate, PerNsGlobImports}, item_tree::{ self, AttrOwner, FieldsShape, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId, UseTreeKind, @@ -208,7 +208,7 @@ struct DefCollector<'a> { def_map: DefMap, // The dependencies of the current crate, including optional deps like `test`. deps: FxHashMap, - glob_imports: FxHashMap>, + glob_imports: FxHashMap>, unresolved_imports: Vec, indeterminate_imports: Vec<(ImportDirective, PerNs)>, unresolved_macros: Vec, @@ -524,11 +524,7 @@ impl DefCollector<'_> { match per_ns.types { Some(Item { def: ModuleDefId::ModuleId(m), import, .. }) => { - // FIXME: This should specifically look for a glob import somehow and record that here - self.def_map.prelude = Some(( - m, - import.and_then(ImportOrExternCrate::into_import).map(|it| it.import), - )); + self.def_map.prelude = Some((m, import.and_then(ImportOrExternCrate::use_))); } types => { tracing::debug!( @@ -845,13 +841,14 @@ impl DefCollector<'_> { def.values = None; def.macros = None; } - let imp = ImportType::Import(ImportId { import: id, idx: use_tree }); + let imp = ImportOrExternCrate::Import(ImportId { use_: id, idx: use_tree }); tracing::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); self.update(module_id, &[(name.cloned(), def)], vis, Some(imp)); } - ImportSource { kind: ImportKind::Glob, id, is_prelude, .. } => { + ImportSource { kind: ImportKind::Glob, id, is_prelude, use_tree } => { tracing::debug!("glob import: {:?}", import); + let glob = GlobId { use_: id, idx: use_tree }; match def.take_types() { Some(ModuleDefId::ModuleId(m)) => { if is_prelude { @@ -875,7 +872,12 @@ impl DefCollector<'_> { .filter(|(_, res)| !res.is_none()) .collect::>(); - self.update(module_id, &items, vis, Some(ImportType::Glob(id))); + self.update( + module_id, + &items, + vis, + Some(ImportOrExternCrate::Glob(glob)), + ); } else { // glob import from same crate => we do an initial // import, and then need to propagate any further @@ -907,11 +909,16 @@ impl DefCollector<'_> { .filter(|(_, res)| !res.is_none()) .collect::>(); - self.update(module_id, &items, vis, Some(ImportType::Glob(id))); + self.update( + module_id, + &items, + vis, + Some(ImportOrExternCrate::Glob(glob)), + ); // record the glob import in case we add further items - let glob = self.glob_imports.entry(m.local_id).or_default(); - match glob.iter_mut().find(|(mid, _, _)| *mid == module_id) { - None => glob.push((module_id, vis, id)), + let glob_imports = self.glob_imports.entry(m.local_id).or_default(); + match glob_imports.iter_mut().find(|(mid, _, _)| *mid == module_id) { + None => glob_imports.push((module_id, vis, glob)), Some((_, old_vis, _)) => { if let Some(new_vis) = old_vis.max(vis, &self.def_map) { *old_vis = new_vis; @@ -944,7 +951,12 @@ impl DefCollector<'_> { (Some(name), res) }) .collect::>(); - self.update(module_id, &resolutions, vis, Some(ImportType::Glob(id))); + self.update( + module_id, + &resolutions, + vis, + Some(ImportOrExternCrate::Glob(glob)), + ); } Some(d) => { tracing::debug!("glob import {:?} from non-module/enum {:?}", import, d); @@ -964,7 +976,7 @@ impl DefCollector<'_> { resolutions: &[(Option, PerNs)], // Visibility this import will have vis: Visibility, - import: Option, + import: Option, ) { self.db.unwind_if_cancelled(); self.update_recursive(module_id, resolutions, vis, import, 0) @@ -978,7 +990,7 @@ impl DefCollector<'_> { // All resolutions are imported with this visibility; the visibilities in // the `PerNs` values are ignored and overwritten vis: Visibility, - import: Option, + import: Option, depth: usize, ) { if GLOB_RECURSION_LIMIT.check(depth).is_err() { @@ -994,8 +1006,10 @@ impl DefCollector<'_> { self.push_res_and_update_glob_vis(module_id, name, *res, vis, import); } None => { - let tr = match res.take_types() { - Some(ModuleDefId::TraitId(tr)) => tr, + let (tr, import) = match res.take_types_full() { + Some(Item { def: ModuleDefId::TraitId(tr), vis: _, import }) => { + (tr, import) + } Some(other) => { tracing::debug!("non-trait `_` import of {:?}", other); continue; @@ -1021,7 +1035,11 @@ impl DefCollector<'_> { if should_update { changed = true; - self.def_map.modules[module_id].scope.push_unnamed_trait(tr, vis); + self.def_map.modules[module_id].scope.push_unnamed_trait( + tr, + vis, + import.and_then(ImportOrExternCrate::import), + ); } } } @@ -1043,13 +1061,13 @@ impl DefCollector<'_> { .cloned() .collect::>(); - for (glob_importing_module, glob_import_vis, use_) in glob_imports { + for (glob_importing_module, glob_import_vis, glob) in glob_imports { let vis = glob_import_vis.min(vis, &self.def_map).unwrap_or(glob_import_vis); self.update_recursive( glob_importing_module, resolutions, vis, - Some(ImportType::Glob(use_)), + Some(ImportOrExternCrate::Glob(glob)), depth + 1, ); } @@ -1061,7 +1079,7 @@ impl DefCollector<'_> { name: &Name, mut defs: PerNs, vis: Visibility, - def_import_type: Option, + def_import_type: Option, ) -> bool { // `extern crate crate_name` things can be re-exported as `pub use crate_name`. // But they cannot be re-exported as `pub use self::crate_name`, `pub use crate::crate_name` @@ -1074,10 +1092,10 @@ impl DefCollector<'_> { let Some(ImportOrExternCrate::ExternCrate(_)) = def.import else { return false; }; - let Some(ImportType::Import(id)) = def_import_type else { + let Some(ImportOrExternCrate::Import(id)) = def_import_type else { return false; }; - let use_id = id.import.lookup(self.db).id; + let use_id = id.use_.lookup(self.db).id; let item_tree = use_id.item_tree(self.db); let use_kind = item_tree[use_id.value].use_tree.kind(); let UseTreeKind::Single { path, .. } = use_kind else { @@ -1100,7 +1118,7 @@ impl DefCollector<'_> { let mut changed = false; - if let Some(ImportType::Glob(_)) = def_import_type { + if let Some(ImportOrExternCrate::Glob(_)) = def_import_type { let prev_defs = self.def_map[module_id].scope.get(name); // Multiple globs may import the same item and they may override visibility from @@ -1727,7 +1745,7 @@ impl ModCollector<'_, '_> { ), )], vis, - Some(ImportType::ExternCrate(id)), + Some(ImportOrExternCrate::ExternCrate(id)), ); } else { if let Some(name) = name { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs index ab4ffbb2c1e4a..d7e4ca41cd5d5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs @@ -4,7 +4,6 @@ use base_db::AnchoredPath; use hir_expand::{name::Name, HirFileIdExt}; use limit::Limit; use span::EditionedFileId; -use syntax::ToSmolStr as _; use crate::{db::DefDatabase, HirFileId}; @@ -35,7 +34,7 @@ impl ModDir { let path = match attr_path { None => { let mut path = self.dir_path.clone(); - path.push(&name.unescaped().display_no_db().to_smolstr()); + path.push(name.as_str()); path } Some(attr_path) => { @@ -66,7 +65,7 @@ impl ModDir { name: &Name, attr_path: Option<&str>, ) -> Result<(EditionedFileId, bool, ModDir), Box<[String]>> { - let name = name.unescaped(); + let name = name.as_str(); let mut candidate_files = ArrayVec::<_, 2>::new(); match attr_path { @@ -74,16 +73,8 @@ impl ModDir { candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner)) } None => { - candidate_files.push(format!( - "{}{}.rs", - self.dir_path.0, - name.display(db.upcast()) - )); - candidate_files.push(format!( - "{}{}/mod.rs", - self.dir_path.0, - name.display(db.upcast()) - )); + candidate_files.push(format!("{}{}.rs", self.dir_path.0, name)); + candidate_files.push(format!("{}{}/mod.rs", self.dir_path.0, name)); } }; @@ -97,7 +88,7 @@ impl ModDir { let dir_path = if root_dir_owner { DirPath::empty() } else { - DirPath::new(format!("{}/", name.display(db.upcast()))) + DirPath::new(format!("{}/", name)) }; if let Some(mod_dir) = self.child(dir_path, !root_dir_owner) { return Ok(( diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs index 318aee04f7b7f..73fc6787bfe81 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs @@ -103,8 +103,8 @@ mod a { c: t crate::a::b::c - A: v - b: t + A: vg + b: tg "#]], ); } @@ -256,8 +256,8 @@ pub enum Foo { Bar, Baz } "#, expect![[r#" crate - Bar: t v - Baz: t v + Bar: tg vg + Baz: tg vg "#]], ); } @@ -421,10 +421,10 @@ pub struct NotExported; "#, expect![[r#" crate - Exported: t v - PublicItem: t v - allowed_reexport: t - exported: t + Exported: tg vg + PublicItem: tg vg + allowed_reexport: tg + exported: tg not_allowed_reexport1: _ not_allowed_reexport2: _ "#]], @@ -692,7 +692,7 @@ mod b { b: t crate::a - T: t v + T: t vg crate::b T: v diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs index 8963a5767942e..ddb9d4a134d33 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs @@ -18,9 +18,9 @@ pub struct Baz; "#, expect![[r#" crate - Baz: t v - Foo: t v - bar: t + Baz: tg vg + Foo: tg vg + bar: tg foo: t crate::foo @@ -53,20 +53,20 @@ pub use super::*; "#, expect![[r#" crate - Baz: t v - Foo: t v - bar: t + Baz: tg vg + Foo: tg vg + bar: tg foo: t crate::foo - Baz: t v + Baz: tg vg Foo: t v bar: t crate::foo::bar Baz: t v - Foo: t v - bar: t + Foo: tg vg + bar: tg "#]], ); } @@ -91,20 +91,20 @@ pub use super::*; ", expect![[r#" crate - Baz: t v - bar: t + Baz: tg vg + bar: tg foo: t crate::foo - Baz: t v + Baz: tg vg PrivateStructFoo: t v bar: t crate::foo::bar Baz: t v PrivateStructBar: t v - PrivateStructFoo: t v - bar: t + PrivateStructFoo: tg vg + bar: tg "#]], ); } @@ -130,9 +130,9 @@ pub(crate) struct PubCrateStruct; ", expect![[r#" crate - Foo: t - PubCrateStruct: t v - bar: t + Foo: tg + PubCrateStruct: tg vg + bar: tg foo: t crate::foo @@ -160,7 +160,7 @@ pub struct Baz; "#, expect![[r#" crate - Baz: t v + Baz: tg vg "#]], ); } @@ -178,7 +178,7 @@ struct Foo; "#, expect![[r#" crate - Baz: t v + Baz: tg vg "#]], ); } @@ -193,8 +193,8 @@ use self::Foo::*; "#, expect![[r#" crate - Bar: t v - Baz: t v + Bar: tg vg + Baz: tg vg Foo: t "#]], ); @@ -210,8 +210,8 @@ use self::Foo::{*}; "#, expect![[r#" crate - Bar: t v - Baz: t v + Bar: tg vg + Baz: tg vg Foo: t "#]], ); @@ -359,7 +359,7 @@ use event::Event; event: t crate::event - Event: t v + Event: t vg serenity: t crate::event::serenity @@ -388,10 +388,10 @@ use reexport::*; "#, expect![[r#" crate - Trait: t + Trait: tg defs: t - function: v - makro: m + function: vg + makro: mg reexport: t crate::defs @@ -400,10 +400,10 @@ use reexport::*; makro: m crate::reexport - Trait: t - function: v + Trait: tg + function: vg inner: t - makro: m + makro: mg crate::reexport::inner Trait: ti @@ -442,12 +442,12 @@ mod glob_target { ShouldBePrivate: t v crate::outer - ShouldBePrivate: t v + ShouldBePrivate: tg vg inner_superglob: t crate::outer::inner_superglob - ShouldBePrivate: t v - inner_superglob: t + ShouldBePrivate: tg vg + inner_superglob: tg "#]], ); } @@ -473,20 +473,20 @@ use reexport_2::*; "#, expect![[r#" crate - Placeholder: t v + Placeholder: tg vg libs: t - reexport_1: t + reexport_1: tg reexport_2: t crate::libs Placeholder: t v crate::reexport_2 - Placeholder: t v + Placeholder: tg vg reexport_1: t crate::reexport_2::reexport_1 - Placeholder: t v + Placeholder: tg vg "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs index a05c4dcf9bd70..610886d55f40f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs @@ -97,9 +97,9 @@ macro_rules! structs { bar: t crate::bar - Bar: t - Foo: t - bar: t + Bar: tg + Foo: tg + bar: tg "#]], ); } @@ -130,9 +130,9 @@ macro_rules! structs { bar: t crate::bar - Bar: t - Foo: t - bar: t + Bar: tg + Foo: tg + bar: tg "#]], ); } @@ -169,9 +169,9 @@ macro_rules! inner { bar: t crate::bar - Bar: t - Foo: t - bar: t + Bar: tg + Foo: tg + bar: tg "#]], ); } @@ -794,7 +794,7 @@ pub trait Clone {} "#, expect![[r#" crate - Clone: t m + Clone: tg mg "#]], ); } @@ -1075,9 +1075,9 @@ macro_rules! mbe { "#, expect![[r#" crate - DummyTrait: m - attribute_macro: m - function_like_macro: m + DummyTrait: mg + attribute_macro: mg + function_like_macro: mg "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs index 899dd4afffef6..c2d3f67f17e77 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs @@ -6,7 +6,7 @@ use bitflags::bitflags; use crate::{ - item_scope::{ImportId, ImportOrExternCrate, ItemInNs}, + item_scope::{ImportId, ImportOrExternCrate, ImportOrGlob, ItemInNs}, visibility::Visibility, MacroId, ModuleDefId, }; @@ -36,8 +36,8 @@ pub struct Item { } pub type TypesItem = Item; -pub type ValuesItem = Item; -pub type MacrosItem = Item; +pub type ValuesItem = Item; +pub type MacrosItem = Item; #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct PerNs { @@ -59,7 +59,7 @@ impl PerNs { PerNs { types: None, values: None, macros: None } } - pub fn values(def: ModuleDefId, vis: Visibility, import: Option) -> PerNs { + pub fn values(def: ModuleDefId, vis: Visibility, import: Option) -> PerNs { PerNs { types: None, values: Some(Item { def, vis, import }), macros: None } } @@ -78,13 +78,13 @@ impl PerNs { values: Some(Item { def: values, vis, - import: import.and_then(ImportOrExternCrate::into_import), + import: import.and_then(ImportOrExternCrate::import_or_glob), }), macros: None, } } - pub fn macros(def: MacroId, vis: Visibility, import: Option) -> PerNs { + pub fn macros(def: MacroId, vis: Visibility, import: Option) -> PerNs { PerNs { types: None, values: None, macros: Some(Item { def, vis, import }) } } @@ -108,7 +108,7 @@ impl PerNs { self.values.map(|it| it.def) } - pub fn take_values_import(self) -> Option<(ModuleDefId, Option)> { + pub fn take_values_import(self) -> Option<(ModuleDefId, Option)> { self.values.map(|it| (it.def, it.import)) } @@ -116,7 +116,7 @@ impl PerNs { self.macros.map(|it| it.def) } - pub fn take_macros_import(self) -> Option<(MacroId, Option)> { + pub fn take_macros_import(self) -> Option<(MacroId, Option)> { self.macros.map(|it| (it.def, it.import)) } @@ -159,14 +159,12 @@ impl PerNs { .map(|it| (ItemInNs::Types(it.def), it.import)) .into_iter() .chain( - self.values.map(|it| { - (ItemInNs::Values(it.def), it.import.map(ImportOrExternCrate::Import)) - }), + self.values + .map(|it| (ItemInNs::Values(it.def), it.import.map(ImportOrExternCrate::from))), ) .chain( - self.macros.map(|it| { - (ItemInNs::Macros(it.def), it.import.map(ImportOrExternCrate::Import)) - }), + self.macros + .map(|it| (ItemInNs::Macros(it.def), it.import.map(ImportOrExternCrate::from))), ) } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index 0b9b6da8d5133..8c556d8a8c3f4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -19,7 +19,7 @@ use crate::{ db::DefDatabase, generics::{GenericParams, TypeOrConstParamData}, hir::{BindingId, ExprId, LabelId}, - item_scope::{BuiltinShadowMode, ImportId, ImportOrExternCrate, BUILTIN_SCOPE}, + item_scope::{BuiltinShadowMode, ImportOrExternCrate, ImportOrGlob, BUILTIN_SCOPE}, lang_item::LangItemTarget, nameres::{DefMap, MacroSubNs, ResolvePathResultPrefixInfo}, path::{ModPath, Path, PathKind}, @@ -107,7 +107,7 @@ pub enum TypeNs { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ResolveValueResult { - ValueNs(ValueNs, Option), + ValueNs(ValueNs, Option), Partial(TypeNs, usize, Option), } @@ -485,7 +485,7 @@ impl Resolver { db: &dyn DefDatabase, path: &ModPath, expected_macro_kind: Option, - ) -> Option<(MacroId, Option)> { + ) -> Option<(MacroId, Option)> { let (item_map, module) = self.item_scope(); item_map .resolve_path(db, module, path, BuiltinShadowMode::Other, expected_macro_kind) @@ -1014,7 +1014,7 @@ impl ModuleItemMap { } } -fn to_value_ns(per_ns: PerNs) -> Option<(ValueNs, Option)> { +fn to_value_ns(per_ns: PerNs) -> Option<(ValueNs, Option)> { let (def, import) = per_ns.take_values_import()?; let res = match def { ModuleDefId::FunctionId(it) => ValueNs::FunctionId(it), diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs index 89eae862bd96c..f0cf7ebf479f8 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs @@ -23,15 +23,6 @@ pub struct ModPath { segments: SmallVec<[Name; 1]>, } -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct UnescapedModPath<'a>(&'a ModPath); - -impl<'a> UnescapedModPath<'a> { - pub fn display(&'a self, db: &'a dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a { - UnescapedDisplay { db, path: self } - } -} - #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum PathKind { Plain, @@ -135,9 +126,11 @@ impl ModPath { _ => None, } } - - pub fn unescaped(&self) -> UnescapedModPath<'_> { - UnescapedModPath(self) + pub fn display_verbatim<'a>( + &'a self, + db: &'a dyn crate::db::ExpandDatabase, + ) -> impl fmt::Display + 'a { + Display { db, path: self, edition: None } } pub fn display<'a>( @@ -145,7 +138,7 @@ impl ModPath { db: &'a dyn crate::db::ExpandDatabase, edition: Edition, ) -> impl fmt::Display + 'a { - Display { db, path: self, edition } + Display { db, path: self, edition: Some(edition) } } } @@ -158,23 +151,12 @@ impl Extend for ModPath { struct Display<'a> { db: &'a dyn ExpandDatabase, path: &'a ModPath, - edition: Edition, + edition: Option, } impl fmt::Display for Display<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - display_fmt_path(self.db, self.path, f, Escape::IfNeeded(self.edition)) - } -} - -struct UnescapedDisplay<'a> { - db: &'a dyn ExpandDatabase, - path: &'a UnescapedModPath<'a>, -} - -impl fmt::Display for UnescapedDisplay<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - display_fmt_path(self.db, self.path.0, f, Escape::No) + display_fmt_path(self.db, self.path, f, self.edition) } } @@ -184,16 +166,11 @@ impl From for ModPath { } } -enum Escape { - No, - IfNeeded(Edition), -} - fn display_fmt_path( db: &dyn ExpandDatabase, path: &ModPath, f: &mut fmt::Formatter<'_>, - escaped: Escape, + edition: Option, ) -> fmt::Result { let mut first_segment = true; let mut add_segment = |s| -> fmt::Result { @@ -221,10 +198,10 @@ fn display_fmt_path( f.write_str("::")?; } first_segment = false; - match escaped { - Escape::IfNeeded(edition) => segment.display(db, edition).fmt(f)?, - Escape::No => segment.unescaped().display(db).fmt(f)?, - } + match edition { + Some(edition) => segment.display(db, edition).fmt(f)?, + None => fmt::Display::fmt(segment.as_str(), f)?, + }; } Ok(()) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs index cc53d2e34aacb..848870c3a3844 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs @@ -4,8 +4,8 @@ use std::fmt; use intern::{sym, Symbol}; use span::{Edition, SyntaxContextId}; -use syntax::ast; use syntax::utils::is_raw_identifier; +use syntax::{ast, format_smolstr}; /// `Name` is a wrapper around string, which is used in hir for both references /// and declarations. In theory, names should also carry hygiene info, but we are @@ -51,33 +51,26 @@ impl PartialEq for Name { } } +impl PartialEq<&Symbol> for Name { + fn eq(&self, &sym: &&Symbol) -> bool { + self.symbol == *sym + } +} + impl PartialEq for Symbol { fn eq(&self, name: &Name) -> bool { *self == name.symbol } } -/// Wrapper of `Name` to print the name without "r#" even when it is a raw identifier. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct UnescapedName<'a>(&'a Name); - -impl<'a> UnescapedName<'a> { - pub fn display(self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a { - _ = db; - UnescapedDisplay { name: self } - } - #[doc(hidden)] - pub fn display_no_db(self) -> impl fmt::Display + 'a { - UnescapedDisplay { name: self } +impl PartialEq for &Symbol { + fn eq(&self, name: &Name) -> bool { + **self == name.symbol } } impl Name { - /// Note: this is private to make creating name from random string hard. - /// Hopefully, this should allow us to integrate hygiene cleaner in the - /// future, and to switch to interned representation of names. fn new_text(text: &str) -> Name { - debug_assert!(!text.starts_with("r#")); Name { symbol: Symbol::intern(text), ctx: () } } @@ -87,12 +80,15 @@ impl Name { // Can't do that for all `SyntaxContextId`s because it breaks Salsa. ctx.remove_root_edition(); _ = ctx; - Self::new_text(text) + match text.strip_prefix("r#") { + Some(text) => Self::new_text(text), + None => Self::new_text(text), + } } pub fn new_root(text: &str) -> Name { // The edition doesn't matter for hygiene. - Self::new(text.trim_start_matches("r#"), SyntaxContextId::root(Edition::Edition2015)) + Self::new(text, SyntaxContextId::root(Edition::Edition2015)) } pub fn new_tuple_field(idx: usize) -> Name { @@ -119,12 +115,22 @@ impl Name { } pub fn new_lifetime(lt: &ast::Lifetime) -> Name { - Self::new_text(lt.text().as_str().trim_start_matches("r#")) + let text = lt.text(); + match text.strip_prefix("'r#") { + Some(text) => Self::new_text(&format_smolstr!("'{text}")), + None => Self::new_text(text.as_str()), + } + } + + pub fn new_symbol(symbol: Symbol, ctx: SyntaxContextId) -> Self { + debug_assert!(!symbol.as_str().starts_with("r#")); + _ = ctx; + Self { symbol, ctx: () } } - /// Resolve a name from the text of token. - fn resolve(raw_text: &str) -> Name { - Name::new_text(raw_text.trim_start_matches("r#")) + // FIXME: This needs to go once we have hygiene + pub fn new_symbol_root(sym: Symbol) -> Self { + Self::new_symbol(sym, SyntaxContextId::root(Edition::Edition2015)) } /// A fake name for things missing in the source code. @@ -161,22 +167,19 @@ impl Name { self.symbol.as_str().parse().ok() } + /// Whether this name needs to be escaped in the given edition via `r#`. + pub fn needs_escape(&self, edition: Edition) -> bool { + is_raw_identifier(self.symbol.as_str(), edition) + } + /// Returns the text this name represents if it isn't a tuple field. /// /// Do not use this for user-facing text, use `display` instead to handle editions properly. + // FIXME: This should take a database argument to hide the interning pub fn as_str(&self) -> &str { self.symbol.as_str() } - // FIXME: Remove this - pub fn unescaped(&self) -> UnescapedName<'_> { - UnescapedName(self) - } - - pub fn needs_escape(&self, edition: Edition) -> bool { - is_raw_identifier(self.symbol.as_str(), edition) - } - pub fn display<'a>( &'a self, db: &dyn crate::db::ExpandDatabase, @@ -186,7 +189,7 @@ impl Name { self.display_no_db(edition) } - // FIXME: Remove this + // FIXME: Remove this in favor of `display`, see fixme on `as_str` #[doc(hidden)] pub fn display_no_db(&self, edition: Edition) -> impl fmt::Display + '_ { Display { name: self, needs_escaping: is_raw_identifier(self.symbol.as_str(), edition) } @@ -195,24 +198,6 @@ impl Name { pub fn symbol(&self) -> &Symbol { &self.symbol } - - pub fn new_symbol(symbol: Symbol, ctx: SyntaxContextId) -> Self { - debug_assert!(!symbol.as_str().starts_with("r#")); - _ = ctx; - Self { symbol, ctx: () } - } - - // FIXME: This needs to go once we have hygiene - pub fn new_symbol_root(sym: Symbol) -> Self { - debug_assert!(!sym.as_str().starts_with("r#")); - Self { symbol: sym, ctx: () } - } - - // FIXME: Remove this - #[inline] - pub fn eq_ident(&self, ident: &str) -> bool { - self.as_str() == ident.trim_start_matches("r#") - } } struct Display<'a> { @@ -229,17 +214,6 @@ impl fmt::Display for Display<'_> { } } -struct UnescapedDisplay<'a> { - name: UnescapedName<'a>, -} - -impl fmt::Display for UnescapedDisplay<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let symbol = self.name.0.symbol.as_str(); - fmt::Display::fmt(symbol, f) - } -} - pub trait AsName { fn as_name(&self) -> Name; } @@ -248,14 +222,14 @@ impl AsName for ast::NameRef { fn as_name(&self) -> Name { match self.as_tuple_field() { Some(idx) => Name::new_tuple_field(idx), - None => Name::resolve(&self.text()), + None => Name::new_root(&self.text()), } } } impl AsName for ast::Name { fn as_name(&self) -> Name { - Name::resolve(&self.text()) + Name::new_root(&self.text()) } } @@ -270,7 +244,7 @@ impl AsName for ast::NameOrNameRef { impl AsName for tt::Ident { fn as_name(&self) -> Name { - Name::resolve(self.sym.as_str()) + Name::new_root(self.sym.as_str()) } } @@ -288,6 +262,6 @@ impl AsName for ast::FieldKind { impl AsName for base_db::Dependency { fn as_name(&self) -> Name { - Name::new_text(&self.name) + Name::new_root(&self.name) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index 2b5342314a65a..62feca5f8cbbf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -17,7 +17,7 @@ use crate::{ TraitEnvironment, Ty, TyBuilder, TyKind, }; -static AUTODEREF_RECURSION_LIMIT: Limit = Limit::new(10); +static AUTODEREF_RECURSION_LIMIT: Limit = Limit::new(20); #[derive(Debug)] pub(crate) enum AutoderefKind { @@ -39,7 +39,7 @@ pub fn autoderef( ) -> impl Iterator { let mut table = InferenceTable::new(db, env); let ty = table.instantiate_canonical(ty); - let mut autoderef = Autoderef::new_no_tracking(&mut table, ty, false); + let mut autoderef = Autoderef::new_no_tracking(&mut table, ty, false, false); let mut v = Vec::new(); while let Some((ty, _steps)) = autoderef.next() { // `ty` may contain unresolved inference variables. Since there's no chance they would be @@ -49,7 +49,7 @@ pub fn autoderef( // If the deref chain contains a cycle (e.g. `A` derefs to `B` and `B` derefs to `A`), we // would revisit some already visited types. Stop here to avoid duplication. // - // XXX: The recursion limit for `Autoderef` is currently 10, so `Vec::contains()` shouldn't + // XXX: The recursion limit for `Autoderef` is currently 20, so `Vec::contains()` shouldn't // be too expensive. Replace this duplicate check with `FxHashSet` if it proves to be more // performant. if v.contains(&resolved) { @@ -89,12 +89,18 @@ pub(crate) struct Autoderef<'table, 'db, T = Vec<(AutoderefKind, Ty)>> { at_start: bool, steps: T, explicit: bool, + use_receiver_trait: bool, } impl<'table, 'db> Autoderef<'table, 'db> { - pub(crate) fn new(table: &'table mut InferenceTable<'db>, ty: Ty, explicit: bool) -> Self { + pub(crate) fn new( + table: &'table mut InferenceTable<'db>, + ty: Ty, + explicit: bool, + use_receiver_trait: bool, + ) -> Self { let ty = table.resolve_ty_shallow(&ty); - Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit } + Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit, use_receiver_trait } } pub(crate) fn steps(&self) -> &[(AutoderefKind, Ty)] { @@ -107,9 +113,10 @@ impl<'table, 'db> Autoderef<'table, 'db, usize> { table: &'table mut InferenceTable<'db>, ty: Ty, explicit: bool, + use_receiver_trait: bool, ) -> Self { let ty = table.resolve_ty_shallow(&ty); - Autoderef { table, ty, at_start: true, steps: 0, explicit } + Autoderef { table, ty, at_start: true, steps: 0, explicit, use_receiver_trait } } } @@ -137,7 +144,8 @@ impl Iterator for Autoderef<'_, '_, T> { return None; } - let (kind, new_ty) = autoderef_step(self.table, self.ty.clone(), self.explicit)?; + let (kind, new_ty) = + autoderef_step(self.table, self.ty.clone(), self.explicit, self.use_receiver_trait)?; self.steps.push(kind, &self.ty); self.ty = new_ty; @@ -150,11 +158,12 @@ pub(crate) fn autoderef_step( table: &mut InferenceTable<'_>, ty: Ty, explicit: bool, + use_receiver_trait: bool, ) -> Option<(AutoderefKind, Ty)> { if let Some(derefed) = builtin_deref(table.db, &ty, explicit) { Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed))) } else { - Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?)) + Some((AutoderefKind::Overloaded, deref_by_trait(table, ty, use_receiver_trait)?)) } } @@ -176,6 +185,7 @@ pub(crate) fn builtin_deref<'ty>( pub(crate) fn deref_by_trait( table @ &mut InferenceTable { db, .. }: &mut InferenceTable<'_>, ty: Ty, + use_receiver_trait: bool, ) -> Option { let _p = tracing::info_span!("deref_by_trait").entered(); if table.resolve_ty_shallow(&ty).inference_var(Interner).is_some() { @@ -183,14 +193,25 @@ pub(crate) fn deref_by_trait( return None; } - let deref_trait = - db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait())?; + let trait_id = || { + if use_receiver_trait { + if let Some(receiver) = + db.lang_item(table.trait_env.krate, LangItem::Receiver).and_then(|l| l.as_trait()) + { + return Some(receiver); + } + } + // Old rustc versions might not have `Receiver` trait. + // Fallback to `Deref` if they don't + db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait()) + }; + let trait_id = trait_id()?; let target = db - .trait_data(deref_trait) + .trait_data(trait_id) .associated_type_by_name(&Name::new_symbol_root(sym::Target.clone()))?; let projection = { - let b = TyBuilder::subst_for_def(db, deref_trait, None); + let b = TyBuilder::subst_for_def(db, trait_id, None); if b.remaining() != 1 { // the Target type + Deref trait should only have one generic parameter, // namely Deref's Self type diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs index 4991d173b9c42..774991560e9ca 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs @@ -231,8 +231,7 @@ impl<'a> DeclValidator<'a> { .filter_map(|(pat_id, pat)| match pat { Pat::Bind { id, .. } => { let bind_name = &body.bindings[*id].name; - let mut suggested_text = - to_lower_snake_case(&bind_name.unescaped().display_no_db().to_smolstr())?; + let mut suggested_text = to_lower_snake_case(bind_name.as_str())?; if is_raw_identifier(&suggested_text, edition) { suggested_text.insert_str(0, "r#"); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 3545bf7677671..ae8fbe2ce6d76 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -34,6 +34,7 @@ use rustc_apfloat::{ ieee::{Half as f16, Quad as f128}, Float, }; +use rustc_hash::FxHashSet; use smallvec::SmallVec; use span::Edition; use stdx::never; @@ -87,6 +88,35 @@ pub struct HirFormatter<'a> { omit_verbose_types: bool, closure_style: ClosureStyle, display_target: DisplayTarget, + bounds_formatting_ctx: BoundsFormattingCtx, +} + +#[derive(Default)] +enum BoundsFormattingCtx { + Entered { + /// We can have recursive bounds like the following case: + /// ```rust + /// where + /// T: Foo, + /// T::FooAssoc: Baz<::BarAssoc> + Bar + /// ``` + /// So, record the projection types met while formatting bounds and + //. prevent recursing into their bounds to avoid infinite loops. + projection_tys_met: FxHashSet, + }, + #[default] + Exited, +} + +impl BoundsFormattingCtx { + fn contains(&mut self, proj: &ProjectionTy) -> bool { + match self { + BoundsFormattingCtx::Entered { projection_tys_met } => { + projection_tys_met.contains(proj) + } + BoundsFormattingCtx::Exited => false, + } + } } impl HirFormatter<'_> { @@ -97,6 +127,30 @@ impl HirFormatter<'_> { fn end_location_link(&mut self) { self.fmt.end_location_link(); } + + fn format_bounds_with T>( + &mut self, + target: ProjectionTy, + format_bounds: F, + ) -> T { + match self.bounds_formatting_ctx { + BoundsFormattingCtx::Entered { ref mut projection_tys_met } => { + projection_tys_met.insert(target); + format_bounds(self) + } + BoundsFormattingCtx::Exited => { + let mut projection_tys_met = FxHashSet::default(); + projection_tys_met.insert(target); + self.bounds_formatting_ctx = BoundsFormattingCtx::Entered { projection_tys_met }; + let res = format_bounds(self); + // Since we want to prevent only the infinite recursions in bounds formatting + // and do not want to skip formatting of other separate bounds, clear context + // when exiting the formatting of outermost bounds + self.bounds_formatting_ctx = BoundsFormattingCtx::Exited; + res + } + } + } } pub trait HirDisplay { @@ -220,6 +274,7 @@ pub trait HirDisplay { closure_style: ClosureStyle::ImplFn, display_target: DisplayTarget::SourceCode { module_id, allow_opaque }, show_container_bounds: false, + bounds_formatting_ctx: Default::default(), }) { Ok(()) => {} Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"), @@ -427,6 +482,7 @@ impl HirDisplayWrapper<'_, T> { display_target: self.display_target, closure_style: self.closure_style, show_container_bounds: self.show_container_bounds, + bounds_formatting_ctx: Default::default(), }) } @@ -479,42 +535,46 @@ impl HirDisplay for ProjectionTy { // `::Assoc` if !f.display_target.is_source_code() { if let TyKind::Placeholder(idx) = self_ty.kind(Interner) { - let db = f.db; - let id = from_placeholder_idx(db, *idx); - let generics = generics(db.upcast(), id.parent); - - let substs = generics.placeholder_subst(db); - let bounds = db - .generic_predicates(id.parent) - .iter() - .map(|pred| pred.clone().substitute(Interner, &substs)) - .filter(|wc| match wc.skip_binders() { - WhereClause::Implemented(tr) => { - match tr.self_type_parameter(Interner).kind(Interner) { - TyKind::Alias(AliasTy::Projection(proj)) => proj == self, - _ => false, + if !f.bounds_formatting_ctx.contains(self) { + let db = f.db; + let id = from_placeholder_idx(db, *idx); + let generics = generics(db.upcast(), id.parent); + + let substs = generics.placeholder_subst(db); + let bounds = db + .generic_predicates(id.parent) + .iter() + .map(|pred| pred.clone().substitute(Interner, &substs)) + .filter(|wc| match wc.skip_binders() { + WhereClause::Implemented(tr) => { + matches!( + tr.self_type_parameter(Interner).kind(Interner), + TyKind::Alias(_) + ) } - } - WhereClause::TypeOutlives(t) => match t.ty.kind(Interner) { - TyKind::Alias(AliasTy::Projection(proj)) => proj == self, - _ => false, - }, - // We shouldn't be here if these exist - WhereClause::AliasEq(_) => false, - WhereClause::LifetimeOutlives(_) => false, - }) - .collect::>(); - if !bounds.is_empty() { - return write_bounds_like_dyn_trait_with_prefix( - f, - "impl", - Either::Left( - &TyKind::Alias(AliasTy::Projection(self.clone())).intern(Interner), - ), - &bounds, - SizedByDefault::NotSized, - ); - }; + WhereClause::TypeOutlives(t) => { + matches!(t.ty.kind(Interner), TyKind::Alias(_)) + } + // We shouldn't be here if these exist + WhereClause::AliasEq(_) => false, + WhereClause::LifetimeOutlives(_) => false, + }) + .collect::>(); + if !bounds.is_empty() { + return f.format_bounds_with(self.clone(), |f| { + write_bounds_like_dyn_trait_with_prefix( + f, + "impl", + Either::Left( + &TyKind::Alias(AliasTy::Projection(self.clone())) + .intern(Interner), + ), + &bounds, + SizedByDefault::NotSized, + ) + }); + } + } } } @@ -1159,6 +1219,7 @@ impl HirDisplay for Ty { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }, ) { write!(f, "{}", path.display(f.db.upcast(), f.edition()))?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 2523aba538334..9283c46d0f611 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -277,7 +277,7 @@ impl CapturedItem { /// Converts the place to a name that can be inserted into source code. pub fn place_to_name(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String { let body = db.body(owner); - let mut result = body[self.place.local].name.unescaped().display(db.upcast()).to_string(); + let mut result = body[self.place.local].name.as_str().to_owned(); for proj in &self.place.projections { match proj { ProjectionElem::Deref => {} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 2fe90a8a92432..d40816ba8ced2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -420,7 +420,7 @@ impl InferenceTable<'_> { let snapshot = self.snapshot(); - let mut autoderef = Autoderef::new(self, from_ty.clone(), false); + let mut autoderef = Autoderef::new(self, from_ty.clone(), false, false); let mut first_error = None; let mut found = None; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 6b6c0348dcb4f..b951443897cb0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -487,7 +487,7 @@ impl InferenceContext<'_> { } Expr::Call { callee, args, .. } => { let callee_ty = self.infer_expr(*callee, &Expectation::none(), ExprIsRead::Yes); - let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false); + let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false, true); let (res, derefed_callee) = loop { let Some((callee_deref_ty, _)) = derefs.next() else { break (None, callee_ty.clone()); @@ -854,7 +854,7 @@ impl InferenceContext<'_> { if let Some(derefed) = builtin_deref(self.table.db, &inner_ty, true) { self.resolve_ty_shallow(derefed) } else { - deref_by_trait(&mut self.table, inner_ty) + deref_by_trait(&mut self.table, inner_ty, false) .unwrap_or_else(|| self.err_ty()) } } @@ -1718,7 +1718,7 @@ impl InferenceContext<'_> { receiver_ty: &Ty, name: &Name, ) -> Option<(Ty, Either, Vec, bool)> { - let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone(), false); + let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone(), false, false); let mut private_field = None; let res = autoderef.by_ref().find_map(|(derefed_ty, _)| { let (field_id, parameters) = match derefed_ty.kind(Interner) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 182032f04812d..1cea67ee96419 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -528,7 +528,7 @@ impl ReceiverAdjustments { let mut ty = table.resolve_ty_shallow(&ty); let mut adjust = Vec::new(); for _ in 0..self.autoderefs { - match autoderef::autoderef_step(table, ty.clone(), true) { + match autoderef::autoderef_step(table, ty.clone(), true, false) { None => { never!("autoderef not possible for {:?}", ty); ty = TyKind::Error.intern(Interner); @@ -1106,7 +1106,8 @@ fn iterate_method_candidates_by_receiver( // be found in any of the derefs of receiver_ty, so we have to go through // that, including raw derefs. table.run_in_snapshot(|table| { - let mut autoderef = autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true); + let mut autoderef = + autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true, true); while let Some((self_ty, _)) = autoderef.next() { iterate_inherent_methods( &self_ty, @@ -1123,7 +1124,8 @@ fn iterate_method_candidates_by_receiver( ControlFlow::Continue(()) })?; table.run_in_snapshot(|table| { - let mut autoderef = autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true); + let mut autoderef = + autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true, true); while let Some((self_ty, _)) = autoderef.next() { if matches!(self_ty.kind(Interner), TyKind::InferenceVar(_, TyVariableKind::General)) { // don't try to resolve methods on unknown types @@ -1709,7 +1711,7 @@ fn autoderef_method_receiver( ty: Ty, ) -> Vec<(Canonical, ReceiverAdjustments)> { let mut deref_chain: Vec<_> = Vec::new(); - let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ty, false); + let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ty, false, true); while let Some((ty, derefs)) = autoderef.next() { deref_chain.push(( autoderef.table.canonicalize(ty), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index 74acf23b75ab6..8866de22dfb99 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -1343,7 +1343,7 @@ fn foo(a: &T) { fn autoderef_visibility_field() { check( r#" -//- minicore: deref +//- minicore: receiver mod a { pub struct Foo(pub char); pub struct Bar(i32); @@ -1375,7 +1375,7 @@ fn autoderef_visibility_method() { cov_mark::check!(autoderef_candidate_not_visible); check( r#" -//- minicore: deref +//- minicore: receiver mod a { pub struct Foo(pub char); impl Foo { @@ -1741,7 +1741,7 @@ fn main() { fn deref_fun_1() { check_types( r#" -//- minicore: deref +//- minicore: receiver struct A(T, U); struct B(T); @@ -1782,7 +1782,7 @@ fn test() { fn deref_fun_2() { check_types( r#" -//- minicore: deref +//- minicore: receiver struct A(T, U); struct B(T); @@ -1903,7 +1903,7 @@ pub fn test(generic_args: impl Into) { fn bad_inferred_reference_2() { check_no_mismatches( r#" -//- minicore: deref +//- minicore: receiver trait ExactSizeIterator { fn len(&self) -> usize; } @@ -2054,7 +2054,7 @@ fn foo() { fn box_deref_is_builtin() { check( r#" -//- minicore: deref +//- minicore: receiver use core::ops::Deref; #[lang = "owned_box"] @@ -2087,7 +2087,7 @@ fn test() { fn manually_drop_deref_is_not_builtin() { check( r#" -//- minicore: manually_drop, deref +//- minicore: manually_drop, receiver struct Foo; impl Foo { fn foo(&self) {} @@ -2105,7 +2105,7 @@ fn test() { fn mismatched_args_due_to_supertraits_with_deref() { check_no_mismatches( r#" -//- minicore: deref +//- minicore: receiver use core::ops::Deref; trait Trait1 { @@ -2139,3 +2139,34 @@ fn problem_method() { "#, ); } + +#[test] +fn receiver_without_deref_impl() { + check( + r#" +//- minicore: receiver +use core::ops::Receiver; + +struct Foo; + +impl Foo { + fn foo1(self: &Bar) -> i32 { 42 } + fn foo2(self: Bar) -> bool { true } +} + +struct Bar; + +impl Receiver for Bar { + type Target = Foo; +} + +fn main() { + let bar = Bar; + let _v1 = bar.foo1(); + //^^^ type: i32 + let _v2 = bar.foo2(); + //^^^ type: bool +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index db3121d3cd35f..0cbc75726bf39 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -45,7 +45,7 @@ use hir_def::{ body::BodyDiagnostic, data::{adt::VariantData, TraitFlags}, generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance}, - hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, Pat}, + hir::{BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, LabelId, Pat}, item_tree::{AttrOwner, FieldParent, ItemTreeFieldId, ItemTreeNode}, lang_item::LangItemTarget, layout::{self, ReprOptions, TargetDataLayout}, @@ -2470,20 +2470,31 @@ impl Param { } pub fn as_local(&self, db: &dyn HirDatabase) -> Option { - let parent = match self.func { - Callee::Def(CallableDefId::FunctionId(it)) => DefWithBodyId::FunctionId(it), - Callee::Closure(closure, _) => db.lookup_intern_closure(closure.into()).0, - _ => return None, - }; - let body = db.body(parent); - if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) { - Some(Local { parent, binding_id: self_param }) - } else if let Pat::Bind { id, .. } = - &body[body.params[self.idx - body.self_param.is_some() as usize]] - { - Some(Local { parent, binding_id: *id }) - } else { - None + match self.func { + Callee::Def(CallableDefId::FunctionId(it)) => { + let parent = DefWithBodyId::FunctionId(it); + let body = db.body(parent); + if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) { + Some(Local { parent, binding_id: self_param }) + } else if let Pat::Bind { id, .. } = + &body[body.params[self.idx - body.self_param.is_some() as usize]] + { + Some(Local { parent, binding_id: *id }) + } else { + None + } + } + Callee::Closure(closure, _) => { + let c = db.lookup_intern_closure(closure.into()); + let body = db.body(c.0); + if let Expr::Closure { args, .. } = &body[c.1] { + if let Pat::Bind { id, .. } = &body[args[self.idx]] { + return Some(Local { parent: c.0, binding_id: *id }); + } + } + None + } + _ => None, } } @@ -2756,6 +2767,15 @@ impl Trait { traits.iter().map(|tr| Trait::from(*tr)).collect() } + pub fn function(self, db: &dyn HirDatabase, name: impl PartialEq) -> Option { + db.trait_data(self.id).items.iter().find(|(n, _)| name == *n).and_then( + |&(_, it)| match it { + AssocItemId::FunctionId(id) => Some(Function { id }), + _ => None, + }, + ) + } + pub fn items(self, db: &dyn HirDatabase) -> Vec { db.trait_data(self.id).items.iter().map(|(_name, it)| (*it).into()).collect() } @@ -4673,6 +4693,10 @@ impl Type { matches!(self.ty.kind(Interner), TyKind::Scalar(Scalar::Bool)) } + pub fn is_str(&self) -> bool { + matches!(self.ty.kind(Interner), TyKind::Str) + } + pub fn is_never(&self) -> bool { matches!(self.ty.kind(Interner), TyKind::Never) } diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 523bc6f10aab6..09470bed9cfb4 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1439,8 +1439,20 @@ impl<'db> SemanticsImpl<'db> { self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call) } - pub fn resolve_known_blanket_dual_impls(&self, call: &ast::MethodCallExpr) -> Option { - self.analyze(call.syntax())?.resolve_known_blanket_dual_impls(self.db, call) + /// Env is used to derive the trait environment + // FIXME: better api for the trait environment + pub fn resolve_trait_impl_method( + &self, + env: Type, + trait_: Trait, + func: Function, + subst: impl IntoIterator, + ) -> Option { + let mut substs = hir_ty::TyBuilder::subst_for_def(self.db, TraitId::from(trait_), None); + for s in subst { + substs = substs.push(s.ty); + } + Some(self.db.lookup_impl_method(env.env, func.into(), substs.build()).0.into()) } fn resolve_range_pat(&self, range_pat: &ast::RangePat) -> Option { @@ -1471,6 +1483,8 @@ impl<'db> SemanticsImpl<'db> { self.analyze(try_expr.syntax())?.resolve_try_expr(self.db, try_expr) } + // This does not resolve the method call to the correct trait impl! + // We should probably fix that. pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option { self.analyze(call.syntax())?.resolve_method_call_as_callable(self.db, call) } diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 6b78d7a3631fa..b699ccde4128e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -322,68 +322,6 @@ impl SourceAnalyzer { } } - // If the method is into(), try_into(), parse(), resolve it to from, try_from, from_str. - pub(crate) fn resolve_known_blanket_dual_impls( - &self, - db: &dyn HirDatabase, - call: &ast::MethodCallExpr, - ) -> Option { - // e.g. if the method call is let b = a.into(), - // - receiver_type is A (type of a) - // - return_type is B (type of b) - // We will find the definition of B::from(a: A). - let callable = self.resolve_method_call_as_callable(db, call)?; - let (_, receiver_type) = callable.receiver_param(db)?; - let return_type = callable.return_type(); - let (search_method, substs) = match call.name_ref()?.text().as_str() { - "into" => { - let trait_ = - self.resolver.resolve_known_trait(db.upcast(), &path![core::convert::From])?; - ( - self.trait_fn(db, trait_, "from")?, - hir_ty::TyBuilder::subst_for_def(db, trait_, None) - .push(return_type.ty) - .push(receiver_type.ty) - .build(), - ) - } - "try_into" => { - let trait_ = self - .resolver - .resolve_known_trait(db.upcast(), &path![core::convert::TryFrom])?; - ( - self.trait_fn(db, trait_, "try_from")?, - hir_ty::TyBuilder::subst_for_def(db, trait_, None) - // If the method is try_into() or parse(), return_type is Result. - // Get T from type arguments of Result. - .push(return_type.type_arguments().next()?.ty) - .push(receiver_type.ty) - .build(), - ) - } - "parse" => { - let trait_ = - self.resolver.resolve_known_trait(db.upcast(), &path![core::str::FromStr])?; - ( - self.trait_fn(db, trait_, "from_str")?, - hir_ty::TyBuilder::subst_for_def(db, trait_, None) - .push(return_type.type_arguments().next()?.ty) - .build(), - ) - } - _ => return None, - }; - - let found_method = self.resolve_impl_method_or_trait_def(db, search_method, substs); - // If found_method == search_method, the method in trait itself is resolved. - // It means the blanket dual impl is not found. - if found_method == search_method { - None - } else { - Some(found_method.into()) - } - } - pub(crate) fn resolve_expr_as_callable( &self, db: &dyn HirDatabase, @@ -1309,18 +1247,6 @@ impl SourceAnalyzer { Some((trait_id, fn_id)) } - fn trait_fn( - &self, - db: &dyn HirDatabase, - trait_id: TraitId, - method_name: &str, - ) -> Option { - db.trait_data(trait_id).items.iter().find_map(|(item_name, item)| match item { - AssocItemId::FunctionId(t) if item_name.as_str() == method_name => Some(*t), - _ => None, - }) - } - fn ty_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<&Ty> { self.infer.as_ref()?.type_of_expr_or_pat(self.expr_id(db, expr)?) } diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index a6b8ed70c363a..2ebd88edae2d7 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -3,7 +3,7 @@ use either::Either; use hir_def::{ db::DefDatabase, - item_scope::{ImportId, ImportOrExternCrate}, + item_scope::{ImportId, ImportOrExternCrate, ImportOrGlob}, per_ns::Item, src::{HasChildSource, HasSource}, visibility::{Visibility, VisibilityExplicitness}, @@ -55,9 +55,10 @@ impl DeclarationLocation { } /// Represents an outstanding module that the symbol collector must collect symbols from. +#[derive(Debug)] struct SymbolCollectorWork { module_id: ModuleId, - parent: Option, + parent: Option, } pub struct SymbolCollector<'a> { @@ -81,7 +82,15 @@ impl<'a> SymbolCollector<'a> { } } + pub fn new_module(db: &dyn HirDatabase, module: Module) -> Box<[FileSymbol]> { + let mut symbol_collector = SymbolCollector::new(db); + symbol_collector.collect(module); + symbol_collector.finish() + } + pub fn collect(&mut self, module: Module) { + let _p = tracing::info_span!("SymbolCollector::collect", ?module).entered(); + tracing::info!(?module, "SymbolCollector::collect",); self.edition = module.krate().edition(self.db); // The initial work is the root module we're collecting, additional work will @@ -97,16 +106,12 @@ impl<'a> SymbolCollector<'a> { self.symbols.into_iter().collect() } - pub fn collect_module(db: &dyn HirDatabase, module: Module) -> Box<[FileSymbol]> { - let mut symbol_collector = SymbolCollector::new(db); - symbol_collector.collect(module); - symbol_collector.finish() - } - fn do_work(&mut self, work: SymbolCollectorWork) { + let _p = tracing::info_span!("SymbolCollector::do_work", ?work).entered(); + tracing::info!(?work, "SymbolCollector::do_work"); self.db.unwind_if_cancelled(); - let parent_name = work.parent.and_then(|id| self.def_with_body_id_name(id)); + let parent_name = work.parent.map(|name| name.as_str().to_smolstr()); self.with_container_name(parent_name, |s| s.collect_from_module(work.module_id)); } @@ -116,18 +121,18 @@ impl<'a> SymbolCollector<'a> { ModuleDefId::ModuleId(id) => this.push_module(id, name), ModuleDefId::FunctionId(id) => { this.push_decl(id, name, false); - this.collect_from_body(id); + this.collect_from_body(id, Some(name.clone())); } ModuleDefId::AdtId(AdtId::StructId(id)) => this.push_decl(id, name, false), ModuleDefId::AdtId(AdtId::EnumId(id)) => this.push_decl(id, name, false), ModuleDefId::AdtId(AdtId::UnionId(id)) => this.push_decl(id, name, false), ModuleDefId::ConstId(id) => { this.push_decl(id, name, false); - this.collect_from_body(id); + this.collect_from_body(id, Some(name.clone())); } ModuleDefId::StaticId(id) => { this.push_decl(id, name, false); - this.collect_from_body(id); + this.collect_from_body(id, Some(name.clone())); } ModuleDefId::TraitId(id) => { this.push_decl(id, name, false); @@ -153,24 +158,32 @@ impl<'a> SymbolCollector<'a> { // Nested trees are very common, so a cache here will hit a lot. let import_child_source_cache = &mut FxHashMap::default(); - let mut push_import = |this: &mut Self, i: ImportId, name: &Name, def: ModuleDefId| { + let is_explicit_import = |vis| match vis { + Visibility::Public => true, + Visibility::Module(_, VisibilityExplicitness::Explicit) => true, + Visibility::Module(_, VisibilityExplicitness::Implicit) => false, + }; + + let mut push_import = |this: &mut Self, i: ImportId, name: &Name, def: ModuleDefId, vis| { let source = import_child_source_cache - .entry(i.import) - .or_insert_with(|| i.import.child_source(this.db.upcast())); + .entry(i.use_) + .or_insert_with(|| i.use_.child_source(this.db.upcast())); let Some(use_tree_src) = source.value.get(i.idx) else { return }; - let Some(name_ptr) = use_tree_src - .rename() - .and_then(|rename| rename.name()) - .map(Either::Left) - .or_else(|| use_tree_src.path()?.segment()?.name_ref().map(Either::Right)) - .map(|it| AstPtr::new(&it)) - else { + let rename = use_tree_src.rename().and_then(|rename| rename.name()); + let name_syntax = match rename { + Some(name) => Some(Either::Left(name)), + None if is_explicit_import(vis) => { + (|| use_tree_src.path()?.segment()?.name_ref().map(Either::Right))() + } + None => None, + }; + let Some(name_syntax) = name_syntax else { return; }; let dec_loc = DeclarationLocation { hir_file_id: source.file_id, ptr: SyntaxNodePtr::new(use_tree_src.syntax()), - name_ptr, + name_ptr: AstPtr::new(&name_syntax), }; this.symbols.insert(FileSymbol { name: name.symbol().clone(), @@ -183,23 +196,23 @@ impl<'a> SymbolCollector<'a> { }; let push_extern_crate = - |this: &mut Self, i: ExternCrateId, name: &Name, def: ModuleDefId| { + |this: &mut Self, i: ExternCrateId, name: &Name, def: ModuleDefId, vis| { let loc = i.lookup(this.db.upcast()); let source = loc.source(this.db.upcast()); - let Some(name_ptr) = source - .value - .rename() - .and_then(|rename| rename.name()) - .map(Either::Left) - .or_else(|| source.value.name_ref().map(Either::Right)) - .map(|it| AstPtr::new(&it)) - else { + let rename = source.value.rename().and_then(|rename| rename.name()); + + let name_syntax = match rename { + Some(name) => Some(Either::Left(name)), + None if is_explicit_import(vis) => None, + None => source.value.name_ref().map(Either::Right), + }; + let Some(name_syntax) = name_syntax else { return; }; let dec_loc = DeclarationLocation { hir_file_id: source.file_id, ptr: SyntaxNodePtr::new(source.value.syntax()), - name_ptr, + name_ptr: AstPtr::new(&name_syntax), }; this.symbols.insert(FileSymbol { name: name.symbol().clone(), @@ -211,18 +224,6 @@ impl<'a> SymbolCollector<'a> { }); }; - let is_explicit_import = |vis| { - match vis { - Visibility::Module(_, VisibilityExplicitness::Explicit) => true, - Visibility::Module(_, VisibilityExplicitness::Implicit) => { - // consider imports in the crate root explicit, as these are visibly - // crate-wide anyways - module_id.is_crate_root() - } - Visibility::Public => true, - } - }; - let def_map = module_id.def_map(self.db.upcast()); let scope = &def_map[module_id.local_id].scope; @@ -232,14 +233,14 @@ impl<'a> SymbolCollector<'a> { for (name, Item { def, vis, import }) in scope.types() { if let Some(i) = import { - if is_explicit_import(vis) { - match i { - ImportOrExternCrate::Import(i) => push_import(self, i, name, def), - ImportOrExternCrate::ExternCrate(i) => { - push_extern_crate(self, i, name, def) - } + match i { + ImportOrExternCrate::Import(i) => push_import(self, i, name, def, vis), + ImportOrExternCrate::Glob(_) => (), + ImportOrExternCrate::ExternCrate(i) => { + push_extern_crate(self, i, name, def, vis) } } + continue; } // self is a declaration @@ -248,8 +249,9 @@ impl<'a> SymbolCollector<'a> { for (name, Item { def, vis, import }) in scope.macros() { if let Some(i) = import { - if is_explicit_import(vis) { - push_import(self, i, name, def.into()); + match i { + ImportOrGlob::Import(i) => push_import(self, i, name, def.into(), vis), + ImportOrGlob::Glob(_) => (), } continue; } @@ -259,8 +261,9 @@ impl<'a> SymbolCollector<'a> { for (name, Item { def, vis, import }) in scope.values() { if let Some(i) = import { - if is_explicit_import(vis) { - push_import(self, i, name, def); + match i { + ImportOrGlob::Import(i) => push_import(self, i, name, def, vis), + ImportOrGlob::Glob(_) => (), } continue; } @@ -269,7 +272,7 @@ impl<'a> SymbolCollector<'a> { } for const_id in scope.unnamed_consts() { - self.collect_from_body(const_id); + self.collect_from_body(const_id, None); } for (name, id) in scope.legacy_macros() { @@ -285,7 +288,7 @@ impl<'a> SymbolCollector<'a> { } } - fn collect_from_body(&mut self, body_id: impl Into) { + fn collect_from_body(&mut self, body_id: impl Into, name: Option) { let body_id = body_id.into(); let body = self.db.body(body_id); @@ -294,7 +297,7 @@ impl<'a> SymbolCollector<'a> { for (id, _) in def_map.modules() { self.work.push(SymbolCollectorWork { module_id: def_map.module_id(id), - parent: Some(body_id), + parent: name.clone(), }); } } @@ -333,24 +336,6 @@ impl<'a> SymbolCollector<'a> { } } - fn def_with_body_id_name(&self, body_id: DefWithBodyId) -> Option { - match body_id { - DefWithBodyId::FunctionId(id) => { - Some(self.db.function_data(id).name.display_no_db(self.edition).to_smolstr()) - } - DefWithBodyId::StaticId(id) => { - Some(self.db.static_data(id).name.display_no_db(self.edition).to_smolstr()) - } - DefWithBodyId::ConstId(id) => { - Some(self.db.const_data(id).name.as_ref()?.display_no_db(self.edition).to_smolstr()) - } - DefWithBodyId::VariantId(id) => { - Some(self.db.enum_variant_data(id).name.display_no_db(self.edition).to_smolstr()) - } - DefWithBodyId::InTypeConstId(_) => Some("in type const".into()), - } - } - fn push_assoc_item(&mut self, assoc_item_id: AssocItemId, name: &Name) { match assoc_item_id { AssocItemId::FunctionId(id) => self.push_decl(id, name, true), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs index 82d8db4258924..fb533077d9626 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs @@ -28,6 +28,7 @@ impl AssistConfig { prefer_no_std: self.prefer_no_std, prefer_prelude: self.prefer_prelude, prefer_absolute: self.prefer_absolute, + allow_unstable: true, } } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs index a5c5b08d5b0c7..eb784cd1226fd 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs @@ -159,7 +159,7 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_> }; // Verify this is `bool::then` that is being called. let func = ctx.sema.resolve_method_call(&mcall)?; - if !func.name(ctx.sema.db).eq_ident("then") { + if func.name(ctx.sema.db) != sym::then { return None; } let assoc = func.as_assoc_item(ctx.sema.db)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs index bb04a43cf9615..d34cf895cd90a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs @@ -343,11 +343,9 @@ fn compute_closure_type_params( let mut mentioned_names = mentioned_generic_params .iter() .filter_map(|param| match param { - hir::GenericParam::TypeParam(param) => { - Some(param.name(ctx.db()).unescaped().display(ctx.db()).to_smolstr()) - } + hir::GenericParam::TypeParam(param) => Some(param.name(ctx.db()).as_str().to_smolstr()), hir::GenericParam::ConstParam(param) => { - Some(param.name(ctx.db()).unescaped().display(ctx.db()).to_smolstr()) + Some(param.name(ctx.db()).as_str().to_smolstr()) } hir::GenericParam::LifetimeParam(_) => None, }) @@ -390,7 +388,7 @@ fn compute_closure_type_params( let has_name = syntax .descendants() .filter_map(ast::NameOrNameRef::cast) - .any(|name| mentioned_names.contains(&*name.text())); + .any(|name| mentioned_names.contains(name.text().trim_start_matches("r#"))); let mut has_new_params = false; if has_name { syntax diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 615b5d3f98b55..d4f2ea3bd941b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -170,7 +170,7 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va ), _ => false, }) - .any(|(name, _)| name.eq_ident(variant_name.text().as_str())) + .any(|(name, _)| name.as_str() == variant_name.text().trim_start_matches("r#")) } fn extract_generic_params( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index 97321f4ec1ef0..7b6f76d00452e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -1672,8 +1672,8 @@ macro_rules! vec { () => {Vec} } fn main() { - let $0vec = vec![]; - let _ = vec; + let $0items = vec![]; + let _ = items; } "#, "Extract into variable", @@ -1696,8 +1696,8 @@ macro_rules! vec { () => {Vec} } fn main() { - const $0VEC: Vec = vec![]; - let _ = VEC; + const $0ITEMS: Vec = vec![]; + let _ = ITEMS; } "#, "Extract into constant", @@ -1720,8 +1720,8 @@ macro_rules! vec { () => {Vec} } fn main() { - static $0VEC: Vec = vec![]; - let _ = VEC; + static $0ITEMS: Vec = vec![]; + let _ = ITEMS; } "#, "Extract into static", @@ -2019,8 +2019,8 @@ impl Vec { } fn foo(s: &mut S) { - let $0vec = &mut s.vec; - vec.push(0); + let $0items = &mut s.vec; + items.push(0); }"#, "Extract into variable", ); @@ -2106,8 +2106,8 @@ impl Vec { } fn foo(f: &mut Y) { - let $0vec = &mut f.field.field.vec; - vec.push(0); + let $0items = &mut f.field.field.vec; + items.push(0); }"#, "Extract into variable", ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs index 7a92d8911bf85..47e4a68293f0c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs @@ -48,7 +48,7 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) let (_, def) = module .scope(ctx.db(), None) .into_iter() - .find(|(name, _)| name.eq_ident(name_ref.text().as_str()))?; + .find(|(name, _)| name.as_str() == name_ref.text().trim_start_matches("r#"))?; let ScopeDef::ModuleDef(def) = def else { return None; }; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs index 8a7a06b380f51..10915f8aafb8d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs @@ -2,7 +2,7 @@ use ide_db::{ assists::{AssistId, AssistKind}, base_db::AnchoredPathBuf, }; -use syntax::{ast, AstNode}; +use syntax::{ast, AstNode, ToSmolStr}; use crate::{ assist_context::{AssistContext, Assists}, @@ -39,7 +39,7 @@ pub(crate) fn move_from_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op } let target = source_file.syntax().text_range(); - let module_name = module.name(ctx.db())?.unescaped().display(ctx.db()).to_string(); + let module_name = module.name(ctx.db())?.as_str().to_smolstr(); let path = format!("../{module_name}.rs"); let dst = AnchoredPathBuf { anchor: ctx.file_id().into(), path }; acc.add( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs index 9692b70592912..bbf18e21948eb 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs @@ -61,7 +61,7 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) -> .string_value_unescape() .is_none() => { - format_to!(buf, "{}/", name.unescaped().display(db)) + format_to!(buf, "{}/", name.as_str()) } _ => (), } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs index 2925e2334b44d..7b38c795dc80f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs @@ -2,7 +2,7 @@ use ide_db::{ assists::{AssistId, AssistKind}, base_db::AnchoredPathBuf, }; -use syntax::{ast, AstNode}; +use syntax::{ast, AstNode, ToSmolStr}; use crate::{ assist_context::{AssistContext, Assists}, @@ -39,7 +39,7 @@ pub(crate) fn move_to_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti } let target = source_file.syntax().text_range(); - let module_name = module.name(ctx.db())?.unescaped().display(ctx.db()).to_string(); + let module_name = module.name(ctx.db())?.as_str().to_smolstr(); let path = format!("./{module_name}/mod.rs"); let dst = AnchoredPathBuf { anchor: ctx.file_id().into(), path }; acc.add( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs index 849b8a42c6949..2a8465f634cfb 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs @@ -208,7 +208,7 @@ fn find_trait_method( if let Some(hir::AssocItem::Function(method)) = trait_.items(db).into_iter().find(|item: &hir::AssocItem| { item.name(db) - .map(|name| name.eq_ident(trait_method_name.text().as_str())) + .map(|name| name.as_str() == trait_method_name.text().trim_start_matches("r#")) .unwrap_or(false) }) { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs index 972303c2a0416..a79a82be45079 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs @@ -110,7 +110,7 @@ fn compute_fields_ranks( .fields(ctx.db()) .into_iter() .enumerate() - .map(|(idx, field)| (field.name(ctx.db()).unescaped().display(ctx.db()).to_string(), idx)) + .map(|(idx, field)| (field.name(ctx.db()).as_str().to_owned(), idx)) .collect(); Some(res) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs index eb1d538f8743a..c3404173eafe6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs @@ -122,7 +122,7 @@ fn compute_item_ranks( .iter() .flat_map(|i| i.name(ctx.db())) .enumerate() - .map(|(idx, name)| (name.unescaped().display(ctx.db()).to_string(), idx)) + .map(|(idx, name)| (name.as_str().to_owned(), idx)) .collect(), ) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index 40669c65c5766..a22e7b272ea05 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -188,9 +188,6 @@ impl Completions { resolution: hir::ScopeDef, doc_aliases: Vec, ) { - if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) { - return; - } let is_private_editable = match ctx.def_is_visible(&resolution) { Visible::Yes => false, Visible::Editable => true, @@ -216,9 +213,6 @@ impl Completions { local_name: hir::Name, resolution: hir::ScopeDef, ) { - if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) { - return; - } let is_private_editable = match ctx.def_is_visible(&resolution) { Visible::Yes => false, Visible::Editable => true, @@ -241,7 +235,7 @@ impl Completions { path_ctx: &PathCompletionCtx, e: hir::Enum, ) { - if !ctx.check_stability(Some(&e.attrs(ctx.db))) { + if !ctx.check_stability_and_hidden(e) { return; } e.variants(ctx.db) @@ -257,9 +251,6 @@ impl Completions { local_name: hir::Name, doc_aliases: Vec, ) { - if !ctx.check_stability(Some(&module.attrs(ctx.db))) { - return; - } self.add_path_resolution( ctx, path_ctx, @@ -276,9 +267,6 @@ impl Completions { mac: hir::Macro, local_name: hir::Name, ) { - if !ctx.check_stability(Some(&mac.attrs(ctx.db))) { - return; - } let is_private_editable = match ctx.is_visible(&mac) { Visible::Yes => false, Visible::Editable => true, @@ -302,9 +290,6 @@ impl Completions { func: hir::Function, local_name: Option, ) { - if !ctx.check_stability(Some(&func.attrs(ctx.db))) { - return; - } let is_private_editable = match ctx.is_visible(&func) { Visible::Yes => false, Visible::Editable => true, @@ -332,9 +317,6 @@ impl Completions { receiver: Option, local_name: Option, ) { - if !ctx.check_stability(Some(&func.attrs(ctx.db))) { - return; - } let is_private_editable = match ctx.is_visible(&func) { Visible::Yes => false, Visible::Editable => true, @@ -362,9 +344,6 @@ impl Completions { func: hir::Function, import: LocatedImport, ) { - if !ctx.check_stability(Some(&func.attrs(ctx.db))) { - return; - } let is_private_editable = match ctx.is_visible(&func) { Visible::Yes => false, Visible::Editable => true, @@ -387,9 +366,6 @@ impl Completions { } pub(crate) fn add_const(&mut self, ctx: &CompletionContext<'_>, konst: hir::Const) { - if !ctx.check_stability(Some(&konst.attrs(ctx.db))) { - return; - } let is_private_editable = match ctx.is_visible(&konst) { Visible::Yes => false, Visible::Editable => true, @@ -406,9 +382,6 @@ impl Completions { ctx: &CompletionContext<'_>, type_alias: hir::TypeAlias, ) { - if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) { - return; - } let is_private_editable = match ctx.is_visible(&type_alias) { Visible::Yes => false, Visible::Editable => true, @@ -438,7 +411,7 @@ impl Completions { variant: hir::Variant, path: hir::ModPath, ) { - if !ctx.check_stability(Some(&variant.attrs(ctx.db))) { + if !ctx.check_stability_and_hidden(variant) { return; } if let Some(builder) = @@ -455,7 +428,7 @@ impl Completions { variant: hir::Variant, local_name: Option, ) { - if !ctx.check_stability(Some(&variant.attrs(ctx.db))) { + if !ctx.check_stability_and_hidden(variant) { return; } if let PathCompletionCtx { kind: PathKind::Pat { pat_ctx }, .. } = path_ctx { @@ -479,9 +452,6 @@ impl Completions { field: hir::Field, ty: &hir::Type, ) { - if !ctx.check_stability(Some(&field.attrs(ctx.db))) { - return; - } let is_private_editable = match ctx.is_visible(&field) { Visible::Yes => false, Visible::Editable => true, @@ -506,12 +476,18 @@ impl Completions { path: Option, local_name: Option, ) { - if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) { - return; - } - if let Some(builder) = - render_struct_literal(RenderContext::new(ctx), path_ctx, strukt, path, local_name) - { + let is_private_editable = match ctx.is_visible(&strukt) { + Visible::Yes => false, + Visible::Editable => true, + Visible::No => return, + }; + if let Some(builder) = render_struct_literal( + RenderContext::new(ctx).private_editable(is_private_editable), + path_ctx, + strukt, + path, + local_name, + ) { self.add(builder.build(ctx.db)); } } @@ -523,10 +499,17 @@ impl Completions { path: Option, local_name: Option, ) { - if !ctx.check_stability(Some(&un.attrs(ctx.db))) { - return; - } - let item = render_union_literal(RenderContext::new(ctx), un, path, local_name); + let is_private_editable = match ctx.is_visible(&un) { + Visible::Yes => false, + Visible::Editable => true, + Visible::No => return, + }; + let item = render_union_literal( + RenderContext::new(ctx).private_editable(is_private_editable), + un, + path, + local_name, + ); self.add_opt(item); } @@ -571,7 +554,7 @@ impl Completions { variant: hir::Variant, local_name: Option, ) { - if !ctx.check_stability(Some(&variant.attrs(ctx.db))) { + if !ctx.check_stability_and_hidden(variant) { return; } self.add_opt(render_variant_pat( @@ -591,7 +574,7 @@ impl Completions { variant: hir::Variant, path: hir::ModPath, ) { - if !ctx.check_stability(Some(&variant.attrs(ctx.db))) { + if !ctx.check_stability_and_hidden(variant) { return; } let path = Some(&path); @@ -612,10 +595,17 @@ impl Completions { strukt: hir::Struct, local_name: Option, ) { - if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) { - return; - } - self.add_opt(render_struct_pat(RenderContext::new(ctx), pattern_ctx, strukt, local_name)); + let is_private_editable = match ctx.is_visible(&strukt) { + Visible::Yes => false, + Visible::Editable => true, + Visible::No => return, + }; + self.add_opt(render_struct_pat( + RenderContext::new(ctx).private_editable(is_private_editable), + pattern_ctx, + strukt, + local_name, + )); } pub(crate) fn suggest_name(&mut self, ctx: &CompletionContext<'_>, name: &str) { @@ -660,7 +650,7 @@ fn enum_variants_with_paths( if let Some(path) = ctx.module.find_path( ctx.db, hir::ModuleDef::from(variant), - ctx.config.import_path_config(), + ctx.config.import_path_config(ctx.is_nightly), ) { // Variants with trivial paths are already added by the existing completion logic, // so we should avoid adding these twice diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index 7679d9076ded2..d12654665ce95 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -42,31 +42,38 @@ pub(crate) fn complete_dot( item.detail("expr.await"); item.add_to(acc, ctx.db); - // Completions that skip `.await`, e.g. `.await.foo()`. - let dot_access_kind = match &dot_access.kind { - DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => { - DotAccessKind::Field { receiver_is_ambiguous_float_literal: false } - } - it @ DotAccessKind::Method { .. } => *it, - }; - let dot_access = DotAccess { - receiver: dot_access.receiver.clone(), - receiver_ty: Some(hir::TypeInfo { original: future_output.clone(), adjusted: None }), - kind: dot_access_kind, - ctx: dot_access.ctx, - }; - complete_fields( - acc, - ctx, - &future_output, - |acc, field, ty| acc.add_field(ctx, &dot_access, Some(await_str.clone()), field, &ty), - |acc, field, ty| acc.add_tuple_field(ctx, Some(await_str.clone()), field, &ty), - is_field_access, - is_method_access_with_parens, - ); - complete_methods(ctx, &future_output, &traits_in_scope, |func| { - acc.add_method(ctx, &dot_access, func, Some(await_str.clone()), None) - }); + if ctx.config.enable_auto_await { + // Completions that skip `.await`, e.g. `.await.foo()`. + let dot_access_kind = match &dot_access.kind { + DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => { + DotAccessKind::Field { receiver_is_ambiguous_float_literal: false } + } + it @ DotAccessKind::Method { .. } => *it, + }; + let dot_access = DotAccess { + receiver: dot_access.receiver.clone(), + receiver_ty: Some(hir::TypeInfo { + original: future_output.clone(), + adjusted: None, + }), + kind: dot_access_kind, + ctx: dot_access.ctx, + }; + complete_fields( + acc, + ctx, + &future_output, + |acc, field, ty| { + acc.add_field(ctx, &dot_access, Some(await_str.clone()), field, &ty) + }, + |acc, field, ty| acc.add_tuple_field(ctx, Some(await_str.clone()), field, &ty), + is_field_access, + is_method_access_with_parens, + ); + complete_methods(ctx, &future_output, &traits_in_scope, |func| { + acc.add_method(ctx, &dot_access, func, Some(await_str.clone()), None) + }); + } } complete_fields( @@ -82,39 +89,41 @@ pub(crate) fn complete_dot( acc.add_method(ctx, dot_access, func, None, None) }); - // FIXME: - // Checking for the existence of `iter()` is complicated in our setup, because we need to substitute - // its return type, so we instead check for `<&Self as IntoIterator>::IntoIter`. - // Does <&receiver_ty as IntoIterator>::IntoIter` exist? Assume `iter` is valid - let iter = receiver_ty - .strip_references() - .add_reference(hir::Mutability::Shared) - .into_iterator_iter(ctx.db) - .map(|ty| (ty, SmolStr::new_static("iter()"))); - // Does ::IntoIter` exist? - let into_iter = || { - receiver_ty - .clone() + if ctx.config.enable_auto_iter { + // FIXME: + // Checking for the existence of `iter()` is complicated in our setup, because we need to substitute + // its return type, so we instead check for `<&Self as IntoIterator>::IntoIter`. + // Does <&receiver_ty as IntoIterator>::IntoIter` exist? Assume `iter` is valid + let iter = receiver_ty + .strip_references() + .add_reference(hir::Mutability::Shared) .into_iterator_iter(ctx.db) - .map(|ty| (ty, SmolStr::new_static("into_iter()"))) - }; - if let Some((iter, iter_sym)) = iter.or_else(into_iter) { - // Skip iterators, e.g. complete `.iter().filter_map()`. - let dot_access_kind = match &dot_access.kind { - DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => { - DotAccessKind::Field { receiver_is_ambiguous_float_literal: false } - } - it @ DotAccessKind::Method { .. } => *it, + .map(|ty| (ty, SmolStr::new_static("iter()"))); + // Does ::IntoIter` exist? + let into_iter = || { + receiver_ty + .clone() + .into_iterator_iter(ctx.db) + .map(|ty| (ty, SmolStr::new_static("into_iter()"))) }; - let dot_access = DotAccess { - receiver: dot_access.receiver.clone(), - receiver_ty: Some(hir::TypeInfo { original: iter.clone(), adjusted: None }), - kind: dot_access_kind, - ctx: dot_access.ctx, - }; - complete_methods(ctx, &iter, &traits_in_scope, |func| { - acc.add_method(ctx, &dot_access, func, Some(iter_sym.clone()), None) - }); + if let Some((iter, iter_sym)) = iter.or_else(into_iter) { + // Skip iterators, e.g. complete `.iter().filter_map()`. + let dot_access_kind = match &dot_access.kind { + DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => { + DotAccessKind::Field { receiver_is_ambiguous_float_literal: false } + } + it @ DotAccessKind::Method { .. } => *it, + }; + let dot_access = DotAccess { + receiver: dot_access.receiver.clone(), + receiver_ty: Some(hir::TypeInfo { original: iter.clone(), adjusted: None }), + kind: dot_access_kind, + ctx: dot_access.ctx, + }; + complete_methods(ctx, &iter, &traits_in_scope, |func| { + acc.add_method(ctx, &dot_access, func, Some(iter_sym.clone()), None) + }); + } } } @@ -1466,4 +1475,34 @@ async fn bar() { "#, ); } + + #[test] + fn receiver_without_deref_impl_completion() { + check_no_kw( + r#" +//- minicore: receiver +use core::ops::Receiver; + +struct Foo; + +impl Foo { + fn foo(self: Bar) {} +} + +struct Bar; + +impl Receiver for Bar { + type Target = Foo; +} + +fn main() { + let bar = Bar; + bar.$0 +} +"#, + expect![[r#" + me foo() fn(self: Bar) +"#]], + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index db18b531d7c3c..e710175170199 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -247,7 +247,7 @@ pub(crate) fn complete_expr_path( .find_path( ctx.db, hir::ModuleDef::from(strukt), - ctx.config.import_path_config(), + ctx.config.import_path_config(ctx.is_nightly), ) .filter(|it| it.len() > 1); @@ -269,7 +269,7 @@ pub(crate) fn complete_expr_path( .find_path( ctx.db, hir::ModuleDef::from(un), - ctx.config.import_path_config(), + ctx.config.import_path_config(ctx.is_nightly), ) .filter(|it| it.len() > 1); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index 73313eeaa6b79..24243f57b46a0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs @@ -5,7 +5,7 @@ use ide_db::imports::{ insert_use::ImportScope, }; use itertools::Itertools; -use syntax::{ast, AstNode, SyntaxNode, ToSmolStr}; +use syntax::{ast, AstNode, SyntaxNode}; use crate::{ config::AutoImportExclusionType, @@ -257,7 +257,7 @@ fn import_on_the_fly( }; let user_input_lowercased = potential_import_name.to_lowercase(); - let import_cfg = ctx.config.import_path_config(); + let import_cfg = ctx.config.import_path_config(ctx.is_nightly); import_assets .search_for_imports(&ctx.sema, import_cfg, ctx.config.insert_use.prefix_kind) @@ -316,7 +316,7 @@ fn import_on_the_fly_pat_( ItemInNs::Values(def) => matches!(def, hir::ModuleDef::Const(_)), }; let user_input_lowercased = potential_import_name.to_lowercase(); - let cfg = ctx.config.import_path_config(); + let cfg = ctx.config.import_path_config(ctx.is_nightly); import_assets .search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind) @@ -358,7 +358,7 @@ fn import_on_the_fly_method( let user_input_lowercased = potential_import_name.to_lowercase(); - let cfg = ctx.config.import_path_config(); + let cfg = ctx.config.import_path_config(ctx.is_nightly); import_assets .search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind) @@ -444,7 +444,7 @@ fn compute_fuzzy_completion_order_key( cov_mark::hit!(certain_fuzzy_order_test); let import_name = match proposed_mod_path.segments().last() { // FIXME: nasty alloc, this is a hot path! - Some(name) => name.unescaped().display_no_db().to_smolstr().to_ascii_lowercase(), + Some(name) => name.as_str().to_ascii_lowercase(), None => return usize::MAX, }; match import_name.match_indices(user_input_lowercased).next() { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index 6d1945c45341d..831f5665f4aa0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -31,7 +31,7 @@ //! } //! ``` -use hir::{db::ExpandDatabase, HasAttrs, MacroFileId, Name}; +use hir::{db::ExpandDatabase, MacroFileId, Name}; use ide_db::text_edit::TextEdit; use ide_db::{ documentation::HasDocs, path_transform::PathTransform, @@ -85,7 +85,7 @@ fn complete_trait_impl_name( name: &Option, kind: ImplCompletionKind, ) -> Option<()> { - let item = match name { + let macro_file_item = match name { Some(name) => name.syntax().parent(), None => { let token = &ctx.token; @@ -96,12 +96,12 @@ fn complete_trait_impl_name( .parent() } }?; - let item = ctx.sema.original_syntax_node_rooted(&item)?; + let real_file_item = ctx.sema.original_syntax_node_rooted(¯o_file_item)?; // item -> ASSOC_ITEM_LIST -> IMPL - let impl_def = ast::Impl::cast(item.parent()?.parent()?)?; + let impl_def = ast::Impl::cast(macro_file_item.parent()?.parent()?)?; let replacement_range = { // ctx.sema.original_ast_node(item)?; - let first_child = item + let first_child = real_file_item .children_with_tokens() .find(|child| { !matches!( @@ -109,7 +109,7 @@ fn complete_trait_impl_name( SyntaxKind::COMMENT | SyntaxKind::WHITESPACE | SyntaxKind::ATTR ) }) - .unwrap_or_else(|| SyntaxElement::Node(item.clone())); + .unwrap_or_else(|| SyntaxElement::Node(real_file_item.clone())); TextRange::new(first_child.text_range().start(), ctx.source_range().end()) }; @@ -133,8 +133,11 @@ pub(crate) fn complete_trait_impl_item_by_name( acc, ctx, ImplCompletionKind::All, - match name_ref { - Some(name) => name.syntax().text_range(), + match name_ref + .as_ref() + .and_then(|name| ctx.sema.original_syntax_node_rooted(name.syntax())) + { + Some(name) => name.text_range(), None => ctx.source_range(), }, impl_, @@ -152,7 +155,7 @@ fn complete_trait_impl( if let Some(hir_impl) = ctx.sema.to_def(impl_def) { get_missing_assoc_items(&ctx.sema, impl_def) .into_iter() - .filter(|item| ctx.check_stability(Some(&item.attrs(ctx.db)))) + .filter(|item| ctx.check_stability_and_hidden(*item)) .for_each(|item| { use self::ImplCompletionKind::*; match (item, kind) { @@ -359,7 +362,7 @@ fn add_type_alias_impl( type_alias: hir::TypeAlias, impl_def: hir::Impl, ) { - let alias_name = type_alias.name(ctx.db).unescaped().display(ctx.db).to_smolstr(); + let alias_name = type_alias.name(ctx.db).as_str().to_smolstr(); let label = format_smolstr!("type {alias_name} ="); @@ -516,7 +519,7 @@ fn function_declaration( mod tests { use expect_test::expect; - use crate::tests::{check_edit, check_no_kw}; + use crate::tests::{check, check_edit, check_no_kw}; #[test] fn no_completion_inside_fn() { @@ -1639,4 +1642,51 @@ impl DesugaredAsyncTrait for () { "#, ); } + + #[test] + fn within_attr_macro() { + check( + r#" +//- proc_macros: identity +trait Trait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +#[proc_macros::identity] +impl Trait for () { + f$0 +} + "#, + expect![[r#" + me fn bar(..) + me fn baz(..) + me fn foo(..) + md proc_macros + kw crate:: + kw self:: + "#]], + ); + check( + r#" +//- proc_macros: identity +trait Trait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +#[proc_macros::identity] +impl Trait for () { + fn $0 +} + "#, + expect![[r#" + me fn bar(..) + me fn baz(..) + me fn foo(..) + "#]], + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs index bafe32942098c..cca6a22f290d2 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs @@ -7,7 +7,7 @@ use ide_db::{ base_db::{SourceRootDatabase, VfsPath}, FxHashSet, RootDatabase, SymbolKind, }; -use syntax::{ast, AstNode, SyntaxKind, ToSmolStr}; +use syntax::{ast, AstNode, SyntaxKind}; use crate::{context::CompletionContext, CompletionItem, Completions}; @@ -140,9 +140,7 @@ fn directory_to_look_for_submodules( module_chain_to_containing_module_file(module, db) .into_iter() .filter_map(|module| module.name(db)) - .try_fold(base_directory, |path, name| { - path.join(&name.unescaped().display_no_db().to_smolstr()) - }) + .try_fold(base_directory, |path, name| path.join(name.as_str())) } fn module_chain_to_containing_module_file( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index 67ea05e002b73..2c39a8fdfed73 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs @@ -60,7 +60,7 @@ pub(crate) fn complete_postfix( None => return, }; - let cfg = ctx.config.import_path_config(); + let cfg = ctx.config.import_path_config(ctx.is_nightly); if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() { if receiver_ty.impls_trait(ctx.db, drop_trait, &[]) { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs index 9d62622add206..b384987c51ce1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs @@ -52,8 +52,14 @@ pub(crate) fn complete_use_path( ) }; for (name, def) in module_scope { - if !ctx.check_stability(def.attrs(ctx.db).as_deref()) { - continue; + if let (Some(attrs), Some(defining_crate)) = + (def.attrs(ctx.db), def.krate(ctx.db)) + { + if !ctx.check_stability(Some(&attrs)) + || ctx.is_doc_hidden(&attrs, defining_crate) + { + continue; + } } let is_name_already_imported = already_imported_names.contains(name.as_str()); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs index 8b1ce11e8a45f..45aab38e8ea09 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs @@ -14,6 +14,8 @@ pub struct CompletionConfig<'a> { pub enable_postfix_completions: bool, pub enable_imports_on_the_fly: bool, pub enable_self_on_the_fly: bool, + pub enable_auto_iter: bool, + pub enable_auto_await: bool, pub enable_private_editable: bool, pub enable_term_search: bool, pub term_search_fuel: u64, @@ -57,11 +59,12 @@ impl CompletionConfig<'_> { .flat_map(|snip| snip.prefix_triggers.iter().map(move |trigger| (&**trigger, snip))) } - pub fn import_path_config(&self) -> ImportPathConfig { + pub fn import_path_config(&self, allow_unstable: bool) -> ImportPathConfig { ImportPathConfig { prefer_no_std: self.prefer_no_std, prefer_prelude: self.prefer_prelude, prefer_absolute: self.prefer_absolute, + allow_unstable, } } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index 3a2a4a23a1987..2f1860cbb59af 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -443,7 +443,9 @@ pub(crate) struct CompletionContext<'a> { /// The module of the `scope`. pub(crate) module: hir::Module, /// Whether nightly toolchain is used. Cached since this is looked up a lot. - is_nightly: bool, + pub(crate) is_nightly: bool, + /// The edition of the current crate + // FIXME: This should probably be the crate of the current token? pub(crate) edition: Edition, /// The expected name of what we are completing. @@ -532,7 +534,7 @@ impl CompletionContext<'_> { } } - /// Checks if an item is visible and not `doc(hidden)` at the completion site. + /// Checks if an item is visible, not `doc(hidden)` and stable at the completion site. pub(crate) fn is_visible(&self, item: &I) -> Visible where I: hir::HasVisibility + hir::HasAttrs + hir::HasCrate + Copy, @@ -568,6 +570,15 @@ impl CompletionContext<'_> { !attrs.is_unstable() || self.is_nightly } + pub(crate) fn check_stability_and_hidden(&self, item: I) -> bool + where + I: hir::HasAttrs + hir::HasCrate, + { + let defining_crate = item.krate(self.db); + let attrs = item.attrs(self.db); + self.check_stability(Some(&attrs)) && !self.is_doc_hidden(&attrs, defining_crate) + } + /// Whether the given trait is an operator trait or not. pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool { match trait_.attrs(self.db).lang() { @@ -645,6 +656,10 @@ impl CompletionContext<'_> { attrs: &hir::Attrs, defining_crate: hir::Crate, ) -> Visible { + if !self.check_stability(Some(attrs)) { + return Visible::No; + } + if !vis.is_visible_from(self.db, self.module.into()) { if !self.config.enable_private_editable { return Visible::No; @@ -664,7 +679,7 @@ impl CompletionContext<'_> { } } - fn is_doc_hidden(&self, attrs: &hir::Attrs, defining_crate: hir::Crate) -> bool { + pub(crate) fn is_doc_hidden(&self, attrs: &hir::Attrs, defining_crate: hir::Crate) -> bool { // `doc(hidden)` items are only completed within the defining crate. self.krate != defining_crate && attrs.has_doc_hidden() } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 3c4d489c0ff88..f5a50ae81907f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -5,7 +5,7 @@ use hir::{ExpandResult, Semantics, Type, TypeInfo, Variant}; use ide_db::{active_parameter::ActiveParameter, RootDatabase}; use itertools::Either; use syntax::{ - algo::{ancestors_at_offset, find_node_at_offset, non_trivia_sibling}, + algo::{self, ancestors_at_offset, find_node_at_offset, non_trivia_sibling}, ast::{ self, AttrKind, HasArgList, HasGenericArgs, HasGenericParams, HasLoopBody, HasName, NameOrNameRef, @@ -85,6 +85,11 @@ pub(super) fn expand_and_analyze( }) } +fn token_at_offset_ignore_whitespace(file: &SyntaxNode, offset: TextSize) -> Option { + let token = file.token_at_offset(offset).left_biased()?; + algo::skip_whitespace_token(token, Direction::Prev) +} + /// Expand attributes and macro calls at the current cursor position for both the original file /// and fake file repeatedly. As soon as one of the two expansions fail we stop so the original /// and speculative states stay in sync. @@ -125,9 +130,7 @@ fn expand( // Left biased since there may already be an identifier token there, and we appended to it. if !sema.might_be_inside_macro_call(&fake_ident_token) - && original_file - .token_at_offset(original_offset + relative_offset) - .left_biased() + && token_at_offset_ignore_whitespace(&original_file, original_offset + relative_offset) .is_some_and(|original_token| !sema.might_be_inside_macro_call(&original_token)) { // Recursion base case. @@ -143,9 +146,11 @@ fn expand( let parent_item = |item: &ast::Item| item.syntax().ancestors().skip(1).find_map(ast::Item::cast); + let original_node = token_at_offset_ignore_whitespace(&original_file, original_offset) + .and_then(|token| token.parent_ancestors().find_map(ast::Item::cast)); let ancestor_items = iter::successors( Option::zip( - find_node_at_offset::(&original_file, original_offset), + original_node, find_node_at_offset::( &speculative_file, fake_ident_token.text_range().start(), @@ -1590,11 +1595,11 @@ fn pattern_context_for( }).map(|enum_| enum_.variants(sema.db)) }) }).map(|variants| variants.iter().filter_map(|variant| { - let variant_name = variant.name(sema.db).unescaped().display(sema.db).to_string(); + let variant_name = variant.name(sema.db); let variant_already_present = match_arm_list.arms().any(|arm| { arm.pat().and_then(|pat| { - let pat_already_present = pat.syntax().to_string().contains(&variant_name); + let pat_already_present = pat.syntax().to_string().contains(variant_name.as_str()); pat_already_present.then_some(pat_already_present) }).is_some() }); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index dc2f9a7680293..41a82409597bd 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -82,8 +82,7 @@ pub struct CompletionItem { pub ref_match: Option<(CompletionItemRefMode, TextSize)>, /// The import data to add to completion's edits. - /// (ImportPath, LastSegment) - pub import_to_add: SmallVec<[(String, String); 1]>, + pub import_to_add: SmallVec<[String; 1]>, } #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] @@ -181,6 +180,8 @@ pub struct CompletionRelevance { pub postfix_match: Option, /// This is set for items that are function (associated or method) pub function: Option, + /// true when there is an `await.method()` or `iter().method()` completion. + pub is_skipping_completion: bool, } #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct CompletionRelevanceTraitInfo { @@ -269,6 +270,7 @@ impl CompletionRelevance { postfix_match, trait_, function, + is_skipping_completion, } = self; // only applicable for completions within use items @@ -296,6 +298,12 @@ impl CompletionRelevance { score -= 5; } } + + // Lower rank for completions that skip `await` and `iter()`. + if is_skipping_completion { + score -= 7; + } + // lower rank for items that need an import if requires_import { score -= 1; @@ -561,12 +569,7 @@ impl Builder { let import_to_add = self .imports_to_add .into_iter() - .filter_map(|import| { - Some(( - import.import_path.display(db, self.edition).to_string(), - import.import_path.segments().last()?.display(db, self.edition).to_string(), - )) - }) + .map(|import| import.import_path.display(db, self.edition).to_string()) .collect(); CompletionItem { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index 56d7eeaf8ea03..8051d48ca5fe0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -10,17 +10,13 @@ mod snippet; #[cfg(test)] mod tests; -use ide_db::text_edit::TextEdit; use ide_db::{ - helpers::mod_path_to_ast, - imports::{ - import_assets::NameToImport, - insert_use::{self, ImportScope}, - }, - items_locator, + imports::insert_use::{self, ImportScope}, syntax_helpers::tree_diff::diff, + text_edit::TextEdit, FilePosition, FxHashSet, RootDatabase, }; +use syntax::ast::make; use crate::{ completions::Completions, @@ -272,7 +268,7 @@ pub fn resolve_completion_edits( db: &RootDatabase, config: &CompletionConfig<'_>, FilePosition { file_id, offset }: FilePosition, - imports: impl IntoIterator, + imports: impl IntoIterator, ) -> Option> { let _p = tracing::info_span!("resolve_completion_edits").entered(); let sema = hir::Semantics::new(db); @@ -289,27 +285,12 @@ pub fn resolve_completion_edits( let new_ast = scope.clone_for_update(); let mut import_insert = TextEdit::builder(); - let cfg = config.import_path_config(); - - imports.into_iter().for_each(|(full_import_path, imported_name)| { - let items_with_name = items_locator::items_with_name( - &sema, - current_crate, - NameToImport::exact_case_sensitive(imported_name), - items_locator::AssocSearchMode::Include, + imports.into_iter().for_each(|full_import_path| { + insert_use::insert_use( + &new_ast, + make::path_from_text_with_edition(&full_import_path, current_edition), + &config.insert_use, ); - let import = items_with_name - .filter_map(|candidate| { - current_module.find_use_path(db, candidate, config.insert_use.prefix_kind, cfg) - }) - .find(|mod_path| mod_path.display(db, current_edition).to_string() == full_import_path); - if let Some(import_path) = import { - insert_use::insert_use( - &new_ast, - mod_path_to_ast(&import_path, current_edition), - &config.insert_use, - ); - } }); diff(scope.as_syntax_node(), new_ast.as_syntax_node()).into_text_edit(&mut import_insert); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 61e8114d381aa..dc7eacbfbafdb 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -130,10 +130,8 @@ pub(crate) fn render_field( let db = ctx.db(); let is_deprecated = ctx.is_deprecated(field); let name = field.name(db); - let (name, escaped_name) = ( - name.unescaped().display(db).to_smolstr(), - name.display_no_db(ctx.completion.edition).to_smolstr(), - ); + let (name, escaped_name) = + (name.as_str().to_smolstr(), name.display_no_db(ctx.completion.edition).to_smolstr()); let mut item = CompletionItem::new( SymbolKind::Field, ctx.source_range(), @@ -142,7 +140,8 @@ pub(crate) fn render_field( ); item.set_relevance(CompletionRelevance { type_match: compute_type_match(ctx.completion, ty), - exact_name_match: compute_exact_name_match(ctx.completion, name.as_str()), + exact_name_match: compute_exact_name_match(ctx.completion, &name), + is_skipping_completion: receiver.is_some(), ..CompletionRelevance::default() }); item.detail(ty.display(db, ctx.completion.edition).to_string()) @@ -215,6 +214,10 @@ pub(crate) fn render_tuple_field( ); item.detail(ty.display(ctx.db(), ctx.completion.edition).to_string()) .lookup_by(field.to_string()); + item.set_relevance(CompletionRelevance { + is_skipping_completion: receiver.is_some(), + ..ctx.completion_relevance() + }); item.build(ctx.db()) } @@ -298,7 +301,7 @@ pub(crate) fn render_expr( .unwrap_or_else(|| String::from("...")) }; - let cfg = ctx.config.import_path_config(); + let cfg = ctx.config.import_path_config(ctx.is_nightly); let label = expr.gen_source_code(&ctx.scope, &mut label_formatter, cfg, ctx.edition).ok()?; @@ -512,7 +515,7 @@ fn render_resolution_simple_( let mut item = CompletionItem::new( kind, ctx.source_range(), - local_name.unescaped().display(db).to_smolstr(), + local_name.as_str().to_smolstr(), ctx.completion.edition, ); item.set_relevance(ctx.completion_relevance()) @@ -1335,6 +1338,7 @@ fn main() { let _: m::Spam = S$0 } is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, trigger_call_info: true, }, @@ -1364,6 +1368,7 @@ fn main() { let _: m::Spam = S$0 } is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, trigger_call_info: true, }, @@ -1453,6 +1458,7 @@ fn foo() { A { the$0 } } is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, }, ] @@ -1511,6 +1517,7 @@ impl S { return_type: Other, }, ), + is_skipping_completion: false, }, }, CompletionItem { @@ -1653,6 +1660,7 @@ fn foo(s: S) { s.$0 } return_type: Other, }, ), + is_skipping_completion: false, }, }, ] @@ -1864,6 +1872,7 @@ fn f() -> i32 { is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, }, ] @@ -2624,6 +2633,7 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 } return_type: Other, }, ), + is_skipping_completion: false, }, ref_match: "&@107", }, @@ -2709,6 +2719,7 @@ fn foo() { is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, }, ] @@ -2766,6 +2777,7 @@ fn main() { return_type: Other, }, ), + is_skipping_completion: false, }, ref_match: "&@92", }, @@ -3140,6 +3152,7 @@ fn main() { is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, }, CompletionItem { @@ -3173,6 +3186,7 @@ fn main() { is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, }, ] diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs index 415d87c6239b4..e357ab24d22df 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs @@ -14,10 +14,8 @@ pub(crate) fn render_const(ctx: RenderContext<'_>, const_: hir::Const) -> Option fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option { let db = ctx.db(); let name = const_.name(db)?; - let (name, escaped_name) = ( - name.unescaped().display(db).to_smolstr(), - name.display(db, ctx.completion.edition).to_smolstr(), - ); + let (name, escaped_name) = + (name.as_str().to_smolstr(), name.display(db, ctx.completion.edition).to_smolstr()); let detail = const_.display(db, ctx.completion.edition).to_string(); let mut item = diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index 3b97d67169eca..c3354902c3b78 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -59,13 +59,10 @@ fn render( let (call, escaped_call) = match &func_kind { FuncKind::Method(_, Some(receiver)) => ( - format_smolstr!("{}.{}", receiver, name.unescaped().display(ctx.db())), + format_smolstr!("{}.{}", receiver, name.as_str()), format_smolstr!("{}.{}", receiver, name.display(ctx.db(), completion.edition)), ), - _ => ( - name.unescaped().display(db).to_smolstr(), - name.display(db, completion.edition).to_smolstr(), - ), + _ => (name.as_str().to_smolstr(), name.display(db, completion.edition).to_smolstr()), }; let has_self_param = func.self_param(db).is_some(); let mut item = CompletionItem::new( @@ -126,6 +123,7 @@ fn render( exact_name_match: compute_exact_name_match(completion, &call), function, trait_: trait_info, + is_skipping_completion: matches!(func_kind, FuncKind::Method(_, Some(_))), ..ctx.completion_relevance() }); @@ -151,7 +149,7 @@ fn render( item.set_documentation(ctx.docs(func)) .set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func)) .detail(detail) - .lookup_by(name.unescaped().display(db).to_smolstr()); + .lookup_by(name.as_str().to_smolstr()); if let Some((cap, (self_param, params))) = complete_call_parens { add_call_parens( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs index c71356e5300fc..aab54ca5e0146 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs @@ -75,7 +75,7 @@ fn render( None => (name.clone().into(), name.into(), false), }; let (qualified_name, escaped_qualified_name) = ( - qualified_name.unescaped().display(ctx.db()).to_string(), + qualified_name.display_verbatim(ctx.db()).to_string(), qualified_name.display(ctx.db(), completion.edition).to_string(), ); let snippet_cap = ctx.snippet_cap(); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs index 6490171fbb48b..e265e92f9794b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs @@ -46,21 +46,19 @@ fn render( ctx.source_range() }; - let (name, escaped_name) = ( - name.unescaped().display(ctx.db()).to_smolstr(), - name.display(ctx.db(), completion.edition).to_smolstr(), - ); + let (name, escaped_name) = + (name.as_str(), name.display(ctx.db(), completion.edition).to_smolstr()); let docs = ctx.docs(macro_); let docs_str = docs.as_ref().map(Documentation::as_str).unwrap_or_default(); let is_fn_like = macro_.is_fn_like(completion.db); - let (bra, ket) = if is_fn_like { guess_macro_braces(&name, docs_str) } else { ("", "") }; + let (bra, ket) = if is_fn_like { guess_macro_braces(name, docs_str) } else { ("", "") }; let needs_bang = is_fn_like && !is_use_path && !has_macro_bang; let mut item = CompletionItem::new( SymbolKind::from(macro_.kind(completion.db)), source_range, - label(&ctx, needs_bang, bra, ket, &name), + label(&ctx, needs_bang, bra, ket, &name.to_smolstr()), completion.edition, ); item.set_deprecated(ctx.is_deprecated(macro_)) @@ -71,11 +69,11 @@ fn render( match ctx.snippet_cap() { Some(cap) if needs_bang && !has_call_parens => { let snippet = format!("{escaped_name}!{bra}$0{ket}"); - let lookup = banged_name(&name); + let lookup = banged_name(name); item.insert_snippet(cap, snippet).lookup_by(lookup); } _ if needs_bang => { - item.insert_text(banged_name(&escaped_name)).lookup_by(banged_name(&name)); + item.insert_text(banged_name(&escaped_name)).lookup_by(banged_name(name)); } _ => { cov_mark::hit!(dont_insert_macro_call_parens_unnecessary); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs index 5675dfb5c6ff2..124abb17b6a1c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs @@ -31,13 +31,11 @@ pub(crate) fn render_struct_pat( } let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())); - let (name, escaped_name) = ( - name.unescaped().display(ctx.db()).to_smolstr(), - name.display(ctx.db(), ctx.completion.edition).to_smolstr(), - ); + let (name, escaped_name) = + (name.as_str(), name.display(ctx.db(), ctx.completion.edition).to_smolstr()); let kind = strukt.kind(ctx.db()); - let label = format_literal_label(name.as_str(), kind, ctx.snippet_cap()); - let lookup = format_literal_lookup(name.as_str(), kind); + let label = format_literal_label(name, kind, ctx.snippet_cap()); + let lookup = format_literal_lookup(name, kind); let pat = render_pat(&ctx, pattern_ctx, &escaped_name, kind, &visible_fields, fields_omitted)?; let db = ctx.db(); @@ -61,13 +59,13 @@ pub(crate) fn render_variant_pat( let (name, escaped_name) = match path { Some(path) => ( - path.unescaped().display(ctx.db()).to_string().into(), - path.display(ctx.db(), ctx.completion.edition).to_string().into(), + path.display_verbatim(ctx.db()).to_smolstr(), + path.display(ctx.db(), ctx.completion.edition).to_smolstr(), ), None => { let name = local_name.unwrap_or_else(|| variant.name(ctx.db())); let it = ( - name.unescaped().display(ctx.db()).to_smolstr(), + name.as_str().to_smolstr(), name.display(ctx.db(), ctx.completion.edition).to_smolstr(), ); it diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs index 09eb19201c5b0..1b952f31360c2 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs @@ -32,14 +32,11 @@ fn render( let name = type_alias.name(db); let (name, escaped_name) = if with_eq { ( - SmolStr::from_iter([&name.unescaped().display(db).to_smolstr(), " = "]), + SmolStr::from_iter([&name.as_str().to_smolstr(), " = "]), SmolStr::from_iter([&name.display_no_db(ctx.completion.edition).to_smolstr(), " = "]), ) } else { - ( - name.unescaped().display(db).to_smolstr(), - name.display_no_db(ctx.completion.edition).to_smolstr(), - ) + (name.as_str().to_smolstr(), name.display_no_db(ctx.completion.edition).to_smolstr()) }; let detail = type_alias.display(db, ctx.completion.edition).to_string(); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs index e053e299d903a..742036265211e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs @@ -23,12 +23,12 @@ pub(crate) fn render_union_literal( let (qualified_name, escaped_qualified_name) = match path { Some(p) => ( - p.unescaped().display(ctx.db()).to_string(), - p.display(ctx.db(), ctx.completion.edition).to_string(), + p.display_verbatim(ctx.db()).to_smolstr(), + p.display(ctx.db(), ctx.completion.edition).to_smolstr(), ), None => ( - name.unescaped().display(ctx.db()).to_string(), - name.display(ctx.db(), ctx.completion.edition).to_string(), + name.as_str().to_smolstr(), + name.display(ctx.db(), ctx.completion.edition).to_smolstr(), ), }; let label = format_literal_label( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs index 04bb178c658f3..866b83a614603 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs @@ -164,7 +164,7 @@ impl Snippet { } fn import_edits(ctx: &CompletionContext<'_>, requires: &[ModPath]) -> Option> { - let import_cfg = ctx.config.import_path_config(); + let import_cfg = ctx.config.import_path_config(ctx.is_nightly); let resolve = |import| { let item = ctx.scope.resolve_mod_path(import).next()?; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index b7dbf0a6306c6..9d91f95eb65b8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -87,6 +87,8 @@ pub(crate) const TEST_CONFIG: CompletionConfig<'_> = CompletionConfig { fields_to_resolve: CompletionFieldsToResolve::empty(), exclude_flyimport: vec![], exclude_traits: &[], + enable_auto_await: true, + enable_auto_iter: true, }; pub(crate) fn completion_list(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index e117dbf4bdf0b..663a038580d55 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -1965,3 +1965,24 @@ fn bar() { "#]], ); } + +#[test] +fn doc_hidden_enum_variant() { + check( + r#" +//- /foo.rs crate:foo +pub enum Enum { + #[doc(hidden)] Hidden, + Visible, +} + +//- /lib.rs crate:lib deps:foo +fn foo() { + let _ = foo::Enum::$0; +} + "#, + expect![[r#" + ev Visible Visible + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index d491e438feffc..2e7c53def7fc5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -1390,6 +1390,41 @@ pub struct FooStruct {} ); } +#[test] +fn flyimport_pattern_unstable_path() { + check( + r#" +//- /main.rs crate:main deps:std +fn function() { + let foo$0 +} +//- /std.rs crate:std +#[unstable] +pub mod unstable { + pub struct FooStruct {} +} +"#, + expect![""], + ); + check( + r#" +//- toolchain:nightly +//- /main.rs crate:main deps:std +fn function() { + let foo$0 +} +//- /std.rs crate:std +#[unstable] +pub mod unstable { + pub struct FooStruct {} +} +"#, + expect![[r#" + st FooStruct (use std::unstable::FooStruct) + "#]], + ); +} + #[test] fn flyimport_pattern_unstable_item_on_nightly() { check( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs index 04b3a47a64dab..593b1edde5cc5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs @@ -451,3 +451,20 @@ marco_rules! m { () => {} } "#]], ); } + +#[test] +fn use_tree_doc_hidden() { + check( + r#" +//- /foo.rs crate:foo +#[doc(hidden)] pub struct Hidden; +pub struct Visible; + +//- /lib.rs crate:lib deps:foo +use foo::$0; + "#, + expect![[r#" + st Visible Visible + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index 49d26dfe25c12..d12bda0816fd3 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -794,7 +794,7 @@ impl NameRefClass { hir::AssocItem::TypeAlias(it) => Some(it), _ => None, }) - .find(|alias| alias.name(sema.db).eq_ident(name_ref.text().as_str())) + .find(|alias| alias.name(sema.db).as_str() == name_ref.text().trim_start_matches("r#")) { // No substitution, this can only occur in type position. return Some(NameRefClass::Definition(Definition::TypeAlias(ty), None)); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs index 9e3506d6f53b7..2f4d07446f2c1 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs @@ -46,6 +46,10 @@ impl FamousDefs<'_, '_> { self.find_trait("core:cmp:Ord") } + pub fn core_convert_FromStr(&self) -> Option { + self.find_trait("core:str:FromStr") + } + pub fn core_convert_From(&self) -> Option { self.find_trait("core:convert:From") } @@ -54,6 +58,14 @@ impl FamousDefs<'_, '_> { self.find_trait("core:convert:Into") } + pub fn core_convert_TryFrom(&self) -> Option { + self.find_trait("core:convert:TryFrom") + } + + pub fn core_convert_TryInto(&self) -> Option { + self.find_trait("core:convert:TryInto") + } + pub fn core_convert_Index(&self) -> Option { self.find_trait("core:ops:Index") } @@ -130,6 +142,13 @@ impl FamousDefs<'_, '_> { self.find_macro("core:unimplemented") } + pub fn core_fmt_Display(&self) -> Option { + self.find_trait("core:fmt:Display") + } + + pub fn alloc_string_ToString(&self) -> Option { + self.find_trait("alloc:string:ToString") + } pub fn builtin_crates(&self) -> impl Iterator { IntoIterator::into_iter([ self.std(), @@ -202,14 +221,15 @@ impl FamousDefs<'_, '_> { for segment in path { module = module.children(db).find_map(|child| { let name = child.name(db)?; - if name.eq_ident(segment) { + if name.as_str() == segment { Some(child) } else { None } })?; } - let def = module.scope(db, None).into_iter().find(|(name, _def)| name.eq_ident(trait_))?.1; + let def = + module.scope(db, None).into_iter().find(|(name, _def)| name.as_str() == trait_)?.1; Some(def) } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index a045c22c2dff1..f045e44dd318b 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -319,6 +319,7 @@ impl Ctx<'_> { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }; let found_path = self.target_module.find_path( self.source_scope.db.upcast(), @@ -378,6 +379,7 @@ impl Ctx<'_> { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }; let found_path = self.target_module.find_path(self.source_scope.db.upcast(), def, cfg)?; @@ -417,6 +419,7 @@ impl Ctx<'_> { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }; let found_path = self.target_module.find_path( self.source_scope.db.upcast(), diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs index 42efbd68e33d3..59914bedde434 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs @@ -263,13 +263,12 @@ fn rename_mod( // - Module has submodules defined in separate files let dir_paths = match (is_mod_rs, has_detached_child, module.name(sema.db)) { // Go up one level since the anchor is inside the dir we're trying to rename - (true, _, Some(mod_name)) => Some(( - format!("../{}", mod_name.unescaped().display(sema.db)), - format!("../{new_name}"), - )), + (true, _, Some(mod_name)) => { + Some((format!("../{}", mod_name.as_str()), format!("../{new_name}"))) + } // The anchor is on the same level as target dir (false, true, Some(mod_name)) => { - Some((mod_name.unescaped().display(sema.db).to_string(), new_name.to_owned())) + Some((mod_name.as_str().to_owned(), new_name.to_owned())) } _ => None, }; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index a75aba137be6c..7fc563a424104 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -625,7 +625,7 @@ impl<'a> FindUsages<'a> { let _p = tracing::info_span!("collect_possible_aliases").entered(); let db = sema.db; - let container_name = container.name(db).unescaped().display(db).to_smolstr(); + let container_name = container.name(db).as_str().to_smolstr(); let search_scope = Definition::from(container).search_scope(db); let mut seen = FxHashSet::default(); let mut completed = FxHashSet::default(); @@ -925,12 +925,8 @@ impl<'a> FindUsages<'a> { .or_else(|| ty.as_builtin().map(|builtin| builtin.name())) }) }; - // We need to unescape the name in case it is written without "r#" in earlier - // editions of Rust where it isn't a keyword. - self.def - .name(sema.db) - .or_else(self_kw_refs) - .map(|it| it.unescaped().display(sema.db).to_smolstr()) + // We need to search without the `r#`, hence `as_str` access. + self.def.name(sema.db).or_else(self_kw_refs).map(|it| it.as_str().to_smolstr()) } }; let name = match &name { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index c94644eeb89be..e5ce10a771ef9 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -143,7 +143,7 @@ fn library_symbols(db: &dyn SymbolsDatabase, source_root_id: SourceRootId) -> Ar fn module_symbols(db: &dyn SymbolsDatabase, module: Module) -> Arc { let _p = tracing::info_span!("module_symbols").entered(); - Arc::new(SymbolIndex::new(SymbolCollector::collect_module(db.upcast(), module))) + Arc::new(SymbolIndex::new(SymbolCollector::new_module(db.upcast(), module))) } pub fn crate_symbols(db: &dyn SymbolsDatabase, krate: Crate) -> Box<[Arc]> { @@ -284,13 +284,15 @@ impl SymbolIndex { builder.insert(key, value).unwrap(); } - // FIXME: fst::Map should ideally have a way to shrink the backing buffer without the unwrap dance - let map = fst::Map::new({ - let mut buf = builder.into_inner().unwrap(); - buf.shrink_to_fit(); - buf - }) - .unwrap(); + let map = builder + .into_inner() + .and_then(|mut buf| { + fst::Map::new({ + buf.shrink_to_fit(); + buf + }) + }) + .unwrap(); SymbolIndex { symbols, map } } @@ -491,7 +493,7 @@ pub(self) use crate::Trait as IsThisJustATrait; .modules(&db) .into_iter() .map(|module_id| { - let mut symbols = SymbolCollector::collect_module(&db, module_id); + let mut symbols = SymbolCollector::new_module(&db, module_id); symbols.sort_by_key(|it| it.name.as_str().to_owned()); (module_id, symbols) }) @@ -518,7 +520,7 @@ struct Duplicate; .modules(&db) .into_iter() .map(|module_id| { - let mut symbols = SymbolCollector::collect_module(&db, module_id); + let mut symbols = SymbolCollector::new_module(&db, module_id); symbols.sort_by_key(|it| it.name.as_str().to_owned()); (module_id, symbols) }) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs index 557c95f704b98..0a7141c19b6b5 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs @@ -31,6 +31,12 @@ const USELESS_NAME_PREFIXES: &[&str] = &["from_", "with_", "into_"]; /// `Result` -> `User` const WRAPPER_TYPES: &[&str] = &["Box", "Arc", "Rc", "Option", "Result"]; +/// Generic types replaced by a plural of their first argument. +/// +/// # Examples +/// `Vec` -> "names" +const SEQUENCE_TYPES: &[&str] = &["Vec", "VecDeque", "LinkedList"]; + /// Prefixes to strip from methods names /// /// # Examples @@ -378,6 +384,11 @@ fn name_of_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option Option, db: &RootDatabase, edition: Edition) -> SmolStr { + let items_str = SmolStr::new_static("items"); + let Some(inner_ty) = inner_ty else { + return items_str; + }; + let Some(name) = name_of_type(inner_ty, db, edition) else { + return items_str; + }; + + if name.ends_with(['s', 'x', 'y']) { + // Given a type called e.g. "Boss", "Fox" or "Story", don't try to + // create a plural. + items_str + } else { + SmolStr::new(format!("{name}s")) + } +} + fn trait_name(trait_: &hir::Trait, db: &RootDatabase, edition: Edition) -> Option { let name = trait_.name(db).display(db, edition).to_string(); if USELESS_TRAITS.contains(&name.as_str()) { @@ -897,6 +928,58 @@ fn foo() { $0(bar())$0; } ); } + #[test] + fn vec_value() { + check( + r#" +struct Vec {}; +struct Seed; +fn bar() -> Vec {} +fn foo() { $0(bar())$0; } +"#, + "seeds", + ); + } + + #[test] + fn vec_value_ends_with_s() { + check( + r#" +struct Vec {}; +struct Boss; +fn bar() -> Vec {} +fn foo() { $0(bar())$0; } +"#, + "items", + ); + } + + #[test] + fn vecdeque_value() { + check( + r#" +struct VecDeque {}; +struct Seed; +fn bar() -> VecDeque {} +fn foo() { $0(bar())$0; } +"#, + "seeds", + ); + } + + #[test] + fn slice_value() { + check( + r#" +struct Vec {}; +struct Seed; +fn bar() -> &[Seed] {} +fn foo() { $0(bar())$0; } +"#, + "seeds", + ); + } + #[test] fn ref_call() { check( diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt index 535777dfcbea6..7dce95592b819 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt @@ -1007,6 +1007,39 @@ is_alias: false, is_assoc: false, }, + FileSymbol { + name: "ThisStruct", + def: Adt( + Struct( + Struct { + id: StructId( + 4, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: EditionedFileId( + FileId( + 1, + ), + Edition2021, + ), + ptr: SyntaxNodePtr { + kind: USE_TREE, + range: 85..125, + }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 115..125, + }, + ), + }, + container_name: None, + is_alias: false, + is_assoc: false, + }, ], ), ] diff --git a/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs b/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs index 515bc418cb467..2fdd8358637df 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs @@ -26,7 +26,7 @@ impl TryEnum { _ => return None, }; TryEnum::ALL.iter().find_map(|&var| { - if enum_.name(sema.db).eq_ident(var.type_name()) { + if enum_.name(sema.db).as_str() == var.type_name() { return Some(var); } None diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs index dca889d1a8efe..f22041ebe233b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs @@ -147,6 +147,7 @@ pub(crate) fn json_in_items( prefer_no_std: config.prefer_no_std, prefer_prelude: config.prefer_prelude, prefer_absolute: config.prefer_absolute, + allow_unstable: true, }; if !scope_has("Serialize") { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs index fd1044e51bc23..938b7182bc946 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -128,6 +128,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option, d: &hir::TypedHole) -> Option prefer_no_std: ctx.config.prefer_no_std, prefer_prelude: ctx.config.prefer_prelude, prefer_absolute: ctx.config.prefer_absolute, + allow_unstable: ctx.is_nightly, }, ctx.edition, ) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs index 13591dfb2eebd..f3109b9bb73a2 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs @@ -112,7 +112,7 @@ fn fixes( // shouldn't occur _ => continue 'crates, }; - match current.children.iter().find(|(name, _)| name.eq_ident(seg)) { + match current.children.iter().find(|(name, _)| name.as_str() == seg) { Some((_, &child)) => current = &crate_def_map[child], None => continue 'crates, } @@ -161,7 +161,7 @@ fn fixes( // try finding a parent that has an inline tree from here on let mut current = module; for s in stack.iter().rev() { - match module.children.iter().find(|(name, _)| name.eq_ident(s)) { + match module.children.iter().find(|(name, _)| name.as_str() == s) { Some((_, child)) => { current = &crate_def_map[*child]; } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 1e99d7ad6e68a..50c91a69602c1 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -83,7 +83,7 @@ use either::Either; use hir::{db::ExpandDatabase, diagnostics::AnyDiagnostic, Crate, HirFileId, InFile, Semantics}; use ide_db::{ assists::{Assist, AssistId, AssistKind, AssistResolveStrategy}, - base_db::SourceDatabase, + base_db::{ReleaseChannel, SourceDatabase}, generated::lints::{Lint, LintGroup, CLIPPY_LINT_GROUPS, DEFAULT_LINTS, DEFAULT_LINT_GROUPS}, imports::insert_use::InsertUseConfig, label::Label, @@ -276,6 +276,7 @@ struct DiagnosticsContext<'a> { sema: Semantics<'a, RootDatabase>, resolve: &'a AssistResolveStrategy, edition: Edition, + is_nightly: bool, } impl DiagnosticsContext<'_> { @@ -368,7 +369,11 @@ pub fn semantic_diagnostics( let module = sema.file_to_module_def(file_id); - let ctx = DiagnosticsContext { config, sema, resolve, edition: file_id.edition() }; + let is_nightly = matches!( + module.and_then(|m| db.toolchain_channel(m.krate().into())), + Some(ReleaseChannel::Nightly) | None + ); + let ctx = DiagnosticsContext { config, sema, resolve, edition: file_id.edition(), is_nightly }; let mut diags = Vec::new(); match module { diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs index 4edc3633fbe65..4bead14e31d4d 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs @@ -673,6 +673,7 @@ impl Match { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }; let mod_path = module.find_path(sema.db, module_def, cfg).ok_or_else(|| { match_error!("Failed to render template path `{}` at match location") diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index bc9843f3f35a2..cfd8919730ad2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -413,8 +413,7 @@ fn rewrite_url_link(db: &RootDatabase, def: Definition, target: &str) -> Option< fn mod_path_of_def(db: &RootDatabase, def: Definition) -> Option { def.canonical_module_path(db).map(|it| { let mut path = String::new(); - it.flat_map(|it| it.name(db)) - .for_each(|name| format_to!(path, "{}/", name.unescaped().display(db))); + it.flat_map(|it| it.name(db)).for_each(|name| format_to!(path, "{}/", name.as_str())); path }) } @@ -590,10 +589,10 @@ fn filename_and_frag_for_def( let res = match def { Definition::Adt(adt) => match adt { Adt::Struct(s) => { - format!("struct.{}.html", s.name(db).unescaped().display(db.upcast())) + format!("struct.{}.html", s.name(db).as_str()) } - Adt::Enum(e) => format!("enum.{}.html", e.name(db).unescaped().display(db.upcast())), - Adt::Union(u) => format!("union.{}.html", u.name(db).unescaped().display(db.upcast())), + Adt::Enum(e) => format!("enum.{}.html", e.name(db).as_str()), + Adt::Union(u) => format!("union.{}.html", u.name(db).as_str()), }, Definition::Crate(_) => String::from("index.html"), Definition::Module(m) => match m.name(db) { @@ -603,48 +602,48 @@ fn filename_and_frag_for_def( Some(kw) => { format!("keyword.{}.html", kw) } - None => format!("{}/index.html", name.unescaped().display(db.upcast())), + None => format!("{}/index.html", name.as_str()), } } None => String::from("index.html"), }, Definition::Trait(t) => { - format!("trait.{}.html", t.name(db).unescaped().display(db.upcast())) + format!("trait.{}.html", t.name(db).as_str()) } Definition::TraitAlias(t) => { - format!("traitalias.{}.html", t.name(db).unescaped().display(db.upcast())) + format!("traitalias.{}.html", t.name(db).as_str()) } Definition::TypeAlias(t) => { - format!("type.{}.html", t.name(db).unescaped().display(db.upcast())) + format!("type.{}.html", t.name(db).as_str()) } Definition::BuiltinType(t) => { - format!("primitive.{}.html", t.name().unescaped().display(db.upcast())) + format!("primitive.{}.html", t.name().as_str()) } Definition::Function(f) => { - format!("fn.{}.html", f.name(db).unescaped().display(db.upcast())) + format!("fn.{}.html", f.name(db).as_str()) } Definition::Variant(ev) => { format!( "enum.{}.html#variant.{}", - ev.parent_enum(db).name(db).unescaped().display(db.upcast()), - ev.name(db).unescaped().display(db.upcast()) + ev.parent_enum(db).name(db).as_str(), + ev.name(db).as_str() ) } Definition::Const(c) => { - format!("const.{}.html", c.name(db)?.unescaped().display(db.upcast())) + format!("const.{}.html", c.name(db)?.as_str()) } Definition::Static(s) => { - format!("static.{}.html", s.name(db).unescaped().display(db.upcast())) + format!("static.{}.html", s.name(db).as_str()) } Definition::Macro(mac) => match mac.kind(db) { hir::MacroKind::Declarative | hir::MacroKind::BuiltIn | hir::MacroKind::Attr | hir::MacroKind::ProcMacro => { - format!("macro.{}.html", mac.name(db).unescaped().display(db.upcast())) + format!("macro.{}.html", mac.name(db).as_str()) } hir::MacroKind::Derive => { - format!("derive.{}.html", mac.name(db).unescaped().display(db.upcast())) + format!("derive.{}.html", mac.name(db).as_str()) } }, Definition::Field(field) => { @@ -654,11 +653,7 @@ fn filename_and_frag_for_def( hir::VariantDef::Variant(it) => Definition::Variant(it), }; let (_, file, _) = filename_and_frag_for_def(db, def)?; - return Some(( - def, - file, - Some(format!("structfield.{}", field.name(db).unescaped().display(db.upcast()))), - )); + return Some((def, file, Some(format!("structfield.{}", field.name(db).as_str())))); } Definition::SelfType(impl_) => { let adt = impl_.self_ty(db).as_adt()?.into(); @@ -667,7 +662,7 @@ fn filename_and_frag_for_def( return Some((adt, file, Some(String::from("impl")))); } Definition::ExternCrateDecl(it) => { - format!("{}/index.html", it.name(db).unescaped().display(db.upcast())) + format!("{}/index.html", it.name(db).as_str()) } Definition::Local(_) | Definition::GenericParam(_) @@ -699,16 +694,16 @@ fn get_assoc_item_fragment(db: &dyn HirDatabase, assoc_item: hir::AssocItem) -> // Rustdoc makes this decision based on whether a method 'has defaultness'. // Currently this is only the case for provided trait methods. if is_trait_method && !function.has_body(db) { - format!("tymethod.{}", function.name(db).unescaped().display(db.upcast())) + format!("tymethod.{}", function.name(db).as_str()) } else { - format!("method.{}", function.name(db).unescaped().display(db.upcast())) + format!("method.{}", function.name(db).as_str()) } } AssocItem::Const(constant) => { - format!("associatedconstant.{}", constant.name(db)?.unescaped().display(db.upcast())) + format!("associatedconstant.{}", constant.name(db)?.as_str()) } AssocItem::TypeAlias(ty) => { - format!("associatedtype.{}", ty.name(db).unescaped().display(db.upcast())) + format!("associatedtype.{}", ty.name(db).as_str()) } }) } diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index f804cc3677274..d18732a6b846b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -5,10 +5,14 @@ use crate::{ navigation_target::{self, ToNav}, FilePosition, NavigationTarget, RangeInfo, TryToNav, UpmappingResult, }; -use hir::{AsAssocItem, AssocItem, FileRange, InFile, MacroFileIdExt, ModuleDef, Semantics}; +use hir::{ + sym, AsAssocItem, AssocItem, CallableKind, FileRange, HasCrate, InFile, MacroFileIdExt, + ModuleDef, Semantics, +}; use ide_db::{ base_db::{AnchoredPath, FileLoader, SourceDatabase}, defs::{Definition, IdentClass}, + famous_defs::FamousDefs, helpers::pick_best_token, RootDatabase, SymbolKind, }; @@ -129,15 +133,74 @@ pub(crate) fn goto_definition( Some(RangeInfo::new(original_token.text_range(), navs)) } -// If the token is into(), try_into(), parse(), search the definition of From, TryFrom, FromStr. +// If the token is into(), try_into(), search the definition of From, TryFrom. fn find_definition_for_known_blanket_dual_impls( sema: &Semantics<'_, RootDatabase>, original_token: &SyntaxToken, ) -> Option> { let method_call = ast::MethodCallExpr::cast(original_token.parent()?.parent()?)?; - let target_method = sema.resolve_known_blanket_dual_impls(&method_call)?; + let callable = sema.resolve_method_call_as_callable(&method_call)?; + let CallableKind::Function(f) = callable.kind() else { return None }; + let assoc = f.as_assoc_item(sema.db)?; + + let return_type = callable.return_type(); + let fd = FamousDefs(sema, return_type.krate(sema.db)); + + let t = match assoc.container(sema.db) { + hir::AssocItemContainer::Trait(t) => t, + hir::AssocItemContainer::Impl(impl_) + if impl_.self_ty(sema.db).is_str() && f.name(sema.db) == sym::parse => + { + let t = fd.core_convert_FromStr()?; + let t_f = t.function(sema.db, &sym::from_str)?; + return sema + .resolve_trait_impl_method( + return_type.clone(), + t, + t_f, + [return_type.type_arguments().next()?], + ) + .map(|f| def_to_nav(sema.db, f.into())); + } + hir::AssocItemContainer::Impl(_) => return None, + }; - let def = Definition::from(target_method); + let fn_name = f.name(sema.db); + let f = if fn_name == sym::into && fd.core_convert_Into() == Some(t) { + let dual = fd.core_convert_From()?; + let dual_f = dual.function(sema.db, &sym::from)?; + sema.resolve_trait_impl_method( + return_type.clone(), + dual, + dual_f, + [return_type, callable.receiver_param(sema.db)?.1], + )? + } else if fn_name == sym::try_into && fd.core_convert_TryInto() == Some(t) { + let dual = fd.core_convert_TryFrom()?; + let dual_f = dual.function(sema.db, &sym::try_from)?; + sema.resolve_trait_impl_method( + return_type.clone(), + dual, + dual_f, + // Extract the `T` from `Result` + [return_type.type_arguments().next()?, callable.receiver_param(sema.db)?.1], + )? + } else if fn_name == sym::to_string && fd.alloc_string_ToString() == Some(t) { + let dual = fd.core_fmt_Display()?; + let dual_f = dual.function(sema.db, &sym::fmt)?; + sema.resolve_trait_impl_method( + return_type.clone(), + dual, + dual_f, + [callable.receiver_param(sema.db)?.1.strip_reference()], + )? + } else { + return None; + }; + // Assert that we got a trait impl function, if we are back in a trait definition we didn't + // succeed + let _t = f.as_assoc_item(sema.db)?.implemented_trait(sema.db)?; + let def = Definition::from(f); Some(def_to_nav(sema.db, def)) } @@ -3168,18 +3231,45 @@ fn f() { r#" //- minicore: from, str struct A; - impl FromStr for A { type Error = String; - fn from_str(value: &str) -> Result { //^^^^^^^^ Ok(A) } } - fn f() { let a: Result = "aaaaaa".parse$0(); +} + "#, + ); + } + + #[test] + fn to_string_call_to_display_definition() { + check( + r#" +//- minicore:fmt +//- /alloc.rs crate:alloc +pub mod string { + pub struct String; + pub trait ToString { + fn to_string(&self) -> String; + } + + impl ToString for T { + fn to_string(&self) -> String { String } + } +} +//- /lib.rs crate:lib deps:alloc +use alloc::string::ToString; +struct A; +impl core::fmt::Display for A { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {} + // ^^^ +} +fn f() { + A.to_string$0(); } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 18a3fed07ece1..9d4c103fc2e01 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -346,7 +346,7 @@ fn hover_offset( .unique() .reduce(|mut acc: HoverResult, HoverResult { markup, actions }| { acc.actions.extend(actions); - acc.markup = Markup::from(format!("{}\n---\n{markup}", acc.markup)); + acc.markup = Markup::from(format!("{}\n\n---\n{markup}", acc.markup)); acc }) .map(|mut res: HoverResult| { diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 46242b75dd0b9..40f3406b72d37 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -1082,7 +1082,19 @@ fn render_memory_layout( if config.niches { if let Some(niches) = layout.niches() { - format_to!(label, "niches = {niches}, "); + if niches > 1024 { + if niches.is_power_of_two() { + format_to!(label, "niches = 2{}, ", pwr2_to_exponent(niches)); + } else if is_pwr2plus1(niches) { + format_to!(label, "niches = 2{} + 1, ", pwr2_to_exponent(niches - 1)); + } else if is_pwr2minus1(niches) { + format_to!(label, "niches = 2{} - 1, ", pwr2_to_exponent(niches + 1)); + } else { + format_to!(label, "niches = a lot, "); + } + } else { + format_to!(label, "niches = {niches}, "); + } } } label.pop(); // ' ' @@ -1210,3 +1222,74 @@ fn render_dyn_compatibility( } } } + +fn is_pwr2minus1(val: u128) -> bool { + val == u128::MAX || (val + 1).is_power_of_two() +} + +fn is_pwr2plus1(val: u128) -> bool { + val != 0 && (val - 1).is_power_of_two() +} + +/// Formats a power of two as an exponent of two, i.e. 16 => ⁴. Note that `num` MUST be a power +/// of 2, or this function will panic. +fn pwr2_to_exponent(num: u128) -> String { + const DIGITS: [char; 10] = ['⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹']; + assert_eq!(num.count_ones(), 1); + num.trailing_zeros() + .to_string() + .chars() + .map(|c| c.to_digit(10).unwrap() as usize) + .map(|idx| DIGITS[idx]) + .collect::() +} + +#[cfg(test)] +mod tests { + use super::*; + + const TESTERS: [u128; 10] = [0, 1, 2, 3, 4, 255, 256, 257, u128::MAX - 1, u128::MAX]; + + #[test] + fn test_is_pwr2minus1() { + const OUTCOMES: [bool; 10] = + [true, true, false, true, false, true, false, false, false, true]; + for (test, expected) in TESTERS.iter().zip(OUTCOMES) { + let actual = is_pwr2minus1(*test); + assert_eq!(actual, expected, "is_pwr2minu1({test}) gave {actual}, expected {expected}"); + } + } + + #[test] + fn test_is_pwr2plus1() { + const OUTCOMES: [bool; 10] = + [false, false, true, true, false, false, false, true, false, false]; + for (test, expected) in TESTERS.iter().zip(OUTCOMES) { + let actual = is_pwr2plus1(*test); + assert_eq!(actual, expected, "is_pwr2plus1({test}) gave {actual}, expected {expected}"); + } + } + + #[test] + fn test_pwr2_to_exponent() { + const TESTERS: [u128; 9] = [ + 1, + 2, + 4, + 8, + 16, + 9223372036854775808, + 18446744073709551616, + 36893488147419103232, + 170141183460469231731687303715884105728, + ]; + const OUTCOMES: [&str; 9] = ["⁰", "¹", "²", "³", "⁴", "⁶³", "⁶⁴", "⁶⁵", "¹²⁷"]; + for (test, expected) in TESTERS.iter().zip(OUTCOMES) { + let actual = pwr2_to_exponent(*test); + assert_eq!( + actual, expected, + "pwr2_to_exponent({test}) returned {actual}, expected {expected}", + ); + } + } +} diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 014b751f95b0c..8c32cc9720af4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -303,6 +303,7 @@ m!(ab$0c); --- Outer + --- ```rust @@ -1357,7 +1358,7 @@ fn hover_enum_limit() { --- - size = 12 (0xC), align = 4, niches = 4294967288 + size = 12 (0xC), align = 4, niches = a lot "#]], ); } @@ -4401,6 +4402,7 @@ fn main() { --- size = 8, align = 8, niches = 1 + --- ```rust @@ -10094,6 +10096,7 @@ fn bar() { ```rust let field: i32 ``` + --- ```rust @@ -10128,6 +10131,7 @@ fn bar() { --- size = 4, align = 4 + --- ```rust diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 6d83a747d7669..1f723c85df7aa 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -209,7 +209,7 @@ fn hints( ) { closing_brace::hints(hints, sema, config, file_id, node.clone()); if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) { - generic_param::hints(hints, sema, config, any_has_generic_args); + generic_param::hints(hints, famous_defs, config, any_has_generic_args); } match_ast! { @@ -300,22 +300,23 @@ pub struct InlayHintsConfig { pub closing_brace_hints_min_lines: Option, pub fields_to_resolve: InlayFieldsToResolve, } + impl InlayHintsConfig { - fn lazy_text_edit(&self, finish: impl FnOnce() -> TextEdit) -> Lazy { + fn lazy_text_edit(&self, finish: impl FnOnce() -> TextEdit) -> LazyProperty { if self.fields_to_resolve.resolve_text_edits { - Lazy::Lazy + LazyProperty::Lazy } else { let edit = finish(); never!(edit.is_empty(), "inlay hint produced an empty text edit"); - Lazy::Computed(edit) + LazyProperty::Computed(edit) } } - fn lazy_tooltip(&self, finish: impl FnOnce() -> InlayTooltip) -> Lazy { + fn lazy_tooltip(&self, finish: impl FnOnce() -> InlayTooltip) -> LazyProperty { if self.fields_to_resolve.resolve_hint_tooltip && self.fields_to_resolve.resolve_label_tooltip { - Lazy::Lazy + LazyProperty::Lazy } else { let tooltip = finish(); never!( @@ -326,7 +327,20 @@ impl InlayHintsConfig { .is_empty(), "inlay hint produced an empty tooltip" ); - Lazy::Computed(tooltip) + LazyProperty::Computed(tooltip) + } + } + + /// This always reports a resolvable location, so only use this when it is very likely for a + /// location link to actually resolve but where computing `finish` would be costly. + fn lazy_location_opt( + &self, + finish: impl FnOnce() -> Option, + ) -> Option> { + if self.fields_to_resolve.resolve_label_location { + Some(LazyProperty::Lazy) + } else { + finish().map(LazyProperty::Computed) } } } @@ -441,7 +455,7 @@ pub struct InlayHint { /// The actual label to show in the inlay hint. pub label: InlayHintLabel, /// Text edit to apply when "accepting" this inlay hint. - pub text_edit: Option>, + pub text_edit: Option>, /// Range to recompute inlay hints when trying to resolve for this hint. If this is none, the /// hint does not support resolving. pub resolve_parent: Option, @@ -449,15 +463,15 @@ pub struct InlayHint { /// A type signaling that a value is either computed, or is available for computation. #[derive(Clone, Debug)] -pub enum Lazy { +pub enum LazyProperty { Computed(T), Lazy, } -impl Lazy { +impl LazyProperty { pub fn computed(self) -> Option { match self { - Lazy::Computed(it) => Some(it), + LazyProperty::Computed(it) => Some(it), _ => None, } } @@ -508,8 +522,8 @@ pub struct InlayHintLabel { impl InlayHintLabel { pub fn simple( s: impl Into, - tooltip: Option>, - linked_location: Option, + tooltip: Option>, + linked_location: Option>, ) -> InlayHintLabel { InlayHintLabel { parts: smallvec![InlayHintLabelPart { text: s.into(), linked_location, tooltip }], @@ -593,16 +607,16 @@ pub struct InlayHintLabelPart { /// refers to (not necessarily the location itself). /// When setting this, no tooltip must be set on the containing hint, or VS Code will display /// them both. - pub linked_location: Option, + pub linked_location: Option>, /// The tooltip to show when hovering over the inlay hint, this may invoke other actions like /// hover requests to show. - pub tooltip: Option>, + pub tooltip: Option>, } impl std::hash::Hash for InlayHintLabelPart { fn hash(&self, state: &mut H) { self.text.hash(state); - self.linked_location.hash(state); + self.linked_location.is_some().hash(state); self.tooltip.is_some().hash(state); } } @@ -610,7 +624,9 @@ impl std::hash::Hash for InlayHintLabelPart { impl fmt::Debug for InlayHintLabelPart { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self { text, linked_location: None, tooltip: None | Some(Lazy::Lazy) } => text.fmt(f), + Self { text, linked_location: None, tooltip: None | Some(LazyProperty::Lazy) } => { + text.fmt(f) + } Self { text, linked_location, tooltip } => f .debug_struct("InlayHintLabelPart") .field("text", text) @@ -618,8 +634,10 @@ impl fmt::Debug for InlayHintLabelPart { .field( "tooltip", &tooltip.as_ref().map_or("", |it| match it { - Lazy::Computed(InlayTooltip::String(it) | InlayTooltip::Markdown(it)) => it, - Lazy::Lazy => "", + LazyProperty::Computed( + InlayTooltip::String(it) | InlayTooltip::Markdown(it), + ) => it, + LazyProperty::Lazy => "", }), ) .finish(), @@ -632,7 +650,8 @@ struct InlayHintLabelBuilder<'a> { db: &'a RootDatabase, result: InlayHintLabel, last_part: String, - location: Option, + resolve: bool, + location: Option>, } impl fmt::Write for InlayHintLabelBuilder<'_> { @@ -645,11 +664,16 @@ impl HirWrite for InlayHintLabelBuilder<'_> { fn start_location_link(&mut self, def: ModuleDefId) { never!(self.location.is_some(), "location link is already started"); self.make_new_part(); - let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return }; - let location = location.call_site(); - let location = - FileRange { file_id: location.file_id, range: location.focus_or_full_range() }; - self.location = Some(location); + + self.location = Some(if self.resolve { + LazyProperty::Lazy + } else { + LazyProperty::Computed({ + let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return }; + let location = location.call_site(); + FileRange { file_id: location.file_id, range: location.focus_or_full_range() } + }) + }); } fn end_location_link(&mut self) { @@ -735,6 +759,7 @@ fn label_of_ty( last_part: String::new(), location: None, result: InlayHintLabel::default(), + resolve: config.fields_to_resolve.resolve_label_location, }; let _ = rec(sema, famous_defs, config.max_length, ty, &mut label_builder, config, edition); let r = label_builder.finish(); @@ -783,7 +808,7 @@ fn ty_to_text_edit( ty: &hir::Type, offset_to_insert: TextSize, prefix: impl Into, -) -> Option> { +) -> Option> { // FIXME: Limit the length and bail out on excess somehow? let rendered = sema .scope(node_for_hint) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index ab5464156f0a7..01a1a4545c478 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -1203,6 +1203,40 @@ fn f5>(it: G) { let l = it.f(); //^ () } +"#, + ); + } + + #[test] + fn regression_19007() { + check_types( + r#" +trait Foo { + type Assoc; + + fn foo(&self) -> Self::Assoc; +} + +trait Bar { + type Target; +} + +trait Baz {} + +struct Struct { + field: T, +} + +impl Struct +where + T: Foo, + T::Assoc: Baz<::Target> + Bar, +{ + fn f(&self) { + let x = self.field.foo(); + //^ impl Baz<<::Assoc as Bar>::Target> + Bar + } +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs index 429ddd31cbd0a..e9b728bcaa75d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs @@ -22,11 +22,7 @@ pub(super) fn hints( return None; } - let linked_location = - famous_defs.core_marker_Sized().and_then(|it| it.try_to_nav(sema.db)).map(|it| { - let n = it.call_site(); - FileRange { file_id: n.file_id, range: n.focus_or_full_range() } - }); + let sized_trait = famous_defs.core_marker_Sized(); for param in params.type_or_const_params() { match param { @@ -48,7 +44,17 @@ pub(super) fn hints( } hint.parts.push(InlayHintLabelPart { text: "Sized".to_owned(), - linked_location, + linked_location: sized_trait.and_then(|it| { + config.lazy_location_opt(|| { + it.try_to_nav(sema.db).map(|it| { + let n = it.call_site(); + FileRange { + file_id: n.file_id, + range: n.focus_or_full_range(), + } + }) + }) + }), tooltip: None, }); if has_bounds { @@ -134,12 +140,14 @@ fn foo() {} InlayHintLabelPart { text: "Sized", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 135..140, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 135..140, + }, + ), ), tooltip: "", }, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs index 7fa7ab1a94d63..8471547727fed 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs @@ -81,7 +81,10 @@ mod tests { use crate::{ fixture, - inlay_hints::tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG}, + inlay_hints::{ + tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG}, + LazyProperty, + }, InlayHintsConfig, }; @@ -99,7 +102,7 @@ mod tests { let (analysis, file_id) = fixture::file(ra_fixture); let mut inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap(); inlay_hints.iter_mut().flat_map(|hint| &mut hint.label.parts).for_each(|hint| { - if let Some(loc) = &mut hint.linked_location { + if let Some(LazyProperty::Computed(loc)) = &mut hint.linked_location { loc.range = TextRange::empty(TextSize::from(0)); } }); @@ -134,12 +137,14 @@ fn main() { InlayHintLabelPart { text: "B", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 63..64, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 63..64, + }, + ), ), tooltip: "", }, @@ -151,12 +156,14 @@ fn main() { InlayHintLabelPart { text: "A", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 7..8, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..8, + }, + ), ), tooltip: "", }, @@ -213,12 +220,14 @@ fn main() { InlayHintLabelPart { text: "C", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 51..52, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 51..52, + }, + ), ), tooltip: "", }, @@ -230,12 +239,14 @@ fn main() { InlayHintLabelPart { text: "B", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 29..30, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 29..30, + }, + ), ), tooltip: "", }, @@ -276,12 +287,14 @@ fn main() { InlayHintLabelPart { text: "C", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 51..52, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 51..52, + }, + ), ), tooltip: "", }, @@ -293,12 +306,14 @@ fn main() { InlayHintLabelPart { text: "B", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 29..30, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 29..30, + }, + ), ), tooltip: "", }, @@ -340,12 +355,14 @@ fn main() { InlayHintLabelPart { text: "B", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 23..24, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 23..24, + }, + ), ), tooltip: "", }, @@ -353,12 +370,14 @@ fn main() { InlayHintLabelPart { text: "X", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 55..56, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 55..56, + }, + ), ), tooltip: "", }, @@ -371,12 +390,14 @@ fn main() { InlayHintLabelPart { text: "A", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 7..8, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..8, + }, + ), ), tooltip: "", }, @@ -384,12 +405,14 @@ fn main() { InlayHintLabelPart { text: "X", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 55..56, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 55..56, + }, + ), ), tooltip: "", }, @@ -435,12 +458,14 @@ fn main() { InlayHintLabelPart { text: "Iterator", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -448,12 +473,14 @@ fn main() { InlayHintLabelPart { text: "Item", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -467,12 +494,14 @@ fn main() { InlayHintLabelPart { text: "Iterator", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -480,12 +509,14 @@ fn main() { InlayHintLabelPart { text: "Item", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -499,12 +530,14 @@ fn main() { InlayHintLabelPart { text: "Iterator", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -512,12 +545,14 @@ fn main() { InlayHintLabelPart { text: "Item", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -531,12 +566,14 @@ fn main() { InlayHintLabelPart { text: "MyIter", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -577,12 +614,14 @@ fn main() { InlayHintLabelPart { text: "Struct", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 7..13, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..13, + }, + ), ), tooltip: "", }, @@ -594,12 +633,14 @@ fn main() { InlayHintLabelPart { text: "Struct", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 7..13, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..13, + }, + ), ), tooltip: "", }, @@ -611,12 +652,14 @@ fn main() { InlayHintLabelPart { text: "Struct", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 7..13, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..13, + }, + ), ), tooltip: "", }, @@ -628,12 +671,14 @@ fn main() { InlayHintLabelPart { text: "self", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 42..46, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 42..46, + }, + ), ), tooltip: "", }, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs index 90b8be64a46d2..3767d34e2c7a9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs @@ -11,7 +11,10 @@ use syntax::{ match_ast, SyntaxKind, SyntaxNode, T, }; -use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind}; +use crate::{ + inlay_hints::LazyProperty, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, + InlayKind, +}; pub(super) fn hints( acc: &mut Vec, @@ -141,7 +144,7 @@ pub(super) fn hints( acc.push(InlayHint { range: closing_token.text_range(), kind: InlayKind::ClosingBrace, - label: InlayHintLabel::simple(label, None, linked_location), + label: InlayHintLabel::simple(label, None, linked_location.map(LazyProperty::Computed)), text_edit: None, position: InlayHintPosition::After, pad_left: true, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs index 906f2acf0c445..3e91618d08e6f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs @@ -53,10 +53,6 @@ pub(super) fn hints( let last = captures.len() - 1; for (idx, capture) in captures.into_iter().enumerate() { let local = capture.local(); - let source = local.primary_source(sema.db); - - // force cache the source file, otherwise sema lookup will potentially panic - _ = sema.parse_or_expand(source.file()); let label = format!( "{}{}", @@ -73,8 +69,17 @@ pub(super) fn hints( } hint.label.append_part(InlayHintLabelPart { text: label, - linked_location: source.name().and_then(|name| { - name.syntax().original_file_range_opt(sema.db).map(TupleExt::head).map(Into::into) + linked_location: config.lazy_location_opt(|| { + let source = local.primary_source(sema.db); + + // force cache the source file, otherwise sema lookup will potentially panic + _ = sema.parse_or_expand(source.file()); + source.name().and_then(|name| { + name.syntax() + .original_file_range_opt(sema.db) + .map(TupleExt::head) + .map(Into::into) + }) }), tooltip: None, }); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs index 037b328d971fc..762a4c2655181 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs @@ -1,17 +1,19 @@ //! Implementation of inlay hints for generic parameters. -use ide_db::{active_parameter::generic_def_for_node, RootDatabase}; +use ide_db::{active_parameter::generic_def_for_node, famous_defs::FamousDefs}; use syntax::{ ast::{self, AnyHasGenericArgs, HasGenericArgs, HasName}, AstNode, }; -use crate::{inlay_hints::GenericParameterHints, InlayHint, InlayHintsConfig, InlayKind}; +use crate::{ + inlay_hints::GenericParameterHints, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind, +}; -use super::param_name::{is_argument_similar_to_param_name, render_label}; +use super::param_name::is_argument_similar_to_param_name; pub(crate) fn hints( acc: &mut Vec, - sema: &hir::Semantics<'_, RootDatabase>, + FamousDefs(sema, krate): &FamousDefs<'_, '_>, config: &InlayHintsConfig, node: AnyHasGenericArgs, ) -> Option<()> { @@ -45,12 +47,23 @@ pub(crate) fn hints( return None; } - let name = param.name(sema.db); - let param_name = name.as_str(); + let allowed = match (param, &arg) { + (hir::GenericParam::TypeParam(_), ast::GenericArg::TypeArg(_)) => type_hints, + (hir::GenericParam::ConstParam(_), ast::GenericArg::ConstArg(_)) => const_hints, + (hir::GenericParam::LifetimeParam(_), ast::GenericArg::LifetimeArg(_)) => { + lifetime_hints + } + _ => false, + }; + if !allowed { + return None; + } + + let param_name = param.name(sema.db); let should_hide = { let argument = get_string_representation(&arg)?; - is_argument_similar_to_param_name(&argument, param_name) + is_argument_similar_to_param_name(&argument, param_name.as_str()) }; if should_hide { @@ -59,30 +72,28 @@ pub(crate) fn hints( let range = sema.original_range_opt(arg.syntax())?.range; - let source_syntax = match param { - hir::GenericParam::TypeParam(it) => { - if !type_hints || !matches!(arg, ast::GenericArg::TypeArg(_)) { - return None; - } - sema.source(it.merge())?.value.syntax().clone() - } - hir::GenericParam::ConstParam(it) => { - if !const_hints || !matches!(arg, ast::GenericArg::ConstArg(_)) { - return None; - } - let syntax = sema.source(it.merge())?.value.syntax().clone(); - let const_param = ast::ConstParam::cast(syntax)?; - const_param.name()?.syntax().clone() - } - hir::GenericParam::LifetimeParam(it) => { - if !lifetime_hints || !matches!(arg, ast::GenericArg::LifetimeArg(_)) { - return None; - } - sema.source(it)?.value.syntax().clone() - } - }; - let linked_location = sema.original_range_opt(&source_syntax); - let label = render_label(param_name, config, linked_location); + let colon = if config.render_colons { ":" } else { "" }; + let label = InlayHintLabel::simple( + format!("{}{colon}", param_name.display(sema.db, krate.edition(sema.db))), + None, + config.lazy_location_opt(|| { + let source_syntax = match param { + hir::GenericParam::TypeParam(it) => { + sema.source(it.merge()).map(|it| it.value.syntax().clone()) + } + hir::GenericParam::ConstParam(it) => { + let syntax = sema.source(it.merge())?.value.syntax().clone(); + let const_param = ast::ConstParam::cast(syntax)?; + const_param.name().map(|it| it.syntax().clone()) + } + hir::GenericParam::LifetimeParam(it) => { + sema.source(it).map(|it| it.value.syntax().clone()) + } + }; + let linked_location = source_syntax.and_then(|it| sema.original_range_opt(&it)); + linked_location.map(Into::into) + }), + ); Some(InlayHint { range, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs index 1358d3722f897..27c7c3d498187 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs @@ -49,7 +49,7 @@ pub(super) fn hints( if mir.locals[place.local].ty.adt_id(ChalkTyInterner).is_none() { continue; // Arguably only ADTs have significant drop impls } - let Some(binding) = local_to_binding.get(place.local) else { + let Some(&binding_idx) = local_to_binding.get(place.local) else { continue; // Ignore temporary values }; let range = match terminator.span { @@ -91,25 +91,26 @@ pub(super) fn hints( }, MirSpan::Unknown => continue, }; - let binding_source = source_map - .patterns_for_binding(*binding) - .first() - .and_then(|d| source_map.pat_syntax(*d).ok()) - .and_then(|d| { - Some(FileRange { - file_id: d.file_id.file_id()?.into(), - range: d.value.text_range(), - }) - }); - let binding = &hir.bindings[*binding]; + let binding = &hir.bindings[binding_idx]; let name = binding.name.display_no_db(file_id.edition()).to_smolstr(); if name.starts_with(" i32 { x + y } //! _ = max(/*x*/4, /*y*/4); //! ``` -use std::fmt::Display; use either::Either; use hir::{Callable, Semantics}; @@ -20,7 +19,7 @@ use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, Inla pub(super) fn hints( acc: &mut Vec, - FamousDefs(sema, _): &FamousDefs<'_, '_>, + FamousDefs(sema, krate): &FamousDefs<'_, '_>, config: &InlayHintsConfig, _file_id: EditionedFileId, expr: ast::Expr, @@ -37,23 +36,29 @@ pub(super) fn hints( .filter_map(|(p, arg)| { // Only annotate hints for expressions that exist in the original file let range = sema.original_range_opt(arg.syntax())?; - let source = sema.source(p)?; - let (param_name, name_syntax) = match source.value.as_ref() { - Either::Left(pat) => (pat.name()?, pat.name()), - Either::Right(param) => match param.pat()? { - ast::Pat::IdentPat(it) => (it.name()?, it.name()), - _ => return None, - }, - }; - Some((name_syntax, param_name, arg, range)) + let param_name = p.name(sema.db)?; + Some((p, param_name, arg, range)) }) .filter(|(_, param_name, arg, _)| { - !should_hide_param_name_hint(sema, &callable, ¶m_name.text(), arg) + !should_hide_param_name_hint(sema, &callable, param_name.as_str(), arg) }) .map(|(param, param_name, _, hir::FileRange { range, .. })| { - let linked_location = param.and_then(|name| sema.original_range_opt(name.syntax())); - - let label = render_label(¶m_name, config, linked_location); + let colon = if config.render_colons { ":" } else { "" }; + let label = InlayHintLabel::simple( + format!("{}{colon}", param_name.display(sema.db, krate.edition(sema.db))), + None, + config.lazy_location_opt(|| { + let source = sema.source(param)?; + let name_syntax = match source.value.as_ref() { + Either::Left(pat) => pat.name(), + Either::Right(param) => match param.pat()? { + ast::Pat::IdentPat(it) => it.name(), + _ => None, + }, + }?; + sema.original_range_opt(name_syntax.syntax()).map(Into::into) + }), + ); InlayHint { range, kind: InlayKind::Parameter, @@ -70,16 +75,6 @@ pub(super) fn hints( Some(()) } -pub(super) fn render_label( - param_name: impl Display, - config: &InlayHintsConfig, - linked_location: Option, -) -> InlayHintLabel { - let colon = if config.render_colons { ":" } else { "" }; - - InlayHintLabel::simple(format!("{param_name}{colon}"), None, linked_location.map(Into::into)) -} - fn get_callable( sema: &Semantics<'_, RootDatabase>, expr: &ast::Expr, @@ -124,9 +119,7 @@ fn should_hide_param_name_hint( } let fn_name = match callable.kind() { - hir::CallableKind::Function(it) => { - Some(it.name(sema.db).unescaped().display_no_db().to_smolstr()) - } + hir::CallableKind::Function(it) => Some(it.name(sema.db).as_str().to_smolstr()), _ => None, }; let fn_name = fn_name.as_deref(); diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 346e2862b0fdd..e942f5a6aac78 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -91,7 +91,8 @@ pub use crate::{ inlay_hints::{ AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, GenericParameterHints, InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, - InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, + InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip, LazyProperty, + LifetimeElisionHints, }, join_lines::JoinLinesConfig, markup::Markup, @@ -671,7 +672,7 @@ impl Analysis { &self, config: &CompletionConfig<'_>, position: FilePosition, - imports: impl IntoIterator + std::panic::UnwindSafe, + imports: impl IntoIterator + std::panic::UnwindSafe, ) -> Cancellable> { Ok(self .with_db(|db| ide_completion::resolve_completion_edits(db, config, position, imports))? diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index ba739df3092b3..07dfd83c4eb7f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -263,7 +263,7 @@ fn find_definitions( .and_then(|def| { // if the name differs from the definitions name it has to be an alias if def - .name(sema.db).is_some_and(|it| !it.eq_ident(name_ref.text().as_str())) + .name(sema.db).is_some_and(|it| it.as_str() != name_ref.text().trim_start_matches("r#")) { Err(format_err!("Renaming aliases is currently unsupported")) } else { diff --git a/src/tools/rust-analyzer/crates/intern/Cargo.toml b/src/tools/rust-analyzer/crates/intern/Cargo.toml index 5e7ee54c6af77..c0358ef929b4d 100644 --- a/src/tools/rust-analyzer/crates/intern/Cargo.toml +++ b/src/tools/rust-analyzer/crates/intern/Cargo.toml @@ -19,7 +19,6 @@ dashmap.workspace = true hashbrown.workspace = true rustc-hash.workspace = true triomphe.workspace = true -sptr = "0.3.2" [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol.rs b/src/tools/rust-analyzer/crates/intern/src/symbol.rs index 200b14027f804..b3bf285edfb11 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol.rs @@ -13,7 +13,6 @@ use std::{ use dashmap::{DashMap, SharedValue}; use hashbrown::{hash_map::RawEntryMut, HashMap}; use rustc_hash::FxHasher; -use sptr::Strict; use triomphe::Arc; pub mod symbols; @@ -84,7 +83,7 @@ impl TaggedArcPtr { #[inline] pub(crate) unsafe fn try_as_arc_owned(self) -> Option>>> { // Unpack the tag from the alignment niche - let tag = Strict::addr(self.packed.as_ptr()) & Self::BOOL_BITS; + let tag = self.packed.as_ptr().addr() & Self::BOOL_BITS; if tag != 0 { // Safety: We checked that the tag is non-zero -> true, so we are pointing to the data offset of an `Arc` Some(ManuallyDrop::new(unsafe { @@ -99,40 +98,18 @@ impl TaggedArcPtr { fn pack_arc(ptr: NonNull<*const str>) -> NonNull<*const str> { let packed_tag = true as usize; - // can't use this strict provenance stuff here due to trait methods not being const - // unsafe { - // // Safety: The pointer is derived from a non-null - // NonNull::new_unchecked(Strict::map_addr(ptr.as_ptr(), |addr| { - // // Safety: - // // - The pointer is `NonNull` => it's address is `NonZero` - // // - `P::BITS` least significant bits are always zero (`Pointer` contract) - // // - `T::BITS <= P::BITS` (from `Self::ASSERTION`) - // // - // // Thus `addr >> T::BITS` is guaranteed to be non-zero. - // // - // // `{non_zero} | packed_tag` can't make the value zero. - - // (addr >> Self::BOOL_BITS) | packed_tag - // })) - // } - // so what follows is roughly what the above looks like but inlined - - let self_addr = ptr.as_ptr() as *const *const str as usize; - let addr = self_addr | packed_tag; - let dest_addr = addr as isize; - let offset = dest_addr.wrapping_sub(self_addr as isize); - - // SAFETY: The resulting pointer is guaranteed to be NonNull as we only modify the niche bytes - unsafe { NonNull::new_unchecked(ptr.as_ptr().cast::().wrapping_offset(offset).cast()) } + unsafe { + // Safety: The pointer is derived from a non-null and bit-oring it with true (1) will + // not make it null. + NonNull::new_unchecked(ptr.as_ptr().map_addr(|addr| addr | packed_tag)) + } } #[inline] pub(crate) fn pointer(self) -> NonNull<*const str> { // SAFETY: The resulting pointer is guaranteed to be NonNull as we only modify the niche bytes unsafe { - NonNull::new_unchecked(Strict::map_addr(self.packed.as_ptr(), |addr| { - addr & !Self::BOOL_BITS - })) + NonNull::new_unchecked(self.packed.as_ptr().map_addr(|addr| addr & !Self::BOOL_BITS)) } } diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index b3b46421b50fb..9bc78ff87b8aa 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -240,8 +240,10 @@ define_symbols! { format_unsafe_arg, format, freeze, + from, From, FromStr, + from_str, from_output, from_residual, from_usize, @@ -273,6 +275,8 @@ define_symbols! { index_mut, index, Index, + into, + Into, into_future, into_iter, IntoFuture, @@ -361,6 +365,7 @@ define_symbols! { panic_nounwind, panic, Param, + parse, partial_ord, PartialEq, PartialOrd, @@ -389,6 +394,7 @@ define_symbols! { RangeToInclusive, Ready, receiver, + receiver_target, recursion_limit, register_attr, register_tool, @@ -454,13 +460,17 @@ define_symbols! { termination, test_case, test, + then, thiscall, + to_string, trace_macros, transmute_opts, transmute_trait, transparent, + try_into, Try, TryFrom, + try_from, tuple_trait, u128, u16, diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index 00446b27cf2f3..5654c04a59287 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -256,6 +256,24 @@ impl ProjectFolders { fsc.add_file_set(file_set_roots) } + for ws in workspaces.iter() { + let mut file_set_roots: Vec = vec![]; + let mut entries = vec![]; + + for buildfile in ws.buildfiles() { + file_set_roots.push(VfsPath::from(buildfile.to_owned())); + entries.push(buildfile.to_owned()); + } + + if !file_set_roots.is_empty() { + let entry = vfs::loader::Entry::Files(entries); + res.watch.push(res.load.len()); + res.load.push(entry); + local_filesets.push(fsc.len() as u64); + fsc.add_file_set(file_set_roots) + } + } + if let Some(user_config_path) = user_config_dir_path { let ratoml_path = { let mut p = user_config_path.to_path_buf(); diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index c7614849e015b..59293ee3f9659 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -392,12 +392,12 @@ impl server::Span for RaSpanServer { fn line(&mut self, _span: Self::Span) -> usize { // FIXME requires db to resolve line index, THIS IS NOT INCREMENTAL - 0 + 1 } fn column(&mut self, _span: Self::Span) -> usize { // FIXME requires db to resolve line index, THIS IS NOT INCREMENTAL - 0 + 1 } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs index 466eb14b55ea5..409cf3cc78134 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs @@ -291,11 +291,11 @@ impl server::Span for TokenIdServer { } fn line(&mut self, _span: Self::Span) -> usize { - 0 + 1 } fn column(&mut self, _span: Self::Span) -> usize { - 0 + 1 } } diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs index 6a88cf022dfb0..a396396761041 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs @@ -63,7 +63,7 @@ use crate::{ManifestPath, TargetKind}; pub struct ProjectJson { /// e.g. `path/to/sysroot` pub(crate) sysroot: Option, - /// e.g. `path/to/sysroot/lib/rustlib/src/rust` + /// e.g. `path/to/sysroot/lib/rustlib/src/rust/library` pub(crate) sysroot_src: Option, project_root: AbsPathBuf, /// The path to the rust-project.json file. May be None if this diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs index 4bf9b59e7d038..e472da0c89b0d 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs @@ -19,7 +19,7 @@ pub fn get( let rustc_cfgs = match rustc_cfgs { Ok(cfgs) => cfgs, Err(e) => { - tracing::error!(?e, "failed to get rustc cfgs"); + tracing::warn!(?e, "failed to get rustc cfgs"); return vec![]; } }; diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index f98d983ac0608..dcd62753cb2f9 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -524,6 +524,17 @@ impl ProjectWorkspace { } } + pub fn buildfiles(&self) -> Vec { + match &self.kind { + ProjectWorkspaceKind::Json(project) => project + .crates() + .filter_map(|(_, krate)| krate.build.as_ref().map(|build| build.build_file.clone())) + .map(|build_file| self.workspace_root().join(build_file)) + .collect(), + _ => vec![], + } + } + pub fn find_sysroot_proc_macro_srv(&self) -> anyhow::Result { self.sysroot.discover_proc_macro_srv() } @@ -568,27 +579,15 @@ impl ProjectWorkspace { match &self.kind { ProjectWorkspaceKind::Json(project) => project .crates() - .map(|(_, krate)| { - // FIXME: PackageRoots dont allow specifying files, only directories - let build_file = krate - .build - .as_ref() - .map(|build| self.workspace_root().join(&build.build_file)) - .as_deref() - .and_then(AbsPath::parent) - .map(ToOwned::to_owned); - - PackageRoot { - is_local: krate.is_workspace_member, - include: krate - .include - .iter() - .cloned() - .chain(build_file) - .chain(self.extra_includes.iter().cloned()) - .collect(), - exclude: krate.exclude.clone(), - } + .map(|(_, krate)| PackageRoot { + is_local: krate.is_workspace_member, + include: krate + .include + .iter() + .cloned() + .chain(self.extra_includes.iter().cloned()) + .collect(), + exclude: krate.exclude.clone(), }) .collect::>() .into_iter() diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index bcaec52019591..18c27c844964b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -465,6 +465,7 @@ impl flags::AnalysisStats { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }, Edition::LATEST, ) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 3dc4379258fa1..44325fa1a29e6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -453,6 +453,10 @@ config_data! { /// /// In `match` arms it completes a comma instead. completion_addSemicolonToUnit: bool = true, + /// Toggles the additional completions that automatically show method calls and field accesses with `await` prefixed to them when completing on a future. + completion_autoAwait_enable: bool = true, + /// Toggles the additional completions that automatically show method calls with `iter()` or `into_iter()` prefixed to them when completing on a type that has them. + completion_autoIter_enable: bool = true, /// Toggles the additional completions that automatically add imports when completed. /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. completion_autoimport_enable: bool = true, @@ -1484,6 +1488,8 @@ impl Config { enable_imports_on_the_fly: self.completion_autoimport_enable(source_root).to_owned() && self.caps.completion_item_edit_resolve(), enable_self_on_the_fly: self.completion_autoself_enable(source_root).to_owned(), + enable_auto_iter: *self.completion_autoIter_enable(source_root), + enable_auto_await: *self.completion_autoAwait_enable(source_root), enable_private_editable: self.completion_privateEditable_enable(source_root).to_owned(), full_function_signatures: self .completion_fullFunctionSignatures_enable(source_root) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index 22f06d68d80d1..2309f94a7429b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -88,6 +88,17 @@ pub(crate) enum FlycheckConfig { }, } +impl FlycheckConfig { + pub(crate) fn invocation_strategy_once(&self) -> bool { + match self { + FlycheckConfig::CargoCommand { .. } => false, + FlycheckConfig::CustomCommand { invocation_strategy, .. } => { + *invocation_strategy == InvocationStrategy::Once + } + } + } +} + impl fmt::Display for FlycheckConfig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index 98efc637c2c81..84ba89d9f31f9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -291,9 +291,15 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { let file_id = state.vfs.read().0.file_id(&vfs_path); if let Some(file_id) = file_id { let world = state.snapshot(); + let invocation_strategy_once = state.config.flycheck(None).invocation_strategy_once(); let may_flycheck_workspace = state.config.flycheck_workspace(None); let mut updated = false; let task = move || -> std::result::Result<(), ide::Cancelled> { + if invocation_strategy_once { + let saved_file = vfs_path.as_path().map(|p| p.to_owned()); + world.flycheck[0].restart_workspace(saved_file.clone()); + } + let target = TargetSpec::for_file(&world, file_id)?.and_then(|it| { let tgt_kind = it.target_kind(); let (tgt_name, root, package) = match it { @@ -320,16 +326,15 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { // the user opted into package checks then let package_check_allowed = target.is_some() || !may_flycheck_workspace; if package_check_allowed { - let workspace = - world.workspaces.iter().enumerate().find(|(_, ws)| match &ws.kind { - project_model::ProjectWorkspaceKind::Cargo { cargo, .. } - | project_model::ProjectWorkspaceKind::DetachedFile { - cargo: Some((cargo, _, _)), - .. - } => *cargo.workspace_root() == root, - _ => false, - }); - if let Some((idx, _)) = workspace { + let workspace = world.workspaces.iter().position(|ws| match &ws.kind { + project_model::ProjectWorkspaceKind::Cargo { cargo, .. } + | project_model::ProjectWorkspaceKind::DetachedFile { + cargo: Some((cargo, _, _)), + .. + } => *cargo.workspace_root() == root, + _ => false, + }); + if let Some(idx) = workspace { world.flycheck[idx].restart_for_package(package, target); } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 190015d7faad8..39cbf53eaa21d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -1154,10 +1154,7 @@ pub(crate) fn handle_completion_resolve( .resolve_completion_edits( &forced_resolve_completions_config, position, - resolve_data - .imports - .into_iter() - .map(|import| (import.full_import_path, import.imported_name)), + resolve_data.imports.into_iter().map(|import| import.full_import_path), )? .into_iter() .flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel))) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs index fcfd06679bf2b..5cdc51a1c1995 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -176,6 +176,8 @@ fn integrated_completion_benchmark() { fields_to_resolve: CompletionFieldsToResolve::empty(), exclude_flyimport: vec![], exclude_traits: &[], + enable_auto_await: true, + enable_auto_iter: true, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -226,6 +228,8 @@ fn integrated_completion_benchmark() { fields_to_resolve: CompletionFieldsToResolve::empty(), exclude_flyimport: vec![], exclude_traits: &[], + enable_auto_await: true, + enable_auto_iter: true, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -274,6 +278,8 @@ fn integrated_completion_benchmark() { fields_to_resolve: CompletionFieldsToResolve::empty(), exclude_flyimport: vec![], exclude_traits: &[], + enable_auto_await: true, + enable_auto_iter: true, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs index 61ec576dd4f90..ccffa7a671e66 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs @@ -142,9 +142,8 @@ fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; hasher.update(prefix); hasher.update(u32::from(*text_size).to_le_bytes()); } - for (import_path, import_name) in &item.import_to_add { + for import_path in &item.import_to_add { hasher.update(import_path); - hasher.update(import_name); } hasher.finalize() } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs index 134de92feab3a..ca4372aa83f8d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs @@ -850,7 +850,6 @@ pub struct InlayHintResolveData { #[derive(Debug, Serialize, Deserialize)] pub struct CompletionImport { pub full_import_path: String, - pub imported_name: String, } #[derive(Debug, Deserialize, Default)] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index a5516e7f9d4fe..bff53cf98b7b8 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -11,8 +11,8 @@ use ide::{ Annotation, AnnotationKind, Assist, AssistKind, Cancellable, CompletionFieldsToResolve, CompletionItem, CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, - InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayKind, Markup, - NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp, + InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayKind, LazyProperty, + Markup, NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp, SnippetEdit, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize, }; use ide_db::{assists, rust_doc::format_docs, FxHasher}; @@ -394,10 +394,7 @@ fn completion_item( item.import_to_add .clone() .into_iter() - .map(|(import_path, import_name)| lsp_ext::CompletionImport { - full_import_path: import_path, - imported_name: import_name, - }) + .map(|import_path| lsp_ext::CompletionImport { full_import_path: import_path }) .collect() } else { Vec::new() @@ -549,12 +546,11 @@ pub(crate) fn inlay_hint( ) -> Cancellable { let hint_needs_resolve = |hint: &InlayHint| -> Option { hint.resolve_parent.filter(|_| { - hint.text_edit.is_some() - || hint - .label - .parts - .iter() - .any(|part| part.linked_location.is_some() || part.tooltip.is_some()) + hint.text_edit.as_ref().is_some_and(LazyProperty::is_lazy) + || hint.label.parts.iter().any(|part| { + part.linked_location.as_ref().is_some_and(LazyProperty::is_lazy) + || part.tooltip.as_ref().is_some_and(LazyProperty::is_lazy) + }) }) }; @@ -569,22 +565,21 @@ pub(crate) fn inlay_hint( }); let mut something_to_resolve = false; - let text_edits = if snap - .config - .visual_studio_code_version() - .is_none_or(|version| VersionReq::parse(">=1.86.0").unwrap().matches(version)) - && resolve_range_and_hash.is_some() - && fields_to_resolve.resolve_text_edits - { - something_to_resolve |= inlay_hint.text_edit.is_some(); - None - } else { - inlay_hint - .text_edit - .take() - .and_then(|it| it.computed()) - .map(|it| text_edit_vec(line_index, it)) - }; + let text_edits = inlay_hint + .text_edit + .take() + .and_then(|it| match it { + LazyProperty::Computed(it) => Some(it), + LazyProperty::Lazy => { + something_to_resolve |= + snap.config.visual_studio_code_version().is_none_or(|version| { + VersionReq::parse(">=1.86.0").unwrap().matches(version) + }) && resolve_range_and_hash.is_some() + && fields_to_resolve.resolve_text_edits; + None + } + }) + .map(|it| text_edit_vec(line_index, it)); let (label, tooltip) = inlay_hint_label( snap, fields_to_resolve, @@ -637,22 +632,23 @@ fn inlay_hint_label( let (label, tooltip) = match &*label.parts { [InlayHintLabelPart { linked_location: None, .. }] => { let InlayHintLabelPart { text, tooltip, .. } = label.parts.pop().unwrap(); - let hint_tooltip = if needs_resolve && fields_to_resolve.resolve_hint_tooltip { - *something_to_resolve |= tooltip.is_some(); - None - } else { - match tooltip.and_then(|it| it.computed()) { - Some(ide::InlayTooltip::String(s)) => { - Some(lsp_types::InlayHintTooltip::String(s)) - } - Some(ide::InlayTooltip::Markdown(s)) => { - Some(lsp_types::InlayHintTooltip::MarkupContent(lsp_types::MarkupContent { - kind: lsp_types::MarkupKind::Markdown, - value: s, - })) - } - None => None, + let tooltip = tooltip.and_then(|it| match it { + LazyProperty::Computed(it) => Some(it), + LazyProperty::Lazy => { + *something_to_resolve |= + needs_resolve && fields_to_resolve.resolve_hint_tooltip; + None } + }); + let hint_tooltip = match tooltip { + Some(ide::InlayTooltip::String(s)) => Some(lsp_types::InlayHintTooltip::String(s)), + Some(ide::InlayTooltip::Markdown(s)) => { + Some(lsp_types::InlayHintTooltip::MarkupContent(lsp_types::MarkupContent { + kind: lsp_types::MarkupKind::Markdown, + value: s, + })) + } + None => None, }; (lsp_types::InlayHintLabel::String(text), hint_tooltip) } @@ -661,31 +657,38 @@ fn inlay_hint_label( .parts .into_iter() .map(|part| { - let tooltip = if needs_resolve && fields_to_resolve.resolve_label_tooltip { - *something_to_resolve |= part.tooltip.is_some(); - None - } else { - match part.tooltip.and_then(|it| it.computed()) { - Some(ide::InlayTooltip::String(s)) => { - Some(lsp_types::InlayHintLabelPartTooltip::String(s)) - } - Some(ide::InlayTooltip::Markdown(s)) => { - Some(lsp_types::InlayHintLabelPartTooltip::MarkupContent( - lsp_types::MarkupContent { - kind: lsp_types::MarkupKind::Markdown, - value: s, - }, - )) - } - None => None, + let tooltip = part.tooltip.and_then(|it| match it { + LazyProperty::Computed(it) => Some(it), + LazyProperty::Lazy => { + *something_to_resolve |= fields_to_resolve.resolve_label_tooltip; + None } + }); + let tooltip = match tooltip { + Some(ide::InlayTooltip::String(s)) => { + Some(lsp_types::InlayHintLabelPartTooltip::String(s)) + } + Some(ide::InlayTooltip::Markdown(s)) => { + Some(lsp_types::InlayHintLabelPartTooltip::MarkupContent( + lsp_types::MarkupContent { + kind: lsp_types::MarkupKind::Markdown, + value: s, + }, + )) + } + None => None, }; - let location = if needs_resolve && fields_to_resolve.resolve_label_location { - *something_to_resolve |= part.linked_location.is_some(); - None - } else { - part.linked_location.map(|range| location(snap, range)).transpose()? - }; + let location = part + .linked_location + .and_then(|it| match it { + LazyProperty::Computed(it) => Some(it), + LazyProperty::Lazy => { + *something_to_resolve |= fields_to_resolve.resolve_label_location; + None + } + }) + .map(|range| location(snap, range)) + .transpose()?; Ok(lsp_types::InlayHintLabelPart { value: part.text, tooltip, diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index dca231604fa13..ff027ac5848b3 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -411,6 +411,11 @@ pub fn path_from_text(text: &str) -> ast::Path { ast_from_text(&format!("fn main() {{ let test: {text}; }}")) } +// FIXME: should not be pub +pub fn path_from_text_with_edition(text: &str, edition: Edition) -> ast::Path { + ast_from_text_with_edition(&format!("fn main() {{ let test: {text}; }}"), edition) +} + pub fn use_tree_glob() -> ast::UseTree { ast_from_text("use *;") } @@ -1230,7 +1235,12 @@ pub fn token_tree( #[track_caller] fn ast_from_text(text: &str) -> N { - let parse = SourceFile::parse(text, Edition::CURRENT); + ast_from_text_with_edition(text, Edition::CURRENT) +} + +#[track_caller] +fn ast_from_text_with_edition(text: &str, edition: Edition) -> N { + let parse = SourceFile::parse(text, edition); let node = match parse.tree().syntax().descendants().find_map(N::cast) { Some(it) => it, None => { diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index fd06736a25248..4ed68d18e8071 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -53,6 +53,7 @@ //! pin: //! pointee: copy, send, sync, ord, hash, unpin //! range: +//! receiver: deref //! result: //! send: sized //! size_of: sized @@ -513,10 +514,26 @@ pub mod ops { fn deref_mut(&mut self) -> &mut Self::Target; } // endregion:deref_mut + + // region:receiver + #[lang = "receiver"] + pub trait Receiver { + #[lang = "receiver_target"] + type Target: ?Sized; + } + + impl Receiver for P + where + P: Deref, + { + type Target = T; + } + // endregion:receiver } pub use self::deref::{ Deref, DerefMut, // :deref_mut + Receiver, // :receiver }; // endregion:deref diff --git a/src/tools/rust-analyzer/docs/dev/README.md b/src/tools/rust-analyzer/docs/dev/README.md index 3ba492e095974..c990212d585d2 100644 --- a/src/tools/rust-analyzer/docs/dev/README.md +++ b/src/tools/rust-analyzer/docs/dev/README.md @@ -269,19 +269,13 @@ Note: we tag releases by dates, releasing a patch release on the same day should ## Permissions -There are three sets of people with extra permissions: +There are two sets of people with extra permissions: -* rust-analyzer GitHub organization [**admins**](https://github.com/orgs/rust-analyzer/people?query=role:owner) (which include current t-compiler leads). - Admins have full access to the org. -* [**review**](https://github.com/orgs/rust-analyzer/teams/review) team in the organization. - Reviewers have `r+` access to all of organization's repositories and publish rights on crates.io. - They also have direct commit access, but all changes should via bors queue. +* The [rust-lang](https://github.com/rust-lang) team [t-rust-analyzer](https://github.com/rust-lang/team/blob/master/teams/rust-analyzer.toml). + This team has write access to the repository and merge queue permissions (note the repo itself is managed by infra admins). It's ok to self-approve if you think you know what you are doing! - bors should automatically sync the permissions. Feel free to request a review or assign any PR to a reviewer with the relevant expertise to bring the work to their attention. Don't feel pressured to review assigned PRs though. - If you don't feel like reviewing for whatever reason, someone else will pick the review up! -* [**triage**](https://github.com/orgs/rust-analyzer/teams/triage) team in the organization. - This team can label and close issues. - -Note that at the time being you need to be a member of the org yourself to view the links. + If you don't feel like reviewing for whatever reason, someone else will pick the review up (but please speak up if you don't feel like it)! +* The [rust-lang](https://github.com/rust-lang) team [t-rust-analyzer-contributors]([https://github.com/orgs/rust-analyzer/teams/triage](https://github.com/rust-lang/team/blob/master/teams/rust-analyzer-contributors.toml)). + This team has general triaging permissions allowing to label, close and re-open issues. diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md index a632fc6f5fb87..c7ee4e402363c 100644 --- a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md +++ b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@ $DIR/bad-reg.rs:41:18 - | -LL | asm!("", out("gp") _); - | ^^^^^^^^^^^ - error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:102:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: cannot use register `x16`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:48:18 + --> $DIR/bad-reg.rs:46:18 | LL | asm!("", out("x16") _); | ^^^^^^^^^^^^ error: cannot use register `x17`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:50:18 + --> $DIR/bad-reg.rs:48:18 | LL | asm!("", out("x17") _); | ^^^^^^^^^^^^ error: cannot use register `x18`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:52:18 + --> $DIR/bad-reg.rs:50:18 | LL | asm!("", out("x18") _); | ^^^^^^^^^^^^ error: cannot use register `x19`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:54:18 + --> $DIR/bad-reg.rs:52:18 | LL | asm!("", out("x19") _); | ^^^^^^^^^^^^ error: cannot use register `x20`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:56:18 + --> $DIR/bad-reg.rs:54:18 | LL | asm!("", out("x20") _); | ^^^^^^^^^^^^ error: cannot use register `x21`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:58:18 + --> $DIR/bad-reg.rs:56:18 | LL | asm!("", out("x21") _); | ^^^^^^^^^^^^ error: cannot use register `x22`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:60:18 + --> $DIR/bad-reg.rs:58:18 | LL | asm!("", out("x22") _); | ^^^^^^^^^^^^ error: cannot use register `x23`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:62:18 + --> $DIR/bad-reg.rs:60:18 | LL | asm!("", out("x23") _); | ^^^^^^^^^^^^ error: cannot use register `x24`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:64:18 + --> $DIR/bad-reg.rs:62:18 | LL | asm!("", out("x24") _); | ^^^^^^^^^^^^ error: cannot use register `x25`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:66:18 + --> $DIR/bad-reg.rs:64:18 | LL | asm!("", out("x25") _); | ^^^^^^^^^^^^ error: cannot use register `x26`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:68:18 + --> $DIR/bad-reg.rs:66:18 | LL | asm!("", out("x26") _); | ^^^^^^^^^^^^ error: cannot use register `x27`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:70:18 + --> $DIR/bad-reg.rs:68:18 | LL | asm!("", out("x27") _); | ^^^^^^^^^^^^ error: cannot use register `x28`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:72:18 + --> $DIR/bad-reg.rs:70:18 | LL | asm!("", out("x28") _); | ^^^^^^^^^^^^ error: cannot use register `x29`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:74:18 + --> $DIR/bad-reg.rs:72:18 | LL | asm!("", out("x29") _); | ^^^^^^^^^^^^ error: cannot use register `x30`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:76:18 + --> $DIR/bad-reg.rs:74:18 | LL | asm!("", out("x30") _); | ^^^^^^^^^^^^ error: cannot use register `x31`: register can't be used with the `e` target feature - --> $DIR/bad-reg.rs:78:18 + --> $DIR/bad-reg.rs:76:18 | LL | asm!("", out("x31") _); | ^^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:82:26 + --> $DIR/bad-reg.rs:80:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:84:26 + --> $DIR/bad-reg.rs:82:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:86:26 + --> $DIR/bad-reg.rs:84:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:89:26 + --> $DIR/bad-reg.rs:87:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:96:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -193,7 +187,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:99:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -201,12 +195,12 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:102:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ | = note: register class `vreg` supports these types: -error: aborting due to 34 previous errors +error: aborting due to 33 previous errors diff --git a/tests/ui/asm/riscv/bad-reg.riscv32gc.stderr b/tests/ui/asm/riscv/bad-reg.riscv32gc.stderr index 4770e70cc2b7d..4ff03d819e60f 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv32gc.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv32gc.stderr @@ -22,50 +22,44 @@ error: invalid register `gp`: the global pointer cannot be used as an operand fo LL | asm!("", out("gp") _); | ^^^^^^^^^^^ -error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 - | -LL | asm!("", out("gp") _); - | ^^^^^^^^^^^ - error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:102:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:96:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -73,7 +67,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:99:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -81,12 +75,12 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:102:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ | = note: register class `vreg` supports these types: -error: aborting due to 14 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui/asm/riscv/bad-reg.riscv32i.stderr b/tests/ui/asm/riscv/bad-reg.riscv32i.stderr index ae7db1554b196..fbe63eb0563c3 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv32i.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv32i.stderr @@ -22,74 +22,68 @@ error: invalid register `gp`: the global pointer cannot be used as an operand fo LL | asm!("", out("gp") _); | ^^^^^^^^^^^ -error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 - | -LL | asm!("", out("gp") _); - | ^^^^^^^^^^^ - error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:102:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:82:26 + --> $DIR/bad-reg.rs:80:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:84:26 + --> $DIR/bad-reg.rs:82:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:86:26 + --> $DIR/bad-reg.rs:84:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:89:26 + --> $DIR/bad-reg.rs:87:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:96:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -97,7 +91,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:99:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -105,12 +99,12 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:102:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ | = note: register class `vreg` supports these types: -error: aborting due to 18 previous errors +error: aborting due to 17 previous errors diff --git a/tests/ui/asm/riscv/bad-reg.riscv32imafc.stderr b/tests/ui/asm/riscv/bad-reg.riscv32imafc.stderr index 8bc5c9a87fce4..57664cfe893b7 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv32imafc.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv32imafc.stderr @@ -22,50 +22,44 @@ error: invalid register `gp`: the global pointer cannot be used as an operand fo LL | asm!("", out("gp") _); | ^^^^^^^^^^^ -error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 - | -LL | asm!("", out("gp") _); - | ^^^^^^^^^^^ - error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:102:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: `d` target feature is not enabled - --> $DIR/bad-reg.rs:86:35 + --> $DIR/bad-reg.rs:84:35 | LL | asm!("/* {} */", in(freg) d); | ^ @@ -73,7 +67,7 @@ LL | asm!("/* {} */", in(freg) d); = note: this is required to use type `f64` with register class `freg` error: `d` target feature is not enabled - --> $DIR/bad-reg.rs:89:36 + --> $DIR/bad-reg.rs:87:36 | LL | asm!("/* {} */", out(freg) d); | ^ @@ -81,7 +75,7 @@ LL | asm!("/* {} */", out(freg) d); = note: this is required to use type `f64` with register class `freg` error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:96:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -89,7 +83,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:99:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -97,12 +91,12 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:102:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ | = note: register class `vreg` supports these types: -error: aborting due to 16 previous errors +error: aborting due to 15 previous errors diff --git a/tests/ui/asm/riscv/bad-reg.riscv64gc.stderr b/tests/ui/asm/riscv/bad-reg.riscv64gc.stderr index 4770e70cc2b7d..4ff03d819e60f 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv64gc.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv64gc.stderr @@ -22,50 +22,44 @@ error: invalid register `gp`: the global pointer cannot be used as an operand fo LL | asm!("", out("gp") _); | ^^^^^^^^^^^ -error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 - | -LL | asm!("", out("gp") _); - | ^^^^^^^^^^^ - error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:102:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:96:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -73,7 +67,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:99:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -81,12 +75,12 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:102:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ | = note: register class `vreg` supports these types: -error: aborting due to 14 previous errors +error: aborting due to 13 previous errors diff --git a/tests/ui/asm/riscv/bad-reg.riscv64imac.stderr b/tests/ui/asm/riscv/bad-reg.riscv64imac.stderr index ae7db1554b196..fbe63eb0563c3 100644 --- a/tests/ui/asm/riscv/bad-reg.riscv64imac.stderr +++ b/tests/ui/asm/riscv/bad-reg.riscv64imac.stderr @@ -22,74 +22,68 @@ error: invalid register `gp`: the global pointer cannot be used as an operand fo LL | asm!("", out("gp") _); | ^^^^^^^^^^^ -error: invalid register `gp`: the global pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:41:18 - | -LL | asm!("", out("gp") _); - | ^^^^^^^^^^^ - error: invalid register `tp`: the thread pointer cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:43:18 + --> $DIR/bad-reg.rs:41:18 | LL | asm!("", out("tp") _); | ^^^^^^^^^^^ error: invalid register `zero`: the zero register cannot be used as an operand for inline asm - --> $DIR/bad-reg.rs:45:18 + --> $DIR/bad-reg.rs:43:18 | LL | asm!("", out("zero") _); | ^^^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:96:18 + --> $DIR/bad-reg.rs:94:18 | LL | asm!("", in("v0") x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:99:18 + --> $DIR/bad-reg.rs:97:18 | LL | asm!("", out("v0") x); | ^^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:102:26 + --> $DIR/bad-reg.rs:100:26 | LL | asm!("/* {} */", in(vreg) x); | ^^^^^^^^^^ error: register class `vreg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:105:26 + --> $DIR/bad-reg.rs:103:26 | LL | asm!("/* {} */", out(vreg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:82:26 + --> $DIR/bad-reg.rs:80:26 | LL | asm!("/* {} */", in(freg) f); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:84:26 + --> $DIR/bad-reg.rs:82:26 | LL | asm!("/* {} */", out(freg) _); | ^^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:86:26 + --> $DIR/bad-reg.rs:84:26 | LL | asm!("/* {} */", in(freg) d); | ^^^^^^^^^^ error: register class `freg` requires at least one of the following target features: d, f - --> $DIR/bad-reg.rs:89:26 + --> $DIR/bad-reg.rs:87:26 | LL | asm!("/* {} */", out(freg) d); | ^^^^^^^^^^^ error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:96:27 + --> $DIR/bad-reg.rs:94:27 | LL | asm!("", in("v0") x); | ^ @@ -97,7 +91,7 @@ LL | asm!("", in("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:99:28 + --> $DIR/bad-reg.rs:97:28 | LL | asm!("", out("v0") x); | ^ @@ -105,12 +99,12 @@ LL | asm!("", out("v0") x); = note: register class `vreg` supports these types: error: type `i32` cannot be used with this register class - --> $DIR/bad-reg.rs:102:35 + --> $DIR/bad-reg.rs:100:35 | LL | asm!("/* {} */", in(vreg) x); | ^ | = note: register class `vreg` supports these types: -error: aborting due to 18 previous errors +error: aborting due to 17 previous errors diff --git a/tests/ui/asm/riscv/bad-reg.rs b/tests/ui/asm/riscv/bad-reg.rs index 7f0fc00d5489d..7d032d277aa9b 100644 --- a/tests/ui/asm/riscv/bad-reg.rs +++ b/tests/ui/asm/riscv/bad-reg.rs @@ -38,8 +38,6 @@ fn f() { //~^ ERROR invalid register `sp`: the stack pointer cannot be used as an operand for inline asm asm!("", out("gp") _); //~^ ERROR invalid register `gp`: the global pointer cannot be used as an operand for inline asm - asm!("", out("gp") _); - //~^ ERROR invalid register `gp`: the global pointer cannot be used as an operand for inline asm asm!("", out("tp") _); //~^ ERROR invalid register `tp`: the thread pointer cannot be used as an operand for inline asm asm!("", out("zero") _); diff --git a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.stderr b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.current.stderr similarity index 88% rename from tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.stderr rename to tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.current.stderr index 3ed73918de313..0d57d9d0142d1 100644 --- a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.stderr +++ b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.current.stderr @@ -1,5 +1,5 @@ error[E0382]: use of moved value: `x` - --> $DIR/cant-see-copy-bound-from-child-rigid.rs:14:9 + --> $DIR/cant-see-copy-bound-from-child-rigid.rs:18:9 | LL | fn foo(x: T::Assoc) -> (T::Assoc, T::Assoc) | - move occurs because `x` has type `::Assoc`, which does not implement the `Copy` trait diff --git a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.next.stderr b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.next.stderr new file mode 100644 index 0000000000000..0d57d9d0142d1 --- /dev/null +++ b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.next.stderr @@ -0,0 +1,14 @@ +error[E0382]: use of moved value: `x` + --> $DIR/cant-see-copy-bound-from-child-rigid.rs:18:9 + | +LL | fn foo(x: T::Assoc) -> (T::Assoc, T::Assoc) + | - move occurs because `x` has type `::Assoc`, which does not implement the `Copy` trait +... +LL | (x, x) + | - ^ value used here after move + | | + | value moved here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0382`. diff --git a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.rs b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.rs index 6b3fd7e898d18..fe135031b3138 100644 --- a/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.rs +++ b/tests/ui/associated-type-bounds/cant-see-copy-bound-from-child-rigid.rs @@ -1,3 +1,7 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + trait Id { type This: ?Sized; } diff --git a/tests/ui/associated-type-bounds/return-type-notation/equality.current.stderr b/tests/ui/associated-type-bounds/return-type-notation/equality.current.stderr deleted file mode 100644 index 26b4d935ac7c6..0000000000000 --- a/tests/ui/associated-type-bounds/return-type-notation/equality.current.stderr +++ /dev/null @@ -1,17 +0,0 @@ -warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/equality.rs:5:12 - | -LL | #![feature(return_type_notation)] - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #109417 for more information - = note: `#[warn(incomplete_features)]` on by default - -error: return type notation is not allowed to use type equality - --> $DIR/equality.rs:14:18 - | -LL | fn test>>>() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error; 1 warning emitted - diff --git a/tests/ui/associated-type-bounds/return-type-notation/equality.next.stderr b/tests/ui/associated-type-bounds/return-type-notation/equality.next.stderr deleted file mode 100644 index 26b4d935ac7c6..0000000000000 --- a/tests/ui/associated-type-bounds/return-type-notation/equality.next.stderr +++ /dev/null @@ -1,17 +0,0 @@ -warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/equality.rs:5:12 - | -LL | #![feature(return_type_notation)] - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #109417 for more information - = note: `#[warn(incomplete_features)]` on by default - -error: return type notation is not allowed to use type equality - --> $DIR/equality.rs:14:18 - | -LL | fn test>>>() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error; 1 warning emitted - diff --git a/tests/ui/async-await/return-type-notation/issue-110963-late.next.stderr b/tests/ui/async-await/return-type-notation/issue-110963-late.next.stderr deleted file mode 100644 index 018f4f2207ae7..0000000000000 --- a/tests/ui/async-await/return-type-notation/issue-110963-late.next.stderr +++ /dev/null @@ -1,11 +0,0 @@ -warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/issue-110963-late.rs:6:12 - | -LL | #![feature(return_type_notation)] - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #109417 for more information - = note: `#[warn(incomplete_features)]` on by default - -warning: 1 warning emitted - diff --git a/tests/ui/async-await/return-type-notation/super-method-bound.next.stderr b/tests/ui/async-await/return-type-notation/super-method-bound.next.stderr deleted file mode 100644 index 5f482b6087865..0000000000000 --- a/tests/ui/async-await/return-type-notation/super-method-bound.next.stderr +++ /dev/null @@ -1,10 +0,0 @@ -warning: the feature `return_type_notation` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/super-method-bound.rs:6:31 - | -LL | #![feature(return_type_notation)] | ^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #109417 for more information - = note: `#[warn(incomplete_features)]` on by default - -warning: 1 warning emitted - diff --git a/tests/ui/borrowck/borrowck-describe-lvalue.rs b/tests/ui/borrowck/borrowck-describe-lvalue.rs index cdcff69d6e529..f3a4b382fa89f 100644 --- a/tests/ui/borrowck/borrowck-describe-lvalue.rs +++ b/tests/ui/borrowck/borrowck-describe-lvalue.rs @@ -231,7 +231,6 @@ fn main() { let x = &mut v; v[0].y; //~^ ERROR cannot use `v[_].y` because it was mutably borrowed - //~| ERROR cannot use `*v` because it was mutably borrowed drop(x); } // Field of constant index diff --git a/tests/ui/borrowck/borrowck-describe-lvalue.stderr b/tests/ui/borrowck/borrowck-describe-lvalue.stderr index 11f2e42d42b7b..666a21808d80b 100644 --- a/tests/ui/borrowck/borrowck-describe-lvalue.stderr +++ b/tests/ui/borrowck/borrowck-describe-lvalue.stderr @@ -1,5 +1,5 @@ error[E0499]: cannot borrow `x` as mutable more than once at a time - --> $DIR/borrowck-describe-lvalue.rs:254:13 + --> $DIR/borrowck-describe-lvalue.rs:253:13 | LL | let y = &mut x; | ------ first mutable borrow occurs here @@ -9,7 +9,7 @@ LL | *y = 1; | ------ first borrow later used here error[E0499]: cannot borrow `x` as mutable more than once at a time - --> $DIR/borrowck-describe-lvalue.rs:264:20 + --> $DIR/borrowck-describe-lvalue.rs:263:20 | LL | let y = &mut x; | ------ first mutable borrow occurs here @@ -19,7 +19,7 @@ LL | *y = 1; | ------ first borrow later used here error: captured variable cannot escape `FnMut` closure body - --> $DIR/borrowck-describe-lvalue.rs:262:16 + --> $DIR/borrowck-describe-lvalue.rs:261:16 | LL | let mut x = 0; | ----- variable defined here @@ -300,17 +300,6 @@ LL | S { x: F { y: ref x0, .. }, .. } => LL | drop(x); | - mutable borrow later used here -error[E0503]: cannot use `*v` because it was mutably borrowed - --> $DIR/borrowck-describe-lvalue.rs:232:9 - | -LL | let x = &mut v; - | ------ `v` is borrowed here -LL | v[0].y; - | ^^^^ use of borrowed `v` -... -LL | drop(x); - | - borrow later used here - error[E0503]: cannot use `v[_].y` because it was mutably borrowed --> $DIR/borrowck-describe-lvalue.rs:232:9 | @@ -318,12 +307,12 @@ LL | let x = &mut v; | ------ `v` is borrowed here LL | v[0].y; | ^^^^^^ use of borrowed `v` -... +LL | LL | drop(x); | - borrow later used here error[E0502]: cannot borrow `v[..].x` as immutable because it is also borrowed as mutable - --> $DIR/borrowck-describe-lvalue.rs:243:24 + --> $DIR/borrowck-describe-lvalue.rs:242:24 | LL | let x = &mut v; | ------ mutable borrow occurs here @@ -357,7 +346,7 @@ LL | drop(x); | - mutable borrow later used here error[E0382]: use of moved value: `x` - --> $DIR/borrowck-describe-lvalue.rs:274:22 + --> $DIR/borrowck-describe-lvalue.rs:273:22 | LL | drop(x); | - value moved here @@ -366,7 +355,7 @@ LL | drop(x); | = note: move occurs because `x` has type `Vec`, which does not implement the `Copy` trait -error: aborting due to 32 previous errors +error: aborting due to 31 previous errors Some errors have detailed explanations: E0382, E0499, E0502, E0503. For more information about an error, try `rustc --explain E0382`. diff --git a/tests/ui/borrowck/issue-103095.rs b/tests/ui/borrowck/issue-103095.rs index 3c29bc7615537..53587a16abf86 100644 --- a/tests/ui/borrowck/issue-103095.rs +++ b/tests/ui/borrowck/issue-103095.rs @@ -1,4 +1,7 @@ //@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver trait FnOnceForGenericRef: FnOnce(&T) -> Self::FnOutput { type FnOutput; @@ -16,10 +19,7 @@ struct Data> { impl> Data { fn new(value: T, f: D) -> Self { let output = f(&value); - Self { - value: Some(value), - output: Some(output), - } + Self { value: Some(value), output: Some(output) } } } diff --git a/tests/ui/borrowck/issue-93093.rs b/tests/ui/borrowck/issue-93093.rs index e85b296c983f4..1521b20723892 100644 --- a/tests/ui/borrowck/issue-93093.rs +++ b/tests/ui/borrowck/issue-93093.rs @@ -4,7 +4,7 @@ struct S { } impl S { async fn bar(&self) { //~ HELP consider changing this to be a mutable reference - //~| SUGGESTION &mut self + //~| SUGGESTION mut self.foo += 1; //~ ERROR cannot assign to `self.foo`, which is behind a `&` reference [E0594] } } diff --git a/tests/ui/borrowck/issue-93093.stderr b/tests/ui/borrowck/issue-93093.stderr index b6a2768b61da3..d788ce331973c 100644 --- a/tests/ui/borrowck/issue-93093.stderr +++ b/tests/ui/borrowck/issue-93093.stderr @@ -7,7 +7,7 @@ LL | self.foo += 1; help: consider changing this to be a mutable reference | LL | async fn bar(&mut self) { - | ~~~~~~~~~ + | +++ error: aborting due to 1 previous error diff --git a/tests/ui/borrowck/trait-impl-argument-difference-ice.stderr b/tests/ui/borrowck/trait-impl-argument-difference-ice.stderr index 5c70eccfbd35c..190ddeaa8f285 100644 --- a/tests/ui/borrowck/trait-impl-argument-difference-ice.stderr +++ b/tests/ui/borrowck/trait-impl-argument-difference-ice.stderr @@ -41,7 +41,7 @@ LL | let a16 = self.read_word() as u16; help: consider changing this to be a mutable reference in the `impl` method and the `trait` definition | LL | extern "C" fn read_dword(&'_ mut self) -> u16 { - | ~~~~~~~~~~~~ + | +++ error[E0596]: cannot borrow `*self` as mutable, as it is behind a `&` reference --> $DIR/trait-impl-argument-difference-ice.rs:18:19 @@ -52,7 +52,7 @@ LL | let b16 = self.read_word() as u16; help: consider changing this to be a mutable reference in the `impl` method and the `trait` definition | LL | extern "C" fn read_dword(&'_ mut self) -> u16 { - | ~~~~~~~~~~~~ + | +++ error: aborting due to 5 previous errors; 1 warning emitted diff --git a/tests/ui/cfg/disallowed-cli-cfgs.test_.stderr b/tests/ui/cfg/disallowed-cli-cfgs.test_.stderr deleted file mode 100644 index 96b5beb021062..0000000000000 --- a/tests/ui/cfg/disallowed-cli-cfgs.test_.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: unexpected `--cfg test` flag - | - = note: config `test` is only supposed to be controlled by `--test` - = note: see for more information - = note: `#[deny(unexpected_builtin_cfgs)]` on by default - -error: aborting due to 1 previous error - diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.rs b/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.rs index 3abc81e191ebf..2d22c9a856f6b 100644 --- a/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.rs +++ b/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.rs @@ -12,8 +12,7 @@ fn arrays_1() { // c will capture `arr` completely, therefore another index into the // array can't be modified here arr[1] += 10; - //~^ ERROR: cannot use `arr` because it was mutably borrowed - //~| ERROR: cannot use `arr[_]` because it was mutably borrowed + //~^ ERROR: cannot use `arr[_]` because it was mutably borrowed c(); } @@ -55,8 +54,7 @@ fn arrays_4() { // c will capture `arr` completely, therefore we cannot borrow another index // into the array. println!("{}", arr[3]); - //~^ ERROR: cannot use `arr` because it was mutably borrowed - //~| ERROR: cannot borrow `arr[_]` as immutable because it is also borrowed as mutable + //~^ ERROR: cannot borrow `arr[_]` as immutable because it is also borrowed as mutable c(); } diff --git a/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr b/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr index 9e5200ef34b54..97ecdfab82059 100644 --- a/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr +++ b/tests/ui/closures/2229_closure_analysis/diagnostics/arrays.stderr @@ -1,17 +1,3 @@ -error[E0503]: cannot use `arr` because it was mutably borrowed - --> $DIR/arrays.rs:14:5 - | -LL | let mut c = || { - | -- `arr` is borrowed here -LL | arr[0] += 10; - | --- borrow occurs due to use of `arr` in closure -... -LL | arr[1] += 10; - | ^^^^^^ use of borrowed `arr` -... -LL | c(); - | - borrow later used here - error[E0503]: cannot use `arr[_]` because it was mutably borrowed --> $DIR/arrays.rs:14:5 | @@ -22,12 +8,12 @@ LL | arr[0] += 10; ... LL | arr[1] += 10; | ^^^^^^^^^^^^ use of borrowed `arr` -... +LL | LL | c(); | - borrow later used here error[E0506]: cannot assign to `arr[_]` because it is borrowed - --> $DIR/arrays.rs:29:5 + --> $DIR/arrays.rs:28:5 | LL | let c = || { | -- `arr[_]` is borrowed here @@ -41,7 +27,7 @@ LL | c(); | - borrow later used here error[E0506]: cannot assign to `arr[_]` because it is borrowed - --> $DIR/arrays.rs:43:5 + --> $DIR/arrays.rs:42:5 | LL | let c = || { | -- `arr[_]` is borrowed here @@ -54,22 +40,8 @@ LL | LL | c(); | - borrow later used here -error[E0503]: cannot use `arr` because it was mutably borrowed - --> $DIR/arrays.rs:57:20 - | -LL | let mut c = || { - | -- `arr` is borrowed here -LL | arr[1] += 10; - | --- borrow occurs due to use of `arr` in closure -... -LL | println!("{}", arr[3]); - | ^^^^^^ use of borrowed `arr` -... -LL | c(); - | - borrow later used here - error[E0502]: cannot borrow `arr[_]` as immutable because it is also borrowed as mutable - --> $DIR/arrays.rs:57:20 + --> $DIR/arrays.rs:56:20 | LL | let mut c = || { | -- mutable borrow occurs here @@ -85,7 +57,7 @@ LL | c(); = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0502]: cannot borrow `arr` as immutable because it is also borrowed as mutable - --> $DIR/arrays.rs:73:24 + --> $DIR/arrays.rs:71:24 | LL | let mut c = || { | -- mutable borrow occurs here @@ -98,7 +70,7 @@ LL | println!("{:#?}", &arr[3..2]); LL | c(); | - mutable borrow later used here -error: aborting due to 7 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0502, E0503, E0506. For more information about an error, try `rustc --explain E0502`. diff --git a/tests/ui/closures/closure-expected-type/expect-region-supply-region-2.polonius.stderr b/tests/ui/closures/closure-expected-type/expect-region-supply-region-2.polonius.stderr deleted file mode 100644 index 8846ccef34e2b..0000000000000 --- a/tests/ui/closures/closure-expected-type/expect-region-supply-region-2.polonius.stderr +++ /dev/null @@ -1,37 +0,0 @@ -error: lifetime may not live long enough - --> $DIR/expect-region-supply-region-2.rs:14:30 - | -LL | fn expect_bound_supply_named<'x>() { - | -- lifetime `'x` defined here -... -LL | closure_expecting_bound(|x: &'x u32| { - | ^ - let's call the lifetime of this reference `'1` - | | - | requires that `'1` must outlive `'x` - -error[E0521]: borrowed data escapes outside of closure - --> $DIR/expect-region-supply-region-2.rs:20:9 - | -LL | let mut f: Option<&u32> = None; - | ----- `f` declared here, outside of the closure body -... -LL | closure_expecting_bound(|x: &'x u32| { - | - `x` is a reference that is only valid in the closure body -... -LL | f = Some(x); - | ^^^^^^^^^^^ `x` escapes the closure body here - -error: lifetime may not live long enough - --> $DIR/expect-region-supply-region-2.rs:14:30 - | -LL | fn expect_bound_supply_named<'x>() { - | -- lifetime `'x` defined here -... -LL | closure_expecting_bound(|x: &'x u32| { - | ^ requires that `'x` must outlive `'static` - | - = help: consider replacing `'x` with `'static` - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0521`. diff --git a/tests/ui/closures/supertrait-hint-references-assoc-ty.rs b/tests/ui/closures/supertrait-hint-references-assoc-ty.rs index fa74ffc5bec51..b6a1685cb7206 100644 --- a/tests/ui/closures/supertrait-hint-references-assoc-ty.rs +++ b/tests/ui/closures/supertrait-hint-references-assoc-ty.rs @@ -1,4 +1,7 @@ //@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver pub trait Fn0: Fn(i32) -> Self::Out { type Out; diff --git a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.stderr b/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.stderr deleted file mode 100644 index 77379f7049d02..0000000000000 --- a/tests/ui/cmse-nonsecure/cmse-nonsecure-entry/trustzone-only.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0570]: `"C-cmse-nonsecure-entry"` is not a supported ABI for the current target - --> $DIR/trustzone-only.rs:5:1 - | -LL | pub extern "C-cmse-nonsecure-entry" fn entry_function(input: u32) -> u32 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0570`. diff --git a/tests/ui/const-generics/bad-subst-const-kind.stderr b/tests/ui/const-generics/bad-subst-const-kind.stderr index 5c8d9c9036356..b360526964255 100644 --- a/tests/ui/const-generics/bad-subst-const-kind.stderr +++ b/tests/ui/const-generics/bad-subst-const-kind.stderr @@ -3,6 +3,8 @@ error: the constant `N` is not of type `usize` | LL | impl Q for [u8; N] { | ^^^^^^^ expected `usize`, found `u64` + | + = note: the length of array `[u8; N]` must be type `usize` error: the constant `13` is not of type `u64` --> $DIR/bad-subst-const-kind.rs:13:24 diff --git a/tests/ui/const-generics/generic_const_exprs/type_mismatch.stderr b/tests/ui/const-generics/generic_const_exprs/type_mismatch.stderr index e03580ec007ca..7cb67252da528 100644 --- a/tests/ui/const-generics/generic_const_exprs/type_mismatch.stderr +++ b/tests/ui/const-generics/generic_const_exprs/type_mismatch.stderr @@ -3,6 +3,8 @@ error: the constant `N` is not of type `usize` | LL | impl Q for [u8; N] {} | ^^^^^^^ expected `usize`, found `u64` + | + = note: the length of array `[u8; N]` must be type `usize` error[E0046]: not all trait items implemented, missing: `ASSOC` --> $DIR/type_mismatch.rs:8:1 diff --git a/tests/ui/const-generics/issues/index_array_bad_type.rs b/tests/ui/const-generics/issues/index_array_bad_type.rs new file mode 100644 index 0000000000000..91b89cd3fff3c --- /dev/null +++ b/tests/ui/const-generics/issues/index_array_bad_type.rs @@ -0,0 +1,13 @@ +struct Struct(pub [u8; N]); +//~^ ERROR the constant `N` is not of type `usize` + +pub fn function(value: Struct<3>) -> u8 { + value.0[0] + //~^ ERROR the constant `3` is not of type `usize` + + // FIXME(const_generics): Ideally we wouldn't report the above error + // b/c `Struct<_>` is never well formed, but I'd rather report too many + // errors rather than ICE the compiler. +} + +fn main() {} diff --git a/tests/ui/const-generics/issues/index_array_bad_type.stderr b/tests/ui/const-generics/issues/index_array_bad_type.stderr new file mode 100644 index 0000000000000..ceea097337764 --- /dev/null +++ b/tests/ui/const-generics/issues/index_array_bad_type.stderr @@ -0,0 +1,18 @@ +error: the constant `N` is not of type `usize` + --> $DIR/index_array_bad_type.rs:1:34 + | +LL | struct Struct(pub [u8; N]); + | ^^^^^^^ expected `usize`, found `i128` + | + = note: the length of array `[u8; N]` must be type `usize` + +error: the constant `3` is not of type `usize` + --> $DIR/index_array_bad_type.rs:5:5 + | +LL | value.0[0] + | ^^^^^^^ expected `usize`, found `i128` + | + = note: the length of array `[u8; 3]` must be type `usize` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/const-generics/transmute-fail.stderr b/tests/ui/const-generics/transmute-fail.stderr index 978a9744e88aa..0e26daa3a0f1e 100644 --- a/tests/ui/const-generics/transmute-fail.stderr +++ b/tests/ui/const-generics/transmute-fail.stderr @@ -3,6 +3,8 @@ error: the constant `W` is not of type `usize` | LL | fn bar(v: [[u32; H]; W]) -> [[u32; W]; H] { | ^^^^^^^^^^^^^ expected `usize`, found `bool` + | + = note: the length of array `[[u32; H]; W]` must be type `usize` error[E0512]: cannot transmute between types of different sizes, or dependently-sized types --> $DIR/transmute-fail.rs:11:9 @@ -18,6 +20,8 @@ error: the constant `W` is not of type `usize` | LL | std::mem::transmute(v) | ^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool` + | + = note: the length of array `[[u32; H]; W]` must be type `usize` error[E0512]: cannot transmute between types of different sizes, or dependently-sized types --> $DIR/transmute-fail.rs:26:9 diff --git a/tests/ui/const-generics/type_mismatch.stderr b/tests/ui/const-generics/type_mismatch.stderr index d1bb5c1242f02..bd169ed2ec8f5 100644 --- a/tests/ui/const-generics/type_mismatch.stderr +++ b/tests/ui/const-generics/type_mismatch.stderr @@ -3,6 +3,8 @@ error: the constant `N` is not of type `usize` | LL | fn bar() -> [u8; N] {} | ^^^^^^^ expected `usize`, found `u8` + | + = note: the length of array `[u8; N]` must be type `usize` error: the constant `N` is not of type `u8` --> $DIR/type_mismatch.rs:2:11 diff --git a/tests/ui/consts/bad-array-size-in-type-err.stderr b/tests/ui/consts/bad-array-size-in-type-err.stderr index 25d14d80c3ec4..c3ff216432eeb 100644 --- a/tests/ui/consts/bad-array-size-in-type-err.stderr +++ b/tests/ui/consts/bad-array-size-in-type-err.stderr @@ -3,6 +3,8 @@ error: the constant `N` is not of type `usize` | LL | arr: [i32; N], | ^^^^^^^^ expected `usize`, found `u8` + | + = note: the length of array `[i32; N]` must be type `usize` error[E0308]: mismatched types --> $DIR/bad-array-size-in-type-err.rs:7:38 @@ -15,6 +17,8 @@ error: the constant `2` is not of type `usize` | LL | let _ = BadArraySize::<2> { arr: [0, 0, 0] }; | ^^^^^^^^^ expected `usize`, found `u8` + | + = note: the length of array `[i32; 2]` must be type `usize` error: aborting due to 3 previous errors diff --git a/tests/ui/consts/const-eval/ub-nonnull.chalk.64bit.stderr b/tests/ui/consts/const-eval/ub-nonnull.chalk.64bit.stderr deleted file mode 100644 index fef6c92af9857..0000000000000 --- a/tests/ui/consts/const-eval/ub-nonnull.chalk.64bit.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0284]: type annotations needed: cannot satisfy `>::Output == _` - --> $DIR/ub-nonnull.rs:19:30 - | -LL | let out_of_bounds_ptr = &ptr[255]; - | ^^^^^^^^ cannot satisfy `>::Output == _` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/consts/const-eval/ub-wide-ptr.chalk.64bit.stderr b/tests/ui/consts/const-eval/ub-wide-ptr.chalk.64bit.stderr deleted file mode 100644 index 533db90ce6c9e..0000000000000 --- a/tests/ui/consts/const-eval/ub-wide-ptr.chalk.64bit.stderr +++ /dev/null @@ -1,9 +0,0 @@ -error[E0282]: type annotations needed - --> $DIR/ub-wide-ptr.rs:90:67 - | -LL | const MYSLICE_SUFFIX_BAD: &MySliceBool = &MySlice(true, [unsafe { mem::transmute(3u8) }]); - | ^^^^^^^^^^^^^^ cannot infer type for type parameter `U` declared on the function `transmute` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/consts/issue-65348.rs b/tests/ui/consts/issue-65348.rs index 1443fcbe1c1ce..0d12da3926cd6 100644 --- a/tests/ui/consts/issue-65348.rs +++ b/tests/ui/consts/issue-65348.rs @@ -9,15 +9,17 @@ impl Generic { } pub const fn array() -> &'static T { - #[allow(unconditional_panic)] + #[expect(unconditional_panic)] &Generic::::ARRAY[0] } pub const fn newtype_array() -> &'static T { + #[expect(unconditional_panic)] &Generic::::NEWTYPE_ARRAY.0[0] } pub const fn array_field() -> &'static T { + #[expect(unconditional_panic)] &(Generic::::ARRAY_FIELD.0).1[0] } diff --git a/tests/ui/consts/write_to_mut_ref_dest.stock.stderr b/tests/ui/consts/write_to_mut_ref_dest.stock.stderr deleted file mode 100644 index 688d48ec707ca..0000000000000 --- a/tests/ui/consts/write_to_mut_ref_dest.stock.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error[E0658]: mutable references are not allowed in constants - --> $DIR/write_to_mut_ref_dest.rs:11:27 - | -LL | let b: *mut u32 = &mut a; - | ^^^^^^ - | - = note: see issue #57349 for more information - = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: dereferencing raw mutable pointers in constants is unstable - --> $DIR/write_to_mut_ref_dest.rs:12:18 - | -LL | unsafe { *b = 5; } - | ^^^^^^ - | - = note: see issue #57349 for more information - = help: add `#![feature(const_mut_refs)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/coroutine/coroutine-region-requirements.migrate.stderr b/tests/ui/coroutine/coroutine-region-requirements.migrate.stderr deleted file mode 100644 index cfee8fc44fe09..0000000000000 --- a/tests/ui/coroutine/coroutine-region-requirements.migrate.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0621]: explicit lifetime required in the type of `x` - --> $DIR/generator-region-requirements.rs:16:51 - | -LL | fn dangle(x: &mut i32) -> &'static mut i32 { - | -------- help: add explicit lifetime `'static` to the type of `x`: `&'static mut i32` -... -LL | GeneratorState::Complete(c) => return c, - | ^ lifetime `'static` required - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0621`. diff --git a/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.rs b/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.rs new file mode 100644 index 0000000000000..fa1663d49eb2a --- /dev/null +++ b/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.rs @@ -0,0 +1,9 @@ +//@ edition: 2021 + +fn foo() {} + +fn main() { + let _: Vec<(&str, fn())> = [("foo", foo)].into_iter().collect(); //~ ERROR + let _: Vec = [foo].into_iter().collect(); //~ ERROR + let _: Vec = Vec::from([foo]); //~ ERROR +} diff --git a/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.stderr b/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.stderr new file mode 100644 index 0000000000000..d069d39514dcd --- /dev/null +++ b/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.stderr @@ -0,0 +1,59 @@ +error[E0277]: a value of type `Vec<(&str, fn())>` cannot be built from an iterator over elements of type `(&str, fn() {foo})` + --> $DIR/casting-fn-item-to-fn-pointer.rs:6:59 + | +LL | let _: Vec<(&str, fn())> = [("foo", foo)].into_iter().collect(); + | ^^^^^^^ value of type `Vec<(&str, fn())>` cannot be built from `std::iter::Iterator` + | + = help: the trait `FromIterator<(&_, fn() {foo})>` is not implemented for `Vec<(&str, fn())>` + but trait `FromIterator<(&_, fn())>` is implemented for it + = help: for that trait implementation, expected `fn()`, found `fn() {foo}` + = note: fn items are distinct from fn pointers + = help: consider casting the fn item to a fn pointer: `foo as fn()` +note: the method call chain might not have had the expected associated types + --> $DIR/casting-fn-item-to-fn-pointer.rs:6:47 + | +LL | let _: Vec<(&str, fn())> = [("foo", foo)].into_iter().collect(); + | -------------- ^^^^^^^^^^^ `Iterator::Item` is `(&str, fn() {foo})` here + | | + | this expression has type `[(&str, fn() {foo}); 1]` +note: required by a bound in `collect` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + +error[E0277]: a value of type `Vec` cannot be built from an iterator over elements of type `fn() {foo}` + --> $DIR/casting-fn-item-to-fn-pointer.rs:7:42 + | +LL | let _: Vec = [foo].into_iter().collect(); + | ^^^^^^^ value of type `Vec` cannot be built from `std::iter::Iterator` + | + = help: the trait `FromIterator` is not implemented for `Vec` + but trait `FromIterator` is implemented for it + = help: for that trait implementation, expected `fn()`, found `fn() {foo}` + = note: fn items are distinct from fn pointers + = help: consider casting the fn item to a fn pointer: `foo as fn()` +note: the method call chain might not have had the expected associated types + --> $DIR/casting-fn-item-to-fn-pointer.rs:7:30 + | +LL | let _: Vec = [foo].into_iter().collect(); + | ----- ^^^^^^^^^^^ `Iterator::Item` is `fn() {foo}` here + | | + | this expression has type `[fn() {foo}; 1]` +note: required by a bound in `collect` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + +error[E0308]: mismatched types + --> $DIR/casting-fn-item-to-fn-pointer.rs:8:24 + | +LL | let _: Vec = Vec::from([foo]); + | --------- ^^^^^^^^^^^^^^^^ expected `Vec`, found `Vec` + | | + | expected due to this + | + = note: expected struct `Vec` + found struct `Vec` + = note: fn items are distinct from fn pointers + = help: consider casting the fn item to a fn pointer: `foo as fn()` + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0277, E0308. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/did_you_mean/issue-38147-1.stderr b/tests/ui/did_you_mean/issue-38147-1.stderr index a0392113ab155..6def86e4ba8f1 100644 --- a/tests/ui/did_you_mean/issue-38147-1.stderr +++ b/tests/ui/did_you_mean/issue-38147-1.stderr @@ -7,7 +7,7 @@ LL | self.s.push('x'); help: consider changing this to be a mutable reference | LL | fn f(&mut self) { - | ~~~~~~~~~ + | +++ error: aborting due to 1 previous error diff --git a/tests/ui/did_you_mean/issue-39544.stderr b/tests/ui/did_you_mean/issue-39544.stderr index 8ccb4cbb0c167..62dc027e31f2d 100644 --- a/tests/ui/did_you_mean/issue-39544.stderr +++ b/tests/ui/did_you_mean/issue-39544.stderr @@ -18,7 +18,7 @@ LL | let _ = &mut self.x; help: consider changing this to be a mutable reference | LL | fn foo<'z>(&'z mut self) { - | ~~~~~~~~~~~~ + | +++ error[E0596]: cannot borrow `self.x` as mutable, as it is behind a `&` reference --> $DIR/issue-39544.rs:20:17 @@ -29,7 +29,7 @@ LL | let _ = &mut self.x; help: consider changing this to be a mutable reference | LL | fn foo1(&mut self, other: &Z) { - | ~~~~~~~~~ + | +++ error[E0596]: cannot borrow `other.x` as mutable, as it is behind a `&` reference --> $DIR/issue-39544.rs:21:17 @@ -51,7 +51,7 @@ LL | let _ = &mut self.x; help: consider changing this to be a mutable reference | LL | fn foo2<'a>(&'a mut self, other: &Z) { - | ~~~~~~~~~~~~ + | +++ error[E0596]: cannot borrow `other.x` as mutable, as it is behind a `&` reference --> $DIR/issue-39544.rs:26:17 diff --git a/tests/ui/generic-associated-types/gat-in-trait-path.base.stderr b/tests/ui/generic-associated-types/gat-in-trait-path.base.stderr deleted file mode 100644 index b2b569e6261b5..0000000000000 --- a/tests/ui/generic-associated-types/gat-in-trait-path.base.stderr +++ /dev/null @@ -1,52 +0,0 @@ -error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/gat-in-trait-path.rs:26:17 - | -LL | fn f(_arg : Box Foo = &'a ()>>) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Foo` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/gat-in-trait-path.rs:10:10 - | -LL | trait Foo { - | --- this trait is not dyn compatible... -LL | type A<'a> where Self: 'a; - | ^ ...because it contains the generic associated type `A` - = help: consider moving `A` to another trait - -error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/gat-in-trait-path.rs:32:5 - | -LL | f(Box::new(foo)); - | ^^^^^^^^^^^^^ `Foo` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/gat-in-trait-path.rs:10:10 - | -LL | trait Foo { - | --- this trait is not dyn compatible... -LL | type A<'a> where Self: 'a; - | ^ ...because it contains the generic associated type `A` - = help: consider moving `A` to another trait - -error[E0038]: the trait `Foo` is not dyn compatible - --> $DIR/gat-in-trait-path.rs:32:5 - | -LL | f(Box::new(foo)); - | ^^^^^^^^^^^^^ `Foo` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/gat-in-trait-path.rs:10:10 - | -LL | trait Foo { - | --- this trait is not dyn compatible... -LL | type A<'a> where Self: 'a; - | ^ ...because it contains the generic associated type `A` - = help: consider moving `A` to another trait - = note: required for the cast from `Box>` to `Box<(dyn Foo = &'a ()> + 'static)>` - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/issue-67510-pass.base.stderr b/tests/ui/generic-associated-types/issue-67510-pass.base.stderr deleted file mode 100644 index 5630894896946..0000000000000 --- a/tests/ui/generic-associated-types/issue-67510-pass.base.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0038]: the trait `X` is not dyn compatible - --> $DIR/issue-67510-pass.rs:12:23 - | -LL | fn _func1<'a>(_x: Box=&'a ()>>) {} - | ^^^^^^^^^^^^^^^^^^^ `X` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/issue-67510-pass.rs:9:10 - | -LL | trait X { - | - this trait is not dyn compatible... -LL | type Y<'a>; - | ^ ...because it contains the generic associated type `Y` - = help: consider moving `Y` to another trait - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/issue-76535.base.stderr b/tests/ui/generic-associated-types/issue-76535.base.stderr deleted file mode 100644 index b503fad2d84fe..0000000000000 --- a/tests/ui/generic-associated-types/issue-76535.base.stderr +++ /dev/null @@ -1,57 +0,0 @@ -error[E0107]: missing generics for associated type `SuperTrait::SubType` - --> $DIR/issue-76535.rs:39:33 - | -LL | let sub: Box> = Box::new(SuperStruct::new(0)); - | ^^^^^^^ expected 1 lifetime argument - | -note: associated type defined here, with 1 lifetime parameter: `'a` - --> $DIR/issue-76535.rs:9:10 - | -LL | type SubType<'a>: SubTrait where Self: 'a; - | ^^^^^^^ -- -help: add missing lifetime argument - | -LL | let sub: Box = SubStruct>> = Box::new(SuperStruct::new(0)); - | ++++ - -error[E0038]: the trait `SuperTrait` is not dyn compatible - --> $DIR/issue-76535.rs:39:14 - | -LL | let sub: Box> = Box::new(SuperStruct::new(0)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `SuperTrait` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/issue-76535.rs:9:10 - | -LL | pub trait SuperTrait { - | ---------- this trait is not dyn compatible... -LL | type SubType<'a>: SubTrait where Self: 'a; - | ^^^^^^^ ...because it contains the generic associated type `SubType` - = help: consider moving `SubType` to another trait - = help: only type `SuperStruct` implements `SuperTrait` within this crate. Consider using it directly instead. - = note: `SuperTrait` may be implemented in other crates; if you want to support your users passing their own types here, you can't refer to a specific type - -error[E0038]: the trait `SuperTrait` is not dyn compatible - --> $DIR/issue-76535.rs:39:57 - | -LL | let sub: Box> = Box::new(SuperStruct::new(0)); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `SuperTrait` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/issue-76535.rs:9:10 - | -LL | pub trait SuperTrait { - | ---------- this trait is not dyn compatible... -LL | type SubType<'a>: SubTrait where Self: 'a; - | ^^^^^^^ ...because it contains the generic associated type `SubType` - = help: consider moving `SubType` to another trait - = help: only type `SuperStruct` implements `SuperTrait` within this crate. Consider using it directly instead. - = note: `SuperTrait` may be implemented in other crates; if you want to support your users passing their own types here, you can't refer to a specific type - = note: required for the cast from `Box` to `Box = SubStruct<'_>>>` - -error: aborting due to 3 previous errors - -Some errors have detailed explanations: E0038, E0107. -For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/issue-76535.extended.stderr b/tests/ui/generic-associated-types/issue-76535.extended.stderr deleted file mode 100644 index f6fe8b16902b1..0000000000000 --- a/tests/ui/generic-associated-types/issue-76535.extended.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0107]: missing generics for associated type `SuperTrait::SubType` - --> $DIR/issue-76535.rs:39:33 - | -LL | let sub: Box> = Box::new(SuperStruct::new(0)); - | ^^^^^^^ expected 1 lifetime argument - | -note: associated type defined here, with 1 lifetime parameter: `'a` - --> $DIR/issue-76535.rs:9:10 - | -LL | type SubType<'a>: SubTrait where Self: 'a; - | ^^^^^^^ -- -help: add missing lifetime argument - | -LL | let sub: Box = SubStruct>> = Box::new(SuperStruct::new(0)); - | ++++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0107`. diff --git a/tests/ui/generic-associated-types/issue-78671.base.stderr b/tests/ui/generic-associated-types/issue-78671.base.stderr deleted file mode 100644 index 9bfe8c0b95612..0000000000000 --- a/tests/ui/generic-associated-types/issue-78671.base.stderr +++ /dev/null @@ -1,36 +0,0 @@ -error[E0107]: missing generics for associated type `CollectionFamily::Member` - --> $DIR/issue-78671.rs:10:47 - | -LL | Box::new(Family) as &dyn CollectionFamily - | ^^^^^^ expected 1 generic argument - | -note: associated type defined here, with 1 generic parameter: `T` - --> $DIR/issue-78671.rs:7:10 - | -LL | type Member; - | ^^^^^^ - -help: add missing generic argument - | -LL | Box::new(Family) as &dyn CollectionFamily=usize> - | +++ - -error[E0038]: the trait `CollectionFamily` is not dyn compatible - --> $DIR/issue-78671.rs:10:25 - | -LL | Box::new(Family) as &dyn CollectionFamily - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `CollectionFamily` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/issue-78671.rs:7:10 - | -LL | trait CollectionFamily { - | ---------------- this trait is not dyn compatible... -LL | type Member; - | ^^^^^^ ...because it contains the generic associated type `Member` - = help: consider moving `Member` to another trait - -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0038, E0107. -For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/issue-78671.extended.stderr b/tests/ui/generic-associated-types/issue-78671.extended.stderr deleted file mode 100644 index a5d56256d283b..0000000000000 --- a/tests/ui/generic-associated-types/issue-78671.extended.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0107]: missing generics for associated type `CollectionFamily::Member` - --> $DIR/issue-78671.rs:10:47 - | -LL | Box::new(Family) as &dyn CollectionFamily - | ^^^^^^ expected 1 generic argument - | -note: associated type defined here, with 1 generic parameter: `T` - --> $DIR/issue-78671.rs:7:10 - | -LL | type Member; - | ^^^^^^ - -help: add missing generic argument - | -LL | Box::new(Family) as &dyn CollectionFamily=usize> - | +++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0107`. diff --git a/tests/ui/generic-associated-types/issue-79422.base.stderr b/tests/ui/generic-associated-types/issue-79422.base.stderr deleted file mode 100644 index c3de2b71762e4..0000000000000 --- a/tests/ui/generic-associated-types/issue-79422.base.stderr +++ /dev/null @@ -1,53 +0,0 @@ -error[E0107]: missing generics for associated type `MapLike::VRefCont` - --> $DIR/issue-79422.rs:47:36 - | -LL | as Box>>; - | ^^^^^^^^ expected 1 lifetime argument - | -note: associated type defined here, with 1 lifetime parameter: `'a` - --> $DIR/issue-79422.rs:23:10 - | -LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a; - | ^^^^^^^^ -- -help: add missing lifetime argument - | -LL | as Box = dyn RefCont<'_, u8>>>; - | ++++ - -error[E0038]: the trait `MapLike` is not dyn compatible - --> $DIR/issue-79422.rs:47:12 - | -LL | as Box>>; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `MapLike` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/issue-79422.rs:23:10 - | -LL | trait MapLike { - | ------- this trait is not dyn compatible... -LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a; - | ^^^^^^^^ ...because it contains the generic associated type `VRefCont` - = help: consider moving `VRefCont` to another trait - -error[E0038]: the trait `MapLike` is not dyn compatible - --> $DIR/issue-79422.rs:44:13 - | -LL | let m = Box::new(std::collections::BTreeMap::::new()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `MapLike` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/issue-79422.rs:23:10 - | -LL | trait MapLike { - | ------- this trait is not dyn compatible... -LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a; - | ^^^^^^^^ ...because it contains the generic associated type `VRefCont` - = help: consider moving `VRefCont` to another trait - = note: required for the cast from `Box>` to `Box = (dyn RefCont<'_, u8> + 'static)>>` - -error: aborting due to 3 previous errors - -Some errors have detailed explanations: E0038, E0107. -For more information about an error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/issue-79422.extended.stderr b/tests/ui/generic-associated-types/issue-79422.extended.stderr deleted file mode 100644 index 031f8d8d851a1..0000000000000 --- a/tests/ui/generic-associated-types/issue-79422.extended.stderr +++ /dev/null @@ -1,36 +0,0 @@ -error[E0107]: missing generics for associated type `MapLike::VRefCont` - --> $DIR/issue-79422.rs:47:36 - | -LL | as Box>>; - | ^^^^^^^^ expected 1 lifetime argument - | -note: associated type defined here, with 1 lifetime parameter: `'a` - --> $DIR/issue-79422.rs:23:10 - | -LL | type VRefCont<'a>: RefCont<'a, V> where Self: 'a; - | ^^^^^^^^ -- -help: add missing lifetime argument - | -LL | as Box = dyn RefCont<'_, u8>>>; - | ++++ - -error[E0271]: type mismatch resolving ` as MapLike>::VRefCont<'_> == dyn RefCont<'_, u8>` - --> $DIR/issue-79422.rs:44:13 - | -LL | let m = Box::new(std::collections::BTreeMap::::new()) - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type mismatch resolving ` as MapLike>::VRefCont<'_> == dyn RefCont<'_, u8>` - | -note: expected this to be `(dyn RefCont<'_, u8> + 'static)` - --> $DIR/issue-79422.rs:28:25 - | -LL | type VRefCont<'a> = &'a V where Self: 'a; - | ^^^^^ - = note: expected trait object `(dyn RefCont<'_, u8> + 'static)` - found reference `&u8` - = help: `&u8` implements `RefCont` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well - = note: required for the cast from `Box>` to `Box = (dyn RefCont<'_, u8> + 'static)>>` - -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0107, E0271. -For more information about an error, try `rustc --explain E0107`. diff --git a/tests/ui/generic-associated-types/issue-90014-tait2.next-solver.stderr b/tests/ui/generic-associated-types/issue-90014-tait2.next-solver.stderr deleted file mode 100644 index 85c5dad7fc01a..0000000000000 --- a/tests/ui/generic-associated-types/issue-90014-tait2.next-solver.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/issue-90014-tait2.rs:27:9 - | -LL | fn make_fut(&self) -> Box Trait<'a, Thing = Fut<'a>>> { - | ------------------------------------------- expected `Box<(dyn for<'a> Trait<'a, Thing = Fut<'a>> + 'static)>` because of return type -LL | Box::new((async { () },)) - | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Box>>`, found `Box<(...,)>` - | - = note: expected struct `Box<(dyn for<'a> Trait<'a, Thing = Fut<'a>> + 'static)>` - found struct `Box<({async block@$DIR/issue-90014-tait2.rs:27:19: 27:31},)>` - = help: `({async block@$DIR/issue-90014-tait2.rs:27:19: 27:31},)` implements `Trait` so you could box the found value and coerce it to the trait object `Box`, you will have to change the expected type as well - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/generic-associated-types/issue-91139.migrate.stderr b/tests/ui/generic-associated-types/issue-91139.migrate.stderr deleted file mode 100644 index e3b658558e328..0000000000000 --- a/tests/ui/generic-associated-types/issue-91139.migrate.stderr +++ /dev/null @@ -1,7 +0,0 @@ -error: expected identifier, found `<<` - --> $DIR/issue-91139.rs:1:1 - | - | ^^ expected identifier - -error: aborting due to 1 previous error - diff --git a/tests/ui/generic-associated-types/issue-92096.migrate.stderr b/tests/ui/generic-associated-types/issue-92096.migrate.stderr deleted file mode 100644 index ce1fd6dd9831f..0000000000000 --- a/tests/ui/generic-associated-types/issue-92096.migrate.stderr +++ /dev/null @@ -1,24 +0,0 @@ -error[E0311]: the parameter type `C` may not live long enough - --> $DIR/issue-92096.rs:19:33 - | -LL | fn call_connect(c: &'_ C) -> impl '_ + Future + Send - | ^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `C` will meet its required lifetime bounds - | -help: consider adding an explicit lifetime bound... - | -LL | C: Client + Send + Sync + 'a, - | ++++ - -error[E0311]: the parameter type `C` may not live long enough - --> $DIR/issue-92096.rs:19:33 - | -LL | fn call_connect(c: &'_ C) -> impl '_ + Future + Send - | ^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `C` will meet its required lifetime bounds - | -help: consider adding an explicit lifetime bound... - | -LL | C: Client + Send + Sync + 'a, - | ++++ - -error: aborting due to 2 previous errors - diff --git a/tests/ui/generic-associated-types/trait-objects.base.stderr b/tests/ui/generic-associated-types/trait-objects.base.stderr deleted file mode 100644 index fe9ab165d4af4..0000000000000 --- a/tests/ui/generic-associated-types/trait-objects.base.stderr +++ /dev/null @@ -1,51 +0,0 @@ -error[E0038]: the trait `StreamingIterator` is not dyn compatible - --> $DIR/trait-objects.rs:13:21 - | -LL | fn min_size(x: &mut dyn for<'a> StreamingIterator = &'a i32>) -> usize { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `StreamingIterator` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/trait-objects.rs:7:10 - | -LL | trait StreamingIterator { - | ----------------- this trait is not dyn compatible... -LL | type Item<'a> where Self: 'a; - | ^^^^ ...because it contains the generic associated type `Item` - = help: consider moving `Item` to another trait - -error[E0038]: the trait `StreamingIterator` is not dyn compatible - --> $DIR/trait-objects.rs:15:7 - | -LL | x.size_hint().0 - | ^^^^^^^^^ `StreamingIterator` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/trait-objects.rs:7:10 - | -LL | trait StreamingIterator { - | ----------------- this trait is not dyn compatible... -LL | type Item<'a> where Self: 'a; - | ^^^^ ...because it contains the generic associated type `Item` - = help: consider moving `Item` to another trait - -error[E0038]: the trait `StreamingIterator` is not dyn compatible - --> $DIR/trait-objects.rs:15:5 - | -LL | x.size_hint().0 - | ^^^^^^^^^^^^^ `StreamingIterator` is not dyn compatible - | -note: for a trait to be dyn compatible it needs to allow building a vtable - for more information, visit - --> $DIR/trait-objects.rs:7:10 - | -LL | trait StreamingIterator { - | ----------------- this trait is not dyn compatible... -LL | type Item<'a> where Self: 'a; - | ^^^^ ...because it contains the generic associated type `Item` - = help: consider moving `Item` to another trait - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0038`. diff --git a/tests/ui/generic-associated-types/trait-objects.extended.stderr b/tests/ui/generic-associated-types/trait-objects.extended.stderr deleted file mode 100644 index 9f9418e20b9dc..0000000000000 --- a/tests/ui/generic-associated-types/trait-objects.extended.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0521]: borrowed data escapes outside of function - --> $DIR/trait-objects.rs:15:5 - | -LL | fn min_size(x: &mut dyn for<'a> StreamingIterator = &'a i32>) -> usize { - | - - let's call the lifetime of this reference `'1` - | | - | `x` is a reference that is only valid in the function body -LL | -LL | x.size_hint().0 - | ^^^^^^^^^^^^^ - | | - | `x` escapes the function body here - | argument requires that `'1` must outlive `'static` - | - = note: due to current limitations in the borrow checker, this implies a `'static` lifetime - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0521`. diff --git a/tests/ui/generic-const-items/def-site-eval.fail.stderr b/tests/ui/generic-const-items/def-site-eval.fail.stderr new file mode 100644 index 0000000000000..22a5f29169776 --- /dev/null +++ b/tests/ui/generic-const-items/def-site-eval.fail.stderr @@ -0,0 +1,11 @@ +error[E0080]: evaluation of `_::<'_>` failed + --> $DIR/def-site-eval.rs:14:20 + | +LL | const _<'_a>: () = panic!(); + | ^^^^^^^^ the evaluated program panicked at 'explicit panic', $DIR/def-site-eval.rs:14:20 + | + = note: this error originates in the macro `$crate::panic::panic_2015` which comes from the expansion of the macro `panic` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`. diff --git a/tests/ui/generic-const-items/def-site-eval.rs b/tests/ui/generic-const-items/def-site-eval.rs new file mode 100644 index 0000000000000..3ed7f96aed02c --- /dev/null +++ b/tests/ui/generic-const-items/def-site-eval.rs @@ -0,0 +1,16 @@ +//! Test that we only evaluate free const items (their def site to be clear) +//! whose generics don't require monomorphization. +#![feature(generic_const_items)] +#![allow(incomplete_features)] + +//@ revisions: fail pass +//@[fail] build-fail (we require monomorphization) +//@[pass] build-pass (we require monomorphization) + +const _<_T>: () = panic!(); +const _: () = panic!(); + +#[cfg(fail)] +const _<'_a>: () = panic!(); //[fail]~ ERROR evaluation of `_::<'_>` failed + +fn main() {} diff --git a/tests/ui/generic-const-items/def-site-mono.rs b/tests/ui/generic-const-items/def-site-mono.rs new file mode 100644 index 0000000000000..f10d450f6bdb7 --- /dev/null +++ b/tests/ui/generic-const-items/def-site-mono.rs @@ -0,0 +1,13 @@ +//! Ensure that we don't try to collect monomorphizeable items inside free const +//! items (their def site to be clear) whose generics require monomorphization. +//! +//! Such items are to be collected at instantiation sites of free consts. + +#![feature(generic_const_items)] +#![allow(incomplete_features)] + +//@ build-pass (we require monomorphization) + +const _IDENTITY: fn(T) -> T = |x| x; + +fn main() {} diff --git a/tests/ui/generic-const-items/evaluatable-bounds.unconstrained.stderr b/tests/ui/generic-const-items/evaluatable-bounds.unconstrained.stderr deleted file mode 100644 index b6f9bdce1cb7a..0000000000000 --- a/tests/ui/generic-const-items/evaluatable-bounds.unconstrained.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error: unconstrained generic constant - --> $DIR/evaluatable-bounds.rs:16:5 - | -LL | const ARRAY: [i32; Self::LEN]; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: try adding a `where` bound - | -LL | const ARRAY: [i32; Self::LEN] where [(); Self::LEN]:; - | ++++++++++++++++++++++ - -error: aborting due to 1 previous error - diff --git a/tests/ui/higher-ranked/trait-bounds/hrtb-perfect-forwarding.polonius.stderr b/tests/ui/higher-ranked/trait-bounds/hrtb-perfect-forwarding.polonius.stderr deleted file mode 100644 index 795484f110884..0000000000000 --- a/tests/ui/higher-ranked/trait-bounds/hrtb-perfect-forwarding.polonius.stderr +++ /dev/null @@ -1,71 +0,0 @@ -warning: function cannot return without recursing - --> $DIR/hrtb-perfect-forwarding.rs:16:1 - | -LL | / fn no_hrtb<'b, T>(mut t: T) -LL | | where -LL | | T: Bar<&'b isize>, -LL | | { -... | -LL | | no_hrtb(&mut t); - | | --------------- recursive call site -LL | | } - | |_^ cannot return without recursing - | - = note: `#[warn(unconditional_recursion)]` on by default - = help: a `loop` may express intention better if this is on purpose - -warning: function cannot return without recursing - --> $DIR/hrtb-perfect-forwarding.rs:25:1 - | -LL | / fn bar_hrtb(mut t: T) -LL | | where -LL | | T: for<'b> Bar<&'b isize>, -LL | | { -... | -LL | | bar_hrtb(&mut t); - | | ---------------- recursive call site -LL | | } - | |_^ cannot return without recursing - | - = help: a `loop` may express intention better if this is on purpose - -warning: function cannot return without recursing - --> $DIR/hrtb-perfect-forwarding.rs:35:1 - | -LL | / fn foo_hrtb_bar_not<'b, T>(mut t: T) -LL | | where -LL | | T: for<'a> Foo<&'a isize> + Bar<&'b isize>, -LL | | { -... | -LL | | foo_hrtb_bar_not(&mut t); - | | ------------------------ recursive call site -LL | | -LL | | -LL | | } - | |_^ cannot return without recursing - | - = help: a `loop` may express intention better if this is on purpose - -error: higher-ranked subtype error - --> $DIR/hrtb-perfect-forwarding.rs:43:5 - | -LL | foo_hrtb_bar_not(&mut t); - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: function cannot return without recursing - --> $DIR/hrtb-perfect-forwarding.rs:48:1 - | -LL | / fn foo_hrtb_bar_hrtb(mut t: T) -LL | | where -LL | | T: for<'a> Foo<&'a isize> + for<'b> Bar<&'b isize>, -LL | | { -LL | | // OK -- now we have `T : for<'b> Bar<&'b isize>`. -LL | | foo_hrtb_bar_hrtb(&mut t); - | | ------------------------- recursive call site -LL | | } - | |_^ cannot return without recursing - | - = help: a `loop` may express intention better if this is on purpose - -error: aborting due to 1 previous error; 4 warnings emitted - diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs index 34548e2487e0a..a44ed9e5ef59b 100644 --- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.rs @@ -43,9 +43,9 @@ fn main() { } foo(bar, "string", |s| s.len() == 5); - //~^ ERROR implementation of `Parser` is not general enough - //~| ERROR implementation of `Parser` is not general enough + //~^ ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough foo(baz, "string", |s| s.0.len() == 5); - //~^ ERROR implementation of `Parser` is not general enough - //~| ERROR implementation of `Parser` is not general enough + //~^ ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough } diff --git a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.stderr b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.stderr index 23fc6e2f7f40a..b2bb417a8f01b 100644 --- a/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.stderr +++ b/tests/ui/higher-ranked/trait-bounds/normalize-under-binder/issue-71955.stderr @@ -1,39 +1,39 @@ -error: implementation of `Parser` is not general enough +error: implementation of `FnOnce` is not general enough --> $DIR/issue-71955.rs:45:5 | LL | foo(bar, "string", |s| s.len() == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Parser` is not general enough + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough | - = note: `for<'a> fn(&'a str) -> (&'a str, &'a str) {bar}` must implement `Parser<'0>`, for any lifetime `'0`... - = note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1` + = note: closure with signature `for<'a> fn(&'a &'2 str) -> bool` must implement `FnOnce<(&&'1 str,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&&'2 str,)>`, for some specific lifetime `'2` -error: implementation of `Parser` is not general enough +error: implementation of `FnOnce` is not general enough --> $DIR/issue-71955.rs:45:5 | LL | foo(bar, "string", |s| s.len() == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Parser` is not general enough + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough | - = note: `for<'a> fn(&'a str) -> (&'a str, &'a str) {bar}` must implement `Parser<'0>`, for any lifetime `'0`... - = note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1` + = note: closure with signature `for<'a> fn(&'a &'2 str) -> bool` must implement `FnOnce<(&&'1 str,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&&'2 str,)>`, for some specific lifetime `'2` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: implementation of `Parser` is not general enough +error: implementation of `FnOnce` is not general enough --> $DIR/issue-71955.rs:48:5 | LL | foo(baz, "string", |s| s.0.len() == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Parser` is not general enough + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough | - = note: `for<'a> fn(&'a str) -> (&'a str, Wrapper<'a>) {baz}` must implement `Parser<'0>`, for any lifetime `'0`... - = note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1` + = note: closure with signature `for<'a> fn(&'a Wrapper<'2>) -> bool` must implement `FnOnce<(&Wrapper<'1>,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&Wrapper<'2>,)>`, for some specific lifetime `'2` -error: implementation of `Parser` is not general enough +error: implementation of `FnOnce` is not general enough --> $DIR/issue-71955.rs:48:5 | LL | foo(baz, "string", |s| s.0.len() == 5); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Parser` is not general enough + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough | - = note: `for<'a> fn(&'a str) -> (&'a str, Wrapper<'a>) {baz}` must implement `Parser<'0>`, for any lifetime `'0`... - = note: ...but it actually implements `Parser<'1>`, for some specific lifetime `'1` + = note: closure with signature `for<'a> fn(&'a Wrapper<'2>) -> bool` must implement `FnOnce<(&Wrapper<'1>,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&Wrapper<'2>,)>`, for some specific lifetime `'2` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 4 previous errors diff --git a/tests/ui/impl-trait/auto-trait-coherence.next.stderr b/tests/ui/impl-trait/auto-trait-coherence.next.stderr deleted file mode 100644 index cd91bfcb48d73..0000000000000 --- a/tests/ui/impl-trait/auto-trait-coherence.next.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0119]: conflicting implementations of trait `AnotherTrait` for type `D<_>` - --> $DIR/auto-trait-coherence.rs:24:1 - | -LL | impl AnotherTrait for T {} - | -------------------------------- first implementation here -... -LL | impl AnotherTrait for D { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `D<_>` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/impl-trait/auto-trait-coherence.old.stderr b/tests/ui/impl-trait/auto-trait-coherence.old.stderr deleted file mode 100644 index cd91bfcb48d73..0000000000000 --- a/tests/ui/impl-trait/auto-trait-coherence.old.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0119]: conflicting implementations of trait `AnotherTrait` for type `D<_>` - --> $DIR/auto-trait-coherence.rs:24:1 - | -LL | impl AnotherTrait for T {} - | -------------------------------- first implementation here -... -LL | impl AnotherTrait for D { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `D<_>` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/impl-trait/coherence-treats-tait-ambig.current.stderr b/tests/ui/impl-trait/coherence-treats-tait-ambig.current.stderr deleted file mode 100644 index 444f3d6689f20..0000000000000 --- a/tests/ui/impl-trait/coherence-treats-tait-ambig.current.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0119]: conflicting implementations of trait `Into` for type `Foo` - --> $DIR/coherence-treats-tait-ambig.rs:10:1 - | -LL | impl Into for Foo { - | ^^^^^^^^^^^^^^^^^^^^ - | - = note: conflicting implementation in crate `core`: - - impl Into for T - where U: From; - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/impl-trait/multiple-lifetimes/error-handling.polonius.stderr b/tests/ui/impl-trait/multiple-lifetimes/error-handling.polonius.stderr deleted file mode 100644 index c511081a86fff..0000000000000 --- a/tests/ui/impl-trait/multiple-lifetimes/error-handling.polonius.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: lifetime may not live long enough - --> $DIR/error-handling.rs:22:16 - | -LL | fn foo<'a, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> { - | -- -- lifetime `'b` defined here - | | - | lifetime `'a` defined here -... -LL | let _: &'b i32 = *u.0; - | ^^^^^^^ type annotation requires that `'a` must outlive `'b` - | - = help: consider adding the following bound: `'a: 'b` - -error: aborting due to 1 previous error - diff --git a/tests/ui/infinite/infinite-instantiation.polonius.stderr b/tests/ui/infinite/infinite-instantiation.polonius.stderr deleted file mode 100644 index f048c942f1a7b..0000000000000 --- a/tests/ui/infinite/infinite-instantiation.polonius.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: reached the recursion limit while instantiating `function::>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` - --> $DIR/infinite-instantiation.rs:22:9 - | -LL | function(counter - 1, t.to_option()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: `function` defined here - --> $DIR/infinite-instantiation.rs:20:1 - | -LL | fn function(counter: usize, t: T) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: the full type name has been written to '$TEST_BUILD_DIR/infinite/infinite-instantiation.polonius/infinite-instantiation.long-type.txt' - -error: aborting due to 1 previous error - diff --git a/tests/ui/issues/auxiliary/issue-111011.stderr b/tests/ui/issues/auxiliary/issue-111011.stderr deleted file mode 100644 index c0b48c5842f4a..0000000000000 --- a/tests/ui/issues/auxiliary/issue-111011.stderr +++ /dev/null @@ -1,34 +0,0 @@ -error[E0308]: mismatched types - --> $DIR/issue-111011.rs:10:23 - | -LL | foo(async move || {}); - | ^^ expected `Box<_>`, found `async` closure body - | - = note: expected struct `Box<_>` - found `async` closure body `[async closure body@$DIR/issue-111011.rs:10:23: 10:25]` - = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html - -error[E0308]: mismatched types - --> $DIR/issue-111011.rs:11:9 - | -LL | bar(async move || {}); - | --- ^^^^^^^^^^^^^^^^ expected `Box _>`, found closure - | | - | arguments to this function are incorrect - | - = note: expected struct `Box<(dyn FnOnce() -> _ + 'static)>` - found closure `{closure@$DIR/issue-111011.rs:11:9: 11:22}` - = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html -note: function defined here - --> $DIR/issue-111011.rs:7:4 - | -LL | fn bar(x: Box X>) {} - | ^^^ ------------------------- -help: store this in the heap by calling `Box::new` - | -LL | bar(Box::new(async move || {})); - | +++++++++ + - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/issues/issue-22638.polonius.stderr b/tests/ui/issues/issue-22638.polonius.stderr deleted file mode 100644 index 3a94ed7bd3da2..0000000000000 --- a/tests/ui/issues/issue-22638.polonius.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: reached the recursion limit while instantiating `A::matches::$CLOSURE` - --> $DIR/issue-22638.rs:56:9 - | -LL | a.matches(f) - | ^^^^^^^^^^^^ - | -note: `A::matches` defined here - --> $DIR/issue-22638.rs:15:5 - | -LL | pub fn matches(&self, f: &F) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: the full type name has been written to '$TEST_BUILD_DIR/issues/issue-22638.polonius/issue-22638.long-type.txt' - -error: aborting due to 1 previous error - diff --git a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.polonius.stderr b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.polonius.stderr deleted file mode 100644 index 08b4573dd0b81..0000000000000 --- a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.polonius.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: reached the recursion limit while instantiating `<(&(&(&(&(&(&(&(&(&(&(&(&(&(&(&(.....), ...), ...) as Foo>::recurse` - --> $DIR/issue-37311.rs:17:9 - | -LL | (self, self).recurse(); - | ^^^^^^^^^^^^^^^^^^^^^^ - | -note: `::recurse` defined here - --> $DIR/issue-37311.rs:16:5 - | -LL | fn recurse(&self) { - | ^^^^^^^^^^^^^^^^^ - = note: the full type name has been written to '$TEST_BUILD_DIR/issues/issue-37311-type-length-limit/issue-37311.polonius/issue-37311.long-type.txt' - -error: aborting due to 1 previous error - diff --git a/tests/ui/issues/issue-48728.current.stderr b/tests/ui/issues/issue-48728.current.stderr deleted file mode 100644 index 2a1b4ff781854..0000000000000 --- a/tests/ui/issues/issue-48728.current.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0119]: conflicting implementations of trait `Clone` for type `Node<[_]>` - --> $DIR/issue-48728.rs:9:10 - | -LL | #[derive(Clone)] - | ^^^^^ conflicting implementation for `Node<[_]>` -... -LL | impl Clone for Node<[T]> { - | ------------------------------------------- first implementation here - | - = note: upstream crates may add a new impl of trait `std::clone::Clone` for type `[_]` in future versions - = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/issues/issue-67552.polonius.stderr b/tests/ui/issues/issue-67552.polonius.stderr deleted file mode 100644 index ca42f87e81942..0000000000000 --- a/tests/ui/issues/issue-67552.polonius.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error: reached the recursion limit while instantiating `rec::<&mut &mut &mut &mut &mut &... &mut &mut &mut &mut &mut Empty>` - --> $DIR/issue-67552.rs:28:9 - | -LL | rec(identity(&mut it)) - | ^^^^^^^^^^^^^^^^^^^^^^ - | -note: `rec` defined here - --> $DIR/issue-67552.rs:21:1 - | -LL | / fn rec(mut it: T) -LL | | where -LL | | T: Iterator, - | |________________^ - = note: the full type name has been written to '$TEST_BUILD_DIR/issues/issue-67552.polonius/issue-67552.long-type.txt' - -error: aborting due to 1 previous error - diff --git a/tests/ui/issues/issue-8727.polonius.stderr b/tests/ui/issues/issue-8727.polonius.stderr deleted file mode 100644 index 4fb8c2b3aff1c..0000000000000 --- a/tests/ui/issues/issue-8727.polonius.stderr +++ /dev/null @@ -1,26 +0,0 @@ -warning: function cannot return without recursing - --> $DIR/issue-8727.rs:7:1 - | -LL | fn generic() { - | ^^^^^^^^^^^^^^^ cannot return without recursing -LL | generic::>(); - | ---------------------- recursive call site - | - = note: `#[warn(unconditional_recursion)]` on by default - = help: a `loop` may express intention better if this is on purpose - -error: reached the recursion limit while instantiating `generic::>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` - --> $DIR/issue-8727.rs:8:5 - | -LL | generic::>(); - | ^^^^^^^^^^^^^^^^^^^^^^ - | -note: `generic` defined here - --> $DIR/issue-8727.rs:7:1 - | -LL | fn generic() { - | ^^^^^^^^^^^^^^^ - = note: the full type name has been written to '$TEST_BUILD_DIR/issues/issue-8727.polonius/issue-8727.long-type.txt' - -error: aborting due to 1 previous error; 1 warning emitted - diff --git a/tests/ui/json/json-multiple.polonius.stderr b/tests/ui/json/json-multiple.polonius.stderr deleted file mode 100644 index 0e4d442f299c3..0000000000000 --- a/tests/ui/json/json-multiple.polonius.stderr +++ /dev/null @@ -1 +0,0 @@ -{"artifact":"$TEST_BUILD_DIR/json-multiple.polonius/libjson_multiple.rlib","emit":"link"} diff --git a/tests/ui/json/json-options.polonius.stderr b/tests/ui/json/json-options.polonius.stderr deleted file mode 100644 index e21f6f85d162d..0000000000000 --- a/tests/ui/json/json-options.polonius.stderr +++ /dev/null @@ -1 +0,0 @@ -{"artifact":"$TEST_BUILD_DIR/json-options.polonius/libjson_options.rlib","emit":"link"} diff --git a/tests/ui/lazy-type-alias/inherent-impls-overflow.classic.stderr b/tests/ui/lazy-type-alias/inherent-impls-overflow.classic.stderr deleted file mode 100644 index 2f00a877142c0..0000000000000 --- a/tests/ui/lazy-type-alias/inherent-impls-overflow.classic.stderr +++ /dev/null @@ -1,43 +0,0 @@ -error[E0275]: overflow normalizing the type alias `Loop` - --> $DIR/inherent-impls-overflow.rs:7:13 - | -LL | type Loop = Loop; - | ^^^^ - | - = note: in case this is a recursive type alias, consider using a struct, enum, or union instead - -error[E0275]: overflow normalizing the type alias `Loop` - --> $DIR/inherent-impls-overflow.rs:9:1 - | -LL | impl Loop {} - | ^^^^^^^^^^^^ - | - = note: in case this is a recursive type alias, consider using a struct, enum, or union instead - -error[E0275]: overflow normalizing the type alias `Poly0<(((((((...,),),),),),),)>` - --> $DIR/inherent-impls-overflow.rs:13:17 - | -LL | type Poly0 = Poly1<(T,)>; - | ^^^^^^^^^^^ - | - = note: in case this is a recursive type alias, consider using a struct, enum, or union instead - -error[E0275]: overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>` - --> $DIR/inherent-impls-overflow.rs:16:17 - | -LL | type Poly1 = Poly0<(T,)>; - | ^^^^^^^^^^^ - | - = note: in case this is a recursive type alias, consider using a struct, enum, or union instead - -error[E0275]: overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>` - --> $DIR/inherent-impls-overflow.rs:20:1 - | -LL | impl Poly0<()> {} - | ^^^^^^^^^^^^^^^^^ - | - = note: in case this is a recursive type alias, consider using a struct, enum, or union instead - -error: aborting due to 5 previous errors - -For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed index 7383ab177dc31..25943d11fc479 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.current.fixed @@ -9,4 +9,5 @@ fn main() { let _ = (-10..=10).find(|x: &i32| x.signum() == 0); //[current]~^ ERROR type mismatch in closure arguments //[next]~^^ ERROR expected `RangeInclusive<{integer}>` to be an iterator that yields `&&i32`, but it yields `{integer}` + //[next]~| ERROR expected a `FnMut(& as Iterator>::Item)` closure, found } diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr index 6104a08933737..696214c0a3cdc 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.next.stderr @@ -13,12 +13,26 @@ note: required by a bound in `find` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL error[E0271]: expected `RangeInclusive<{integer}>` to be an iterator that yields `&&i32`, but it yields `{integer}` - --> $DIR/closure-arg-type-mismatch-issue-45727.rs:9:33 + --> $DIR/closure-arg-type-mismatch-issue-45727.rs:9:24 | LL | let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); - | ^^^^^^ expected `&&i32`, found integer + | ^^^^ expected `&&i32`, found integer -error: aborting due to 2 previous errors +error[E0277]: expected a `FnMut(& as Iterator>::Item)` closure, found `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:9:29: 9:40}` + --> $DIR/closure-arg-type-mismatch-issue-45727.rs:9:29 + | +LL | let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); + | ---- ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected an `FnMut(& as Iterator>::Item)` closure, found `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:9:29: 9:40}` + | | + | required by a bound introduced by this call + | + = help: the trait `for<'a> FnMut(&'a as Iterator>::Item)` is not implemented for closure `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:9:29: 9:40}` + = note: expected a closure with arguments `(&&&i32,)` + found a closure with arguments `(& as Iterator>::Item,)` +note: required by a bound in `find` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + +error: aborting due to 3 previous errors Some errors have detailed explanations: E0271, E0277. For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs index 668a1a7a29c6f..9e44489cbf17e 100644 --- a/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs +++ b/tests/ui/mismatched_types/closure-arg-type-mismatch-issue-45727.rs @@ -9,4 +9,5 @@ fn main() { let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0); //[current]~^ ERROR type mismatch in closure arguments //[next]~^^ ERROR expected `RangeInclusive<{integer}>` to be an iterator that yields `&&i32`, but it yields `{integer}` + //[next]~| ERROR expected a `FnMut(& as Iterator>::Item)` closure, found } diff --git a/tests/ui/mut/mutable-class-fields-2.stderr b/tests/ui/mut/mutable-class-fields-2.stderr index eb0c54f885ba8..7a6ff4da2bf52 100644 --- a/tests/ui/mut/mutable-class-fields-2.stderr +++ b/tests/ui/mut/mutable-class-fields-2.stderr @@ -7,7 +7,7 @@ LL | self.how_hungry -= 5; help: consider changing this to be a mutable reference | LL | pub fn eat(&mut self) { - | ~~~~~~~~~ + | +++ error: aborting due to 1 previous error diff --git a/tests/ui/nll/get_default.polonius.stderr b/tests/ui/nll/get_default.polonius.stderr deleted file mode 100644 index 613d06cce9156..0000000000000 --- a/tests/ui/nll/get_default.polonius.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error[E0502]: cannot borrow `*map` as mutable because it is also borrowed as immutable - --> $DIR/get_default.rs:32:17 - | -LL | fn err(map: &mut Map) -> &String { - | - let's call the lifetime of this reference `'1` -LL | loop { -LL | match map.get() { - | --- immutable borrow occurs here -LL | Some(v) => { -LL | map.set(String::new()); // Both AST and MIR error here - | ^^^ mutable borrow occurs here -LL | -LL | return v; - | - returning this value requires that `*map` is borrowed for `'1` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0502`. diff --git a/tests/ui/nll/outlives-suggestion-simple.polonius.stderr b/tests/ui/nll/outlives-suggestion-simple.polonius.stderr deleted file mode 100644 index c00288f2e3c73..0000000000000 --- a/tests/ui/nll/outlives-suggestion-simple.polonius.stderr +++ /dev/null @@ -1,124 +0,0 @@ -error: lifetime may not live long enough - --> $DIR/outlives-suggestion-simple.rs:6:5 - | -LL | fn foo1<'a, 'b>(x: &'a usize) -> &'b usize { - | -- -- lifetime `'b` defined here - | | - | lifetime `'a` defined here -LL | x - | ^ returning this value requires that `'a` must outlive `'b` - | - = help: consider adding the following bound: `'a: 'b` - -error: lifetime may not live long enough - --> $DIR/outlives-suggestion-simple.rs:10:5 - | -LL | fn foo2<'a>(x: &'a usize) -> &'static usize { - | -- lifetime `'a` defined here -LL | x - | ^ returning this value requires that `'a` must outlive `'static` - | - = help: consider replacing `'a` with `'static` - -error: lifetime may not live long enough - --> $DIR/outlives-suggestion-simple.rs:14:5 - | -LL | fn foo3<'a, 'b>(x: &'a usize, y: &'b usize) -> (&'b usize, &'a usize) { - | -- -- lifetime `'b` defined here - | | - | lifetime `'a` defined here -LL | (x, y) - | ^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` - | - = help: consider adding the following bound: `'a: 'b` - -error: lifetime may not live long enough - --> $DIR/outlives-suggestion-simple.rs:14:5 - | -LL | fn foo3<'a, 'b>(x: &'a usize, y: &'b usize) -> (&'b usize, &'a usize) { - | -- -- lifetime `'b` defined here - | | - | lifetime `'a` defined here -LL | (x, y) - | ^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` - | - = help: consider adding the following bound: `'b: 'a` - -help: `'a` and `'b` must be the same: replace one with the other - -error: lifetime may not live long enough - --> $DIR/outlives-suggestion-simple.rs:22:5 - | -LL | fn foo4<'a, 'b, 'c>(x: &'a usize) -> (&'b usize, &'c usize) { - | -- -- lifetime `'b` defined here - | | - | lifetime `'a` defined here -... -LL | (x, x) - | ^^^^^^ returning this value requires that `'a` must outlive `'b` - | - = help: consider adding the following bound: `'a: 'b` - -error: lifetime may not live long enough - --> $DIR/outlives-suggestion-simple.rs:22:5 - | -LL | fn foo4<'a, 'b, 'c>(x: &'a usize) -> (&'b usize, &'c usize) { - | -- -- lifetime `'c` defined here - | | - | lifetime `'a` defined here -... -LL | (x, x) - | ^^^^^^ returning this value requires that `'a` must outlive `'c` - | - = help: consider adding the following bound: `'a: 'c` - -help: add bound `'a: 'b + 'c` - -error: lifetime may not live long enough - --> $DIR/outlives-suggestion-simple.rs:31:9 - | -LL | pub fn foo<'a>(x: &'a usize) -> Self { - | -- lifetime `'a` defined here -LL | Foo { x } - | ^^^^^^^^^ returning this value requires that `'a` must outlive `'static` - | - = help: consider replacing `'a` with `'static` - -error: lifetime may not live long enough - --> $DIR/outlives-suggestion-simple.rs:41:9 - | -LL | impl<'a> Bar<'a> { - | -- lifetime `'a` defined here -LL | pub fn get<'b>(&self) -> &'b usize { - | -- lifetime `'b` defined here -LL | self.x - | ^^^^^^ returning this value requires that `'a` must outlive `'b` - | - = help: consider adding the following bound: `'a: 'b` - -error: lifetime may not live long enough - --> $DIR/outlives-suggestion-simple.rs:52:9 - | -LL | impl<'a> Baz<'a> { - | -- lifetime `'a` defined here -LL | fn get<'b>(&'b self) -> &'a i32 { - | -- lifetime `'b` defined here -LL | self.x - | ^^^^^^ returning this value requires that `'b` must outlive `'a` - | - = help: consider adding the following bound: `'b: 'a` - -error[E0521]: borrowed data escapes outside of associated function - --> $DIR/outlives-suggestion-simple.rs:73:9 - | -LL | fn get_bar(&self) -> Bar2 { - | ----- - | | - | `self` declared here, outside of the associated function body - | `self` is a reference that is only valid in the associated function body -LL | Bar2::new(&self) - | ^^^^^^^^^^^^^^^^ `self` escapes the associated function body here - -error: aborting due to 10 previous errors - -For more information about this error, try `rustc --explain E0521`. diff --git a/tests/ui/nll/user-annotations/closure-substs.polonius.stderr b/tests/ui/nll/user-annotations/closure-substs.polonius.stderr deleted file mode 100644 index af159a6cd1b85..0000000000000 --- a/tests/ui/nll/user-annotations/closure-substs.polonius.stderr +++ /dev/null @@ -1,61 +0,0 @@ -error: lifetime may not live long enough - --> $DIR/closure-substs.rs:8:16 - | -LL | fn foo<'a>() { - | -- lifetime `'a` defined here -... -LL | return x; - | ^ returning this value requires that `'a` must outlive `'static` - | - = help: consider replacing `'a` with `'static` - -error: lifetime may not live long enough - --> $DIR/closure-substs.rs:15:16 - | -LL | |x: &i32| -> &'static i32 { - | - let's call the lifetime of this reference `'1` -LL | return x; - | ^ returning this value requires that `'1` must outlive `'static` - -error: lifetime may not live long enough - --> $DIR/closure-substs.rs:15:16 - | -LL | |x: &i32| -> &'static i32 { - | - - let's call the lifetime of this reference `'2` - | | - | let's call the lifetime of this reference `'1` -LL | return x; - | ^ returning this value requires that `'1` must outlive `'2` - -error: lifetime may not live long enough - --> $DIR/closure-substs.rs:22:9 - | -LL | fn bar<'a>() { - | -- lifetime `'a` defined here -... -LL | b(x); - | ^^^^ argument requires that `'a` must outlive `'static` - | - = help: consider replacing `'a` with `'static` - -error[E0521]: borrowed data escapes outside of closure - --> $DIR/closure-substs.rs:29:9 - | -LL | |x: &i32, b: fn(&'static i32)| { - | - `x` is a reference that is only valid in the closure body -LL | b(x); - | ^^^^ `x` escapes the closure body here - -error[E0521]: borrowed data escapes outside of closure - --> $DIR/closure-substs.rs:29:9 - | -LL | |x: &i32, b: fn(&'static i32)| { - | - - `b` declared here, outside of the closure body - | | - | `x` is a reference that is only valid in the closure body -LL | b(x); - | ^^^^ `x` escapes the closure body here - -error: aborting due to 6 previous errors - -For more information about this error, try `rustc --explain E0521`. diff --git a/tests/ui/parallel-rustc/cycle_crash.rs b/tests/ui/parallel-rustc/cycle_crash.rs new file mode 100644 index 0000000000000..94ae11aef39d4 --- /dev/null +++ b/tests/ui/parallel-rustc/cycle_crash.rs @@ -0,0 +1,5 @@ +//@ compile-flags: -Z threads=2 + +const FOO: usize = FOO; //~ERROR cycle detected when simplifying constant for the type system `FOO` + +fn main() {} diff --git a/tests/ui/parallel-rustc/cycle_crash.stderr b/tests/ui/parallel-rustc/cycle_crash.stderr new file mode 100644 index 0000000000000..7af3b8ee532c7 --- /dev/null +++ b/tests/ui/parallel-rustc/cycle_crash.stderr @@ -0,0 +1,18 @@ +error[E0391]: cycle detected when simplifying constant for the type system `FOO` + --> $DIR/cycle_crash.rs:3:1 + | +LL | const FOO: usize = FOO; + | ^^^^^^^^^^^^^^^^ + | +note: ...which requires const-evaluating + checking `FOO`... + --> $DIR/cycle_crash.rs:3:20 + | +LL | const FOO: usize = FOO; + | ^^^ + = note: ...which again requires simplifying constant for the type system `FOO`, completing the cycle + = note: cycle used when running analysis passes on this crate + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/parser/bad-fn-ptr-qualifier.fixed b/tests/ui/parser/bad-fn-ptr-qualifier.fixed index e2a2f9486b785..8a97a2f09ccab 100644 --- a/tests/ui/parser/bad-fn-ptr-qualifier.fixed +++ b/tests/ui/parser/bad-fn-ptr-qualifier.fixed @@ -2,24 +2,24 @@ //@ edition:2018 // Most of items are taken from ./recover-const-async-fn-ptr.rs but this is able to apply rustfix. -pub type T0 = fn(); //~ ERROR an `fn` pointer type cannot be `const` -pub type T1 = extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` -pub type T2 = unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` -pub type T3 = fn(); //~ ERROR an `fn` pointer type cannot be `async` -pub type T4 = extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` -pub type T5 = unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` -pub type T6 = unsafe extern "C" fn(); +pub type T0 = fn(); //~ ERROR an `fn` pointer type cannot be `const` +pub type T1 = extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` +pub type T2 = unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` +pub type T3 = fn(); //~ ERROR an `fn` pointer type cannot be `async` +pub type T4 = extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` +pub type T5 = unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` +pub type T6 = unsafe extern "C" fn(); //~^ ERROR an `fn` pointer type cannot be `const` //~| ERROR an `fn` pointer type cannot be `async` -pub type FTT0 = for<'a> fn(); //~ ERROR an `fn` pointer type cannot be `const` -pub type FTT1 = for<'a> extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` -pub type FTT2 = for<'a> unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` -pub type FTT3 = for<'a> fn(); //~ ERROR an `fn` pointer type cannot be `async` -pub type FTT4 = for<'a> extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` -pub type FTT5 = for<'a> unsafe extern "C" fn(); +pub type FTT0 = for<'a> fn(); //~ ERROR an `fn` pointer type cannot be `const` +pub type FTT1 = for<'a> extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` +pub type FTT2 = for<'a> unsafe extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `const` +pub type FTT3 = for<'a> fn(); //~ ERROR an `fn` pointer type cannot be `async` +pub type FTT4 = for<'a> extern "C" fn(); //~ ERROR an `fn` pointer type cannot be `async` +pub type FTT5 = for<'a> unsafe extern "C" fn(); //~^ ERROR an `fn` pointer type cannot be `async` -pub type FTT6 = for<'a> unsafe extern "C" fn(); +pub type FTT6 = for<'a> unsafe extern "C" fn(); //~^ ERROR an `fn` pointer type cannot be `const` //~| ERROR an `fn` pointer type cannot be `async` diff --git a/tests/ui/parser/bad-fn-ptr-qualifier.stderr b/tests/ui/parser/bad-fn-ptr-qualifier.stderr index ddc8bac678cfb..b9d2625d9f4be 100644 --- a/tests/ui/parser/bad-fn-ptr-qualifier.stderr +++ b/tests/ui/parser/bad-fn-ptr-qualifier.stderr @@ -9,7 +9,7 @@ LL | pub type T0 = const fn(); help: remove the `const` qualifier | LL - pub type T0 = const fn(); -LL + pub type T0 = fn(); +LL + pub type T0 = fn(); | error: an `fn` pointer type cannot be `const` @@ -23,7 +23,7 @@ LL | pub type T1 = const extern "C" fn(); help: remove the `const` qualifier | LL - pub type T1 = const extern "C" fn(); -LL + pub type T1 = extern "C" fn(); +LL + pub type T1 = extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -37,7 +37,7 @@ LL | pub type T2 = const unsafe extern "C" fn(); help: remove the `const` qualifier | LL - pub type T2 = const unsafe extern "C" fn(); -LL + pub type T2 = unsafe extern "C" fn(); +LL + pub type T2 = unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -51,7 +51,7 @@ LL | pub type T3 = async fn(); help: remove the `async` qualifier | LL - pub type T3 = async fn(); -LL + pub type T3 = fn(); +LL + pub type T3 = fn(); | error: an `fn` pointer type cannot be `async` @@ -65,7 +65,7 @@ LL | pub type T4 = async extern "C" fn(); help: remove the `async` qualifier | LL - pub type T4 = async extern "C" fn(); -LL + pub type T4 = extern "C" fn(); +LL + pub type T4 = extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -79,7 +79,7 @@ LL | pub type T5 = async unsafe extern "C" fn(); help: remove the `async` qualifier | LL - pub type T5 = async unsafe extern "C" fn(); -LL + pub type T5 = unsafe extern "C" fn(); +LL + pub type T5 = unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -93,7 +93,7 @@ LL | pub type T6 = const async unsafe extern "C" fn(); help: remove the `const` qualifier | LL - pub type T6 = const async unsafe extern "C" fn(); -LL + pub type T6 = async unsafe extern "C" fn(); +LL + pub type T6 = async unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -107,7 +107,7 @@ LL | pub type T6 = const async unsafe extern "C" fn(); help: remove the `async` qualifier | LL - pub type T6 = const async unsafe extern "C" fn(); -LL + pub type T6 = const unsafe extern "C" fn(); +LL + pub type T6 = const unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -121,7 +121,7 @@ LL | pub type FTT0 = for<'a> const fn(); help: remove the `const` qualifier | LL - pub type FTT0 = for<'a> const fn(); -LL + pub type FTT0 = for<'a> fn(); +LL + pub type FTT0 = for<'a> fn(); | error: an `fn` pointer type cannot be `const` @@ -135,7 +135,7 @@ LL | pub type FTT1 = for<'a> const extern "C" fn(); help: remove the `const` qualifier | LL - pub type FTT1 = for<'a> const extern "C" fn(); -LL + pub type FTT1 = for<'a> extern "C" fn(); +LL + pub type FTT1 = for<'a> extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -149,7 +149,7 @@ LL | pub type FTT2 = for<'a> const unsafe extern "C" fn(); help: remove the `const` qualifier | LL - pub type FTT2 = for<'a> const unsafe extern "C" fn(); -LL + pub type FTT2 = for<'a> unsafe extern "C" fn(); +LL + pub type FTT2 = for<'a> unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -163,7 +163,7 @@ LL | pub type FTT3 = for<'a> async fn(); help: remove the `async` qualifier | LL - pub type FTT3 = for<'a> async fn(); -LL + pub type FTT3 = for<'a> fn(); +LL + pub type FTT3 = for<'a> fn(); | error: an `fn` pointer type cannot be `async` @@ -177,7 +177,7 @@ LL | pub type FTT4 = for<'a> async extern "C" fn(); help: remove the `async` qualifier | LL - pub type FTT4 = for<'a> async extern "C" fn(); -LL + pub type FTT4 = for<'a> extern "C" fn(); +LL + pub type FTT4 = for<'a> extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -191,7 +191,7 @@ LL | pub type FTT5 = for<'a> async unsafe extern "C" fn(); help: remove the `async` qualifier | LL - pub type FTT5 = for<'a> async unsafe extern "C" fn(); -LL + pub type FTT5 = for<'a> unsafe extern "C" fn(); +LL + pub type FTT5 = for<'a> unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -205,7 +205,7 @@ LL | pub type FTT6 = for<'a> const async unsafe extern "C" fn(); help: remove the `const` qualifier | LL - pub type FTT6 = for<'a> const async unsafe extern "C" fn(); -LL + pub type FTT6 = for<'a> async unsafe extern "C" fn(); +LL + pub type FTT6 = for<'a> async unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -219,7 +219,7 @@ LL | pub type FTT6 = for<'a> const async unsafe extern "C" fn(); help: remove the `async` qualifier | LL - pub type FTT6 = for<'a> const async unsafe extern "C" fn(); -LL + pub type FTT6 = for<'a> const unsafe extern "C" fn(); +LL + pub type FTT6 = for<'a> const unsafe extern "C" fn(); | error: aborting due to 16 previous errors diff --git a/tests/ui/parser/recover/recover-const-async-fn-ptr.stderr b/tests/ui/parser/recover/recover-const-async-fn-ptr.stderr index 9112a0e135a50..4e5927914ccb5 100644 --- a/tests/ui/parser/recover/recover-const-async-fn-ptr.stderr +++ b/tests/ui/parser/recover/recover-const-async-fn-ptr.stderr @@ -9,7 +9,7 @@ LL | type T0 = const fn(); help: remove the `const` qualifier | LL - type T0 = const fn(); -LL + type T0 = fn(); +LL + type T0 = fn(); | error: an `fn` pointer type cannot be `const` @@ -23,7 +23,7 @@ LL | type T1 = const extern "C" fn(); help: remove the `const` qualifier | LL - type T1 = const extern "C" fn(); -LL + type T1 = extern "C" fn(); +LL + type T1 = extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -37,7 +37,7 @@ LL | type T2 = const unsafe extern "C" fn(); help: remove the `const` qualifier | LL - type T2 = const unsafe extern "C" fn(); -LL + type T2 = unsafe extern "C" fn(); +LL + type T2 = unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -51,7 +51,7 @@ LL | type T3 = async fn(); help: remove the `async` qualifier | LL - type T3 = async fn(); -LL + type T3 = fn(); +LL + type T3 = fn(); | error: an `fn` pointer type cannot be `async` @@ -65,7 +65,7 @@ LL | type T4 = async extern "C" fn(); help: remove the `async` qualifier | LL - type T4 = async extern "C" fn(); -LL + type T4 = extern "C" fn(); +LL + type T4 = extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -79,7 +79,7 @@ LL | type T5 = async unsafe extern "C" fn(); help: remove the `async` qualifier | LL - type T5 = async unsafe extern "C" fn(); -LL + type T5 = unsafe extern "C" fn(); +LL + type T5 = unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -93,7 +93,7 @@ LL | type T6 = const async unsafe extern "C" fn(); help: remove the `const` qualifier | LL - type T6 = const async unsafe extern "C" fn(); -LL + type T6 = async unsafe extern "C" fn(); +LL + type T6 = async unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -107,7 +107,7 @@ LL | type T6 = const async unsafe extern "C" fn(); help: remove the `async` qualifier | LL - type T6 = const async unsafe extern "C" fn(); -LL + type T6 = const unsafe extern "C" fn(); +LL + type T6 = const unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -121,7 +121,7 @@ LL | type FT0 = for<'a> const fn(); help: remove the `const` qualifier | LL - type FT0 = for<'a> const fn(); -LL + type FT0 = for<'a> fn(); +LL + type FT0 = for<'a> fn(); | error: an `fn` pointer type cannot be `const` @@ -135,7 +135,7 @@ LL | type FT1 = for<'a> const extern "C" fn(); help: remove the `const` qualifier | LL - type FT1 = for<'a> const extern "C" fn(); -LL + type FT1 = for<'a> extern "C" fn(); +LL + type FT1 = for<'a> extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -149,7 +149,7 @@ LL | type FT2 = for<'a> const unsafe extern "C" fn(); help: remove the `const` qualifier | LL - type FT2 = for<'a> const unsafe extern "C" fn(); -LL + type FT2 = for<'a> unsafe extern "C" fn(); +LL + type FT2 = for<'a> unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -163,7 +163,7 @@ LL | type FT3 = for<'a> async fn(); help: remove the `async` qualifier | LL - type FT3 = for<'a> async fn(); -LL + type FT3 = for<'a> fn(); +LL + type FT3 = for<'a> fn(); | error: an `fn` pointer type cannot be `async` @@ -177,7 +177,7 @@ LL | type FT4 = for<'a> async extern "C" fn(); help: remove the `async` qualifier | LL - type FT4 = for<'a> async extern "C" fn(); -LL + type FT4 = for<'a> extern "C" fn(); +LL + type FT4 = for<'a> extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -191,7 +191,7 @@ LL | type FT5 = for<'a> async unsafe extern "C" fn(); help: remove the `async` qualifier | LL - type FT5 = for<'a> async unsafe extern "C" fn(); -LL + type FT5 = for<'a> unsafe extern "C" fn(); +LL + type FT5 = for<'a> unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `const` @@ -205,7 +205,7 @@ LL | type FT6 = for<'a> const async unsafe extern "C" fn(); help: remove the `const` qualifier | LL - type FT6 = for<'a> const async unsafe extern "C" fn(); -LL + type FT6 = for<'a> async unsafe extern "C" fn(); +LL + type FT6 = for<'a> async unsafe extern "C" fn(); | error: an `fn` pointer type cannot be `async` @@ -219,7 +219,7 @@ LL | type FT6 = for<'a> const async unsafe extern "C" fn(); help: remove the `async` qualifier | LL - type FT6 = for<'a> const async unsafe extern "C" fn(); -LL + type FT6 = for<'a> const unsafe extern "C" fn(); +LL + type FT6 = for<'a> const unsafe extern "C" fn(); | error[E0308]: mismatched types diff --git a/tests/ui/pattern/usefulness/empty-types.min_exh_pats.stderr b/tests/ui/pattern/usefulness/empty-types.min_exh_pats.stderr deleted file mode 100644 index cf37bf67e860b..0000000000000 --- a/tests/ui/pattern/usefulness/empty-types.min_exh_pats.stderr +++ /dev/null @@ -1,727 +0,0 @@ -error: unreachable pattern - --> $DIR/empty-types.rs:51:9 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `!` is uninhabited -note: the lint level is defined here - --> $DIR/empty-types.rs:17:9 - | -LL | #![deny(unreachable_patterns)] - | ^^^^^^^^^^^^^^^^^^^^ - -error: unreachable pattern - --> $DIR/empty-types.rs:54:9 - | -LL | _x => {} - | ^^ - | - = note: this pattern matches no values because `!` is uninhabited - -error[E0004]: non-exhaustive patterns: type `&!` is non-empty - --> $DIR/empty-types.rs:58:11 - | -LL | match ref_never {} - | ^^^^^^^^^ - | - = note: the matched value is of type `&!` - = note: references are always considered inhabited -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match ref_never { -LL + _ => todo!(), -LL + } - | - -error: unreachable pattern - --> $DIR/empty-types.rs:73:9 - | -LL | (_, _) => {} - | ^^^^^^ - | - = note: this pattern matches no values because `(u32, !)` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:80:9 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `(!, !)` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:83:9 - | -LL | (_, _) => {} - | ^^^^^^ - | - = note: this pattern matches no values because `(!, !)` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:87:9 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `!` is uninhabited - -error[E0004]: non-exhaustive patterns: `Ok(_)` not covered - --> $DIR/empty-types.rs:91:11 - | -LL | match res_u32_never {} - | ^^^^^^^^^^^^^ pattern `Ok(_)` not covered - | -note: `Result` defined here - --> $SRC_DIR/core/src/result.rs:LL:COL - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Result` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ match res_u32_never { -LL + Ok(_) => todo!(), -LL + } - | - -error: unreachable pattern - --> $DIR/empty-types.rs:99:9 - | -LL | Err(_) => {} - | ^^^^^^ - | - = note: this pattern matches no values because `!` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:104:9 - | -LL | Err(_) => {} - | ^^^^^^ - | - = note: this pattern matches no values because `!` is uninhabited - -error[E0004]: non-exhaustive patterns: `Ok(1_u32..=u32::MAX)` not covered - --> $DIR/empty-types.rs:101:11 - | -LL | match res_u32_never { - | ^^^^^^^^^^^^^ pattern `Ok(1_u32..=u32::MAX)` not covered - | -note: `Result` defined here - --> $SRC_DIR/core/src/result.rs:LL:COL - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Result` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ Err(_) => {}, -LL ~ Ok(1_u32..=u32::MAX) => todo!() - | - -error[E0005]: refutable pattern in local binding - --> $DIR/empty-types.rs:108:9 - | -LL | let Ok(_x) = res_u32_never.as_ref(); - | ^^^^^^ pattern `Err(_)` not covered - | - = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant - = note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html - = note: the matched value is of type `Result<&u32, &!>` -help: you might want to use `let else` to handle the variant that isn't matched - | -LL | let Ok(_x) = res_u32_never.as_ref() else { todo!() }; - | ++++++++++++++++ - -error[E0005]: refutable pattern in local binding - --> $DIR/empty-types.rs:112:9 - | -LL | let Ok(_x) = &res_u32_never; - | ^^^^^^ pattern `&Err(_)` not covered - | - = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant - = note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html - = note: the matched value is of type `&Result` -help: you might want to use `let else` to handle the variant that isn't matched - | -LL | let Ok(_x) = &res_u32_never else { todo!() }; - | ++++++++++++++++ - -error: unreachable pattern - --> $DIR/empty-types.rs:119:9 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `Result` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:123:9 - | -LL | Ok(_) => {} - | ^^^^^ - | - = note: this pattern matches no values because `Result` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:126:9 - | -LL | Ok(_) => {} - | ^^^^^ - | - = note: this pattern matches no values because `Result` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:127:9 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `Result` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:130:9 - | -LL | Ok(_) => {} - | ^^^^^ - | - = note: this pattern matches no values because `Result` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:131:9 - | -LL | Err(_) => {} - | ^^^^^^ - | - = note: this pattern matches no values because `Result` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:140:13 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `Void` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:143:13 - | -LL | _ if false => {} - | ^ - | - = note: this pattern matches no values because `Void` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:152:13 - | -LL | Some(_) => {} - | ^^^^^^^ - | - = note: this pattern matches no values because `Void` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:156:13 - | -LL | None => {} - | ---- matches all the values already -LL | _ => {} - | ^ unreachable pattern - -error[E0004]: non-exhaustive patterns: `Some(_)` not covered - --> $DIR/empty-types.rs:165:15 - | -LL | match *ref_opt_void { - | ^^^^^^^^^^^^^ pattern `Some(_)` not covered - | -note: `Option` defined here - --> $SRC_DIR/core/src/option.rs:LL:COL - ::: $SRC_DIR/core/src/option.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Option` - = note: `Void` is uninhabited but is not being matched by value, so a wildcard `_` is required -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ None => {}, -LL + Some(_) => todo!() - | - -error: unreachable pattern - --> $DIR/empty-types.rs:208:13 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `!` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:213:13 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `!` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:218:13 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `!` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:223:13 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `!` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:229:13 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `!` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:288:9 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `!` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:291:9 - | -LL | (_, _) => {} - | ^^^^^^ - | - = note: this pattern matches no values because `(!, !)` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:294:9 - | -LL | Ok(_) => {} - | ^^^^^ - | - = note: this pattern matches no values because `Result` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:295:9 - | -LL | Err(_) => {} - | ^^^^^^ - | - = note: this pattern matches no values because `Result` is uninhabited - -error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty - --> $DIR/empty-types.rs:316:11 - | -LL | match *x {} - | ^^ - | - = note: the matched value is of type `(u32, !)` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match *x { -LL + _ => todo!(), -LL ~ } - | - -error[E0004]: non-exhaustive patterns: type `(!, !)` is non-empty - --> $DIR/empty-types.rs:318:11 - | -LL | match *x {} - | ^^ - | - = note: the matched value is of type `(!, !)` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match *x { -LL + _ => todo!(), -LL ~ } - | - -error[E0004]: non-exhaustive patterns: `Ok(_)` and `Err(_)` not covered - --> $DIR/empty-types.rs:320:11 - | -LL | match *x {} - | ^^ patterns `Ok(_)` and `Err(_)` not covered - | -note: `Result` defined here - --> $SRC_DIR/core/src/result.rs:LL:COL - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Result` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms - | -LL ~ match *x { -LL + Ok(_) | Err(_) => todo!(), -LL ~ } - | - -error[E0004]: non-exhaustive patterns: type `[!; 3]` is non-empty - --> $DIR/empty-types.rs:322:11 - | -LL | match *x {} - | ^^ - | - = note: the matched value is of type `[!; 3]` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match *x { -LL + _ => todo!(), -LL ~ } - | - -error[E0004]: non-exhaustive patterns: type `&[!]` is non-empty - --> $DIR/empty-types.rs:327:11 - | -LL | match slice_never {} - | ^^^^^^^^^^^ - | - = note: the matched value is of type `&[!]` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match slice_never { -LL + _ => todo!(), -LL + } - | - -error[E0004]: non-exhaustive patterns: `&[_, ..]` not covered - --> $DIR/empty-types.rs:329:11 - | -LL | match slice_never { - | ^^^^^^^^^^^ pattern `&[_, ..]` not covered - | - = note: the matched value is of type `&[!]` - = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ [] => {}, -LL + &[_, ..] => todo!() - | - -error[E0004]: non-exhaustive patterns: `&[]`, `&[_]` and `&[_, _]` not covered - --> $DIR/empty-types.rs:338:11 - | -LL | match slice_never { - | ^^^^^^^^^^^ patterns `&[]`, `&[_]` and `&[_, _]` not covered - | - = note: the matched value is of type `&[!]` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms - | -LL ~ [_, _, _, ..] => {}, -LL + &[] | &[_] | &[_, _] => todo!() - | - -error[E0004]: non-exhaustive patterns: `&[]` and `&[_, ..]` not covered - --> $DIR/empty-types.rs:352:11 - | -LL | match slice_never { - | ^^^^^^^^^^^ patterns `&[]` and `&[_, ..]` not covered - | - = note: the matched value is of type `&[!]` - = note: match arms with guards don't count towards exhaustivity -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms - | -LL ~ &[..] if false => {}, -LL + &[] | &[_, ..] => todo!() - | - -error[E0004]: non-exhaustive patterns: type `[!]` is non-empty - --> $DIR/empty-types.rs:359:11 - | -LL | match *slice_never {} - | ^^^^^^^^^^^^ - | - = note: the matched value is of type `[!]` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match *slice_never { -LL + _ => todo!(), -LL + } - | - -error: unreachable pattern - --> $DIR/empty-types.rs:369:9 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `[!; 3]` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:372:9 - | -LL | [_, _, _] => {} - | ^^^^^^^^^ - | - = note: this pattern matches no values because `[!; 3]` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:375:9 - | -LL | [_, ..] => {} - | ^^^^^^^ - | - = note: this pattern matches no values because `[!; 3]` is uninhabited - -error[E0004]: non-exhaustive patterns: type `[!; 0]` is non-empty - --> $DIR/empty-types.rs:389:11 - | -LL | match array_0_never {} - | ^^^^^^^^^^^^^ - | - = note: the matched value is of type `[!; 0]` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match array_0_never { -LL + _ => todo!(), -LL + } - | - -error: unreachable pattern - --> $DIR/empty-types.rs:396:9 - | -LL | [] => {} - | -- matches all the values already -LL | _ => {} - | ^ unreachable pattern - -error[E0004]: non-exhaustive patterns: `[]` not covered - --> $DIR/empty-types.rs:398:11 - | -LL | match array_0_never { - | ^^^^^^^^^^^^^ pattern `[]` not covered - | - = note: the matched value is of type `[!; 0]` - = note: match arms with guards don't count towards exhaustivity -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ [..] if false => {}, -LL + [] => todo!() - | - -error: unreachable pattern - --> $DIR/empty-types.rs:417:9 - | -LL | Some(_) => {} - | ^^^^^^^ - | - = note: this pattern matches no values because `!` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:422:9 - | -LL | Some(_a) => {} - | ^^^^^^^^ - | - = note: this pattern matches no values because `!` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:427:9 - | -LL | None => {} - | ---- matches all the values already -LL | // !useful, !reachable -LL | _ => {} - | ^ unreachable pattern - -error: unreachable pattern - --> $DIR/empty-types.rs:432:9 - | -LL | None => {} - | ---- matches all the values already -LL | // !useful, !reachable -LL | _a => {} - | ^^ unreachable pattern - -error[E0004]: non-exhaustive patterns: `&Some(_)` not covered - --> $DIR/empty-types.rs:452:11 - | -LL | match ref_opt_never { - | ^^^^^^^^^^^^^ pattern `&Some(_)` not covered - | -note: `Option` defined here - --> $SRC_DIR/core/src/option.rs:LL:COL - ::: $SRC_DIR/core/src/option.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `&Option` - = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ &None => {}, -LL + &Some(_) => todo!() - | - -error[E0004]: non-exhaustive patterns: `Some(_)` not covered - --> $DIR/empty-types.rs:493:11 - | -LL | match *ref_opt_never { - | ^^^^^^^^^^^^^^ pattern `Some(_)` not covered - | -note: `Option` defined here - --> $SRC_DIR/core/src/option.rs:LL:COL - ::: $SRC_DIR/core/src/option.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Option` - = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ None => {}, -LL + Some(_) => todo!() - | - -error[E0004]: non-exhaustive patterns: `Err(_)` not covered - --> $DIR/empty-types.rs:541:11 - | -LL | match *ref_res_never { - | ^^^^^^^^^^^^^^ pattern `Err(_)` not covered - | -note: `Result` defined here - --> $SRC_DIR/core/src/result.rs:LL:COL - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Result` - = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ Ok(_) => {}, -LL + Err(_) => todo!() - | - -error[E0004]: non-exhaustive patterns: `Err(_)` not covered - --> $DIR/empty-types.rs:552:11 - | -LL | match *ref_res_never { - | ^^^^^^^^^^^^^^ pattern `Err(_)` not covered - | -note: `Result` defined here - --> $SRC_DIR/core/src/result.rs:LL:COL - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Result` - = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ Ok(_a) => {}, -LL + Err(_) => todo!() - | - -error[E0004]: non-exhaustive patterns: type `(u32, !)` is non-empty - --> $DIR/empty-types.rs:571:11 - | -LL | match *ref_tuple_half_never {} - | ^^^^^^^^^^^^^^^^^^^^^ - | - = note: the matched value is of type `(u32, !)` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown - | -LL ~ match *ref_tuple_half_never { -LL + _ => todo!(), -LL + } - | - -error: unreachable pattern - --> $DIR/empty-types.rs:604:9 - | -LL | _ => {} - | ^ - | - = note: this pattern matches no values because `!` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:607:9 - | -LL | _x => {} - | ^^ - | - = note: this pattern matches no values because `!` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:610:9 - | -LL | _ if false => {} - | ^ - | - = note: this pattern matches no values because `!` is uninhabited - -error: unreachable pattern - --> $DIR/empty-types.rs:613:9 - | -LL | _x if false => {} - | ^^ - | - = note: this pattern matches no values because `!` is uninhabited - -error[E0004]: non-exhaustive patterns: `&_` not covered - --> $DIR/empty-types.rs:638:11 - | -LL | match ref_never { - | ^^^^^^^^^ pattern `&_` not covered - | - = note: the matched value is of type `&!` - = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required - = note: references are always considered inhabited - = note: match arms with guards don't count towards exhaustivity -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ &_a if false => {}, -LL + &_ => todo!() - | - -error[E0004]: non-exhaustive patterns: `Ok(_)` not covered - --> $DIR/empty-types.rs:654:11 - | -LL | match *ref_result_never { - | ^^^^^^^^^^^^^^^^^ pattern `Ok(_)` not covered - | -note: `Result` defined here - --> $SRC_DIR/core/src/result.rs:LL:COL - ::: $SRC_DIR/core/src/result.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Result` - = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ Err(_) => {}, -LL + Ok(_) => todo!() - | - -error[E0004]: non-exhaustive patterns: `Some(_)` not covered - --> $DIR/empty-types.rs:674:11 - | -LL | match *x { - | ^^ pattern `Some(_)` not covered - | -note: `Option>` defined here - --> $SRC_DIR/core/src/option.rs:LL:COL - ::: $SRC_DIR/core/src/option.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Option>` - = note: `Result` is uninhabited but is not being matched by value, so a wildcard `_` is required -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ None => {}, -LL + Some(_) => todo!() - | - -error: aborting due to 64 previous errors - -Some errors have detailed explanations: E0004, E0005. -For more information about an error, try `rustc --explain E0004`. diff --git a/tests/ui/pattern/usefulness/match-privately-empty.min_exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/match-privately-empty.min_exhaustive_patterns.stderr deleted file mode 100644 index 261a4b3353f23..0000000000000 --- a/tests/ui/pattern/usefulness/match-privately-empty.min_exhaustive_patterns.stderr +++ /dev/null @@ -1,21 +0,0 @@ -error[E0004]: non-exhaustive patterns: `Some(Private { misc: true, .. })` not covered - --> $DIR/match-privately-empty.rs:15:11 - | -LL | match private::DATA { - | ^^^^^^^^^^^^^ pattern `Some(Private { misc: true, .. })` not covered - | -note: `Option` defined here - --> $SRC_DIR/core/src/option.rs:LL:COL - ::: $SRC_DIR/core/src/option.rs:LL:COL - | - = note: not covered - = note: the matched value is of type `Option` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ Some(private::Private { misc: false, .. }) => {}, -LL + Some(Private { misc: true, .. }) => todo!() - | - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/pattern/usefulness/slice_of_empty.min_exhaustive_patterns.stderr b/tests/ui/pattern/usefulness/slice_of_empty.min_exhaustive_patterns.stderr deleted file mode 100644 index f24ce154d149d..0000000000000 --- a/tests/ui/pattern/usefulness/slice_of_empty.min_exhaustive_patterns.stderr +++ /dev/null @@ -1,30 +0,0 @@ -error[E0004]: non-exhaustive patterns: `&[_, ..]` not covered - --> $DIR/slice_of_empty.rs:10:11 - | -LL | match nevers { - | ^^^^^^ pattern `&[_, ..]` not covered - | - = note: the matched value is of type `&[!]` - = note: `!` is uninhabited but is not being matched by value, so a wildcard `_` is required -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown - | -LL ~ &[] => (), -LL ~ &[_, ..] => todo!(), - | - -error[E0004]: non-exhaustive patterns: `&[]` and `&[_, _, ..]` not covered - --> $DIR/slice_of_empty.rs:21:11 - | -LL | match nevers { - | ^^^^^^ patterns `&[]` and `&[_, _, ..]` not covered - | - = note: the matched value is of type `&[!]` -help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern, a match arm with multiple or-patterns as shown, or multiple match arms - | -LL ~ &[_] => (), -LL ~ &[] | &[_, _, ..] => todo!(), - | - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0004`. diff --git a/tests/ui/recursion/issue-38591-non-regular-dropck-recursion.polonius.stderr b/tests/ui/recursion/issue-38591-non-regular-dropck-recursion.polonius.stderr deleted file mode 100644 index ff1a127e63e3e..0000000000000 --- a/tests/ui/recursion/issue-38591-non-regular-dropck-recursion.polonius.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: reached the recursion limit while instantiating `std::ptr::drop_in_place::))` - --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL - | -LL | pub unsafe fn drop_in_place(to_drop: *mut T) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: `std::ptr::drop_in_place` defined here - --> $SRC_DIR/core/src/ptr/mod.rs:LL:COL - | -LL | pub unsafe fn drop_in_place(to_drop: *mut T) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: the full type name has been written to '$TEST_BUILD_DIR/recursion/issue-38591-non-regular-dropck-recursion.polonius/issue-38591-non-regular-dropck-recursion.long-type.txt' - -error: aborting due to 1 previous error - diff --git a/tests/ui/recursion/recursion.polonius.stderr b/tests/ui/recursion/recursion.polonius.stderr deleted file mode 100644 index 737e71e884518..0000000000000 --- a/tests/ui/recursion/recursion.polonius.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error: reached the recursion limit while instantiating `test::>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` - --> $DIR/recursion.rs:18:11 - | -LL | _ => {test (n-1, i+1, Cons {head:2*i+1, tail:first}, Cons{head:i*i, tail:second})} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: `test` defined here - --> $DIR/recursion.rs:16:1 - | -LL | fn test (n:isize, i:isize, first:T, second:T) ->isize { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: the full type name has been written to '$TEST_BUILD_DIR/recursion/recursion.polonius/recursion.long-type.txt' - -error: aborting due to 1 previous error - diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr deleted file mode 100644 index d78f4a5f6ebb5..0000000000000 --- a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.exh_pats.stderr +++ /dev/null @@ -1,55 +0,0 @@ -error: unreachable pattern - --> $DIR/unreachable.rs:16:9 - | -LL | Err(!), - | ^^^^^^ - | - = note: this pattern matches no values because `Void` is uninhabited -note: the lint level is defined here - --> $DIR/unreachable.rs:6:9 - | -LL | #![deny(unreachable_patterns)] - | ^^^^^^^^^^^^^^^^^^^^ - -error: unreachable pattern - --> $DIR/unreachable.rs:19:19 - | -LL | let (Ok(_x) | Err(!)) = res_void; - | ^^^^^^ - | - = note: this pattern matches no values because `Void` is uninhabited - -error: unreachable pattern - --> $DIR/unreachable.rs:21:12 - | -LL | if let Err(!) = res_void {} - | ^^^^^^ - | - = note: this pattern matches no values because `Void` is uninhabited - -error: unreachable pattern - --> $DIR/unreachable.rs:23:24 - | -LL | if let (Ok(true) | Err(!)) = res_void {} - | ^^^^^^ - | - = note: this pattern matches no values because `Void` is uninhabited - -error: unreachable pattern - --> $DIR/unreachable.rs:25:23 - | -LL | for (Ok(mut _x) | Err(!)) in [res_void] {} - | ^^^^^^ - | - = note: this pattern matches no values because `Void` is uninhabited - -error: unreachable pattern - --> $DIR/unreachable.rs:29:18 - | -LL | fn foo((Ok(_x) | Err(!)): Result) {} - | ^^^^^^ - | - = note: this pattern matches no values because `Void` is uninhabited - -error: aborting due to 6 previous errors - diff --git a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.normal.stderr b/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.normal.stderr deleted file mode 100644 index a3bf8e80ecec7..0000000000000 --- a/tests/ui/rfcs/rfc-0000-never_patterns/unreachable.normal.stderr +++ /dev/null @@ -1,44 +0,0 @@ -error: unreachable pattern - --> $DIR/unreachable.rs:16:9 - | -LL | Err(!), - | ^^^^^^ - | -note: the lint level is defined here - --> $DIR/unreachable.rs:6:9 - | -LL | #![deny(unreachable_patterns)] - | ^^^^^^^^^^^^^^^^^^^^ - -error: unreachable pattern - --> $DIR/unreachable.rs:19:19 - | -LL | let (Ok(_x) | Err(!)) = res_void; - | ^^^^^^ - -error: unreachable pattern - --> $DIR/unreachable.rs:21:12 - | -LL | if let Err(!) = res_void {} - | ^^^^^^ - -error: unreachable pattern - --> $DIR/unreachable.rs:23:24 - | -LL | if let (Ok(true) | Err(!)) = res_void {} - | ^^^^^^ - -error: unreachable pattern - --> $DIR/unreachable.rs:25:23 - | -LL | for (Ok(mut _x) | Err(!)) in [res_void] {} - | ^^^^^^ - -error: unreachable pattern - --> $DIR/unreachable.rs:29:18 - | -LL | fn foo((Ok(_x) | Err(!)): Result) {} - | ^^^^^^ - -error: aborting due to 6 previous errors - diff --git a/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.current.stderr b/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.current.stderr deleted file mode 100644 index f2727336bc56b..0000000000000 --- a/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.current.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0277]: the trait bound `Something: Termination` is not satisfied - --> $DIR/issue-103052-2.rs:15:22 - | -LL | fn main() -> Something { - | ^^^^^^^^^ the trait `Termination` is not implemented for `Something` - | -note: required by a bound in `Main::main::{opaque#0}` - --> $DIR/issue-103052-2.rs:9:27 - | -LL | fn main() -> impl std::process::Termination; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Main::main::{opaque#0}` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.next.stderr b/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.next.stderr deleted file mode 100644 index 4bb420664f7f4..0000000000000 --- a/tests/ui/rfcs/rfc-1937-termination-trait/issue-103052-2.next.stderr +++ /dev/null @@ -1,15 +0,0 @@ -error[E0277]: the trait bound `Something: Termination` is not satisfied - --> $DIR/issue-103052-2.rs:15:22 - | -LL | fn main() -> Something { - | ^^^^^^^^^ the trait `Termination` is not implemented for `Something` - | -note: required by a bound in `Main::{opaque#0}` - --> $DIR/issue-103052-2.rs:9:27 - | -LL | fn main() -> impl std::process::Termination; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Main::{opaque#0}` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs index 3eae79faf423b..c880ef0fe8a0e 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.rs @@ -5,6 +5,9 @@ #[target_feature(enable = "avx")] fn foo() {} +#[target_feature(enable = "avx")] +fn bar(arg: i32) {} + #[target_feature(enable = "avx")] unsafe fn foo_unsafe() {} @@ -20,10 +23,15 @@ fn call_once(f: impl FnOnce()) { f() } +fn call_once_i32(f: impl FnOnce(i32)) { + f(0) +} + fn main() { call(foo); //~ ERROR expected a `Fn()` closure, found `#[target_features] fn() {foo}` call_mut(foo); //~ ERROR expected a `FnMut()` closure, found `#[target_features] fn() {foo}` call_once(foo); //~ ERROR expected a `FnOnce()` closure, found `#[target_features] fn() {foo}` + call_once_i32(bar); //~ ERROR expected a `FnOnce(i32)` closure, found `#[target_features] fn(i32) {bar}` call(foo_unsafe); //~^ ERROR expected a `Fn()` closure, found `unsafe fn() {foo_unsafe}` diff --git a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr index 2915b9ad1b3d8..efc061eca5f59 100644 --- a/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr +++ b/tests/ui/rfcs/rfc-2396-target_feature-11/fn-traits.stderr @@ -1,5 +1,5 @@ error[E0277]: expected a `Fn()` closure, found `#[target_features] fn() {foo}` - --> $DIR/fn-traits.rs:24:10 + --> $DIR/fn-traits.rs:31:10 | LL | call(foo); | ---- ^^^ expected an `Fn()` closure, found `#[target_features] fn() {foo}` @@ -9,14 +9,15 @@ LL | call(foo); = help: the trait `Fn()` is not implemented for fn item `#[target_features] fn() {foo}` = note: wrap the `#[target_features] fn() {foo}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits + = note: try casting the function to a `fn` pointer or wrapping it in a closure note: required by a bound in `call` - --> $DIR/fn-traits.rs:11:17 + --> $DIR/fn-traits.rs:14:17 | LL | fn call(f: impl Fn()) { | ^^^^ required by this bound in `call` error[E0277]: expected a `FnMut()` closure, found `#[target_features] fn() {foo}` - --> $DIR/fn-traits.rs:25:14 + --> $DIR/fn-traits.rs:32:14 | LL | call_mut(foo); | -------- ^^^ expected an `FnMut()` closure, found `#[target_features] fn() {foo}` @@ -26,14 +27,15 @@ LL | call_mut(foo); = help: the trait `FnMut()` is not implemented for fn item `#[target_features] fn() {foo}` = note: wrap the `#[target_features] fn() {foo}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits + = note: try casting the function to a `fn` pointer or wrapping it in a closure note: required by a bound in `call_mut` - --> $DIR/fn-traits.rs:15:25 + --> $DIR/fn-traits.rs:18:25 | LL | fn call_mut(mut f: impl FnMut()) { | ^^^^^^^ required by this bound in `call_mut` error[E0277]: expected a `FnOnce()` closure, found `#[target_features] fn() {foo}` - --> $DIR/fn-traits.rs:26:15 + --> $DIR/fn-traits.rs:33:15 | LL | call_once(foo); | --------- ^^^ expected an `FnOnce()` closure, found `#[target_features] fn() {foo}` @@ -43,14 +45,32 @@ LL | call_once(foo); = help: the trait `FnOnce()` is not implemented for fn item `#[target_features] fn() {foo}` = note: wrap the `#[target_features] fn() {foo}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits + = note: try casting the function to a `fn` pointer or wrapping it in a closure note: required by a bound in `call_once` - --> $DIR/fn-traits.rs:19:22 + --> $DIR/fn-traits.rs:22:22 | LL | fn call_once(f: impl FnOnce()) { | ^^^^^^^^ required by this bound in `call_once` +error[E0277]: expected a `FnOnce(i32)` closure, found `#[target_features] fn(i32) {bar}` + --> $DIR/fn-traits.rs:34:19 + | +LL | call_once_i32(bar); + | ------------- ^^^ expected an `FnOnce(i32)` closure, found `#[target_features] fn(i32) {bar}` + | | + | required by a bound introduced by this call + | + = help: the trait `FnOnce(i32)` is not implemented for fn item `#[target_features] fn(i32) {bar}` + = note: `#[target_feature]` functions do not implement the `Fn` traits + = note: try casting the function to a `fn` pointer or wrapping it in a closure +note: required by a bound in `call_once_i32` + --> $DIR/fn-traits.rs:26:26 + | +LL | fn call_once_i32(f: impl FnOnce(i32)) { + | ^^^^^^^^^^^ required by this bound in `call_once_i32` + error[E0277]: expected a `Fn()` closure, found `unsafe fn() {foo_unsafe}` - --> $DIR/fn-traits.rs:28:10 + --> $DIR/fn-traits.rs:36:10 | LL | call(foo_unsafe); | ---- ^^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }` @@ -61,14 +81,15 @@ LL | call(foo_unsafe); = note: unsafe function cannot be called generically without an unsafe block = note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits + = note: try casting the function to a `fn` pointer or wrapping it in a closure note: required by a bound in `call` - --> $DIR/fn-traits.rs:11:17 + --> $DIR/fn-traits.rs:14:17 | LL | fn call(f: impl Fn()) { | ^^^^ required by this bound in `call` error[E0277]: expected a `FnMut()` closure, found `unsafe fn() {foo_unsafe}` - --> $DIR/fn-traits.rs:30:14 + --> $DIR/fn-traits.rs:38:14 | LL | call_mut(foo_unsafe); | -------- ^^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }` @@ -79,14 +100,15 @@ LL | call_mut(foo_unsafe); = note: unsafe function cannot be called generically without an unsafe block = note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits + = note: try casting the function to a `fn` pointer or wrapping it in a closure note: required by a bound in `call_mut` - --> $DIR/fn-traits.rs:15:25 + --> $DIR/fn-traits.rs:18:25 | LL | fn call_mut(mut f: impl FnMut()) { | ^^^^^^^ required by this bound in `call_mut` error[E0277]: expected a `FnOnce()` closure, found `unsafe fn() {foo_unsafe}` - --> $DIR/fn-traits.rs:32:15 + --> $DIR/fn-traits.rs:40:15 | LL | call_once(foo_unsafe); | --------- ^^^^^^^^^^ call the function in a closure: `|| unsafe { /* code */ }` @@ -97,12 +119,13 @@ LL | call_once(foo_unsafe); = note: unsafe function cannot be called generically without an unsafe block = note: wrap the `unsafe fn() {foo_unsafe}` in a closure with no arguments: `|| { /* code */ }` = note: `#[target_feature]` functions do not implement the `Fn` traits + = note: try casting the function to a `fn` pointer or wrapping it in a closure note: required by a bound in `call_once` - --> $DIR/fn-traits.rs:19:22 + --> $DIR/fn-traits.rs:22:22 | LL | fn call_once(f: impl FnOnce()) { | ^^^^^^^^ required by this bound in `call_once` -error: aborting due to 6 previous errors +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nofeature.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nofeature.stderr deleted file mode 100644 index f556ecf7f91d3..0000000000000 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nofeature.stderr +++ /dev/null @@ -1,1111 +0,0 @@ -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:31:9 - | -LL | if (let 0 = 1) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:31:9 - | -LL | if (let 0 = 1) {} - | ^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:34:11 - | -LL | if (((let 0 = 1))) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:34:11 - | -LL | if (((let 0 = 1))) {} - | ^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:37:9 - | -LL | if (let 0 = 1) && true {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:37:9 - | -LL | if (let 0 = 1) && true {} - | ^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:40:17 - | -LL | if true && (let 0 = 1) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:40:17 - | -LL | if true && (let 0 = 1) {} - | ^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:43:9 - | -LL | if (let 0 = 1) && (let 0 = 1) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:43:9 - | -LL | if (let 0 = 1) && (let 0 = 1) {} - | ^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:43:24 - | -LL | if (let 0 = 1) && (let 0 = 1) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:43:24 - | -LL | if (let 0 = 1) && (let 0 = 1) {} - | ^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:47:35 - | -LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:47:35 - | -LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:47:48 - | -LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:47:35 - | -LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:47:61 - | -LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:47:35 - | -LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:56:12 - | -LL | while (let 0 = 1) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:56:12 - | -LL | while (let 0 = 1) {} - | ^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:59:14 - | -LL | while (((let 0 = 1))) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:59:14 - | -LL | while (((let 0 = 1))) {} - | ^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:62:12 - | -LL | while (let 0 = 1) && true {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:62:12 - | -LL | while (let 0 = 1) && true {} - | ^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:65:20 - | -LL | while true && (let 0 = 1) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:65:20 - | -LL | while true && (let 0 = 1) {} - | ^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:68:12 - | -LL | while (let 0 = 1) && (let 0 = 1) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:68:12 - | -LL | while (let 0 = 1) && (let 0 = 1) {} - | ^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:68:27 - | -LL | while (let 0 = 1) && (let 0 = 1) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:68:27 - | -LL | while (let 0 = 1) && (let 0 = 1) {} - | ^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:72:38 - | -LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:72:38 - | -LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:72:51 - | -LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:72:38 - | -LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:72:64 - | -LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:72:38 - | -LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:95:9 - | -LL | if &let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:98:9 - | -LL | if !let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:100:9 - | -LL | if *let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:102:9 - | -LL | if -let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:110:9 - | -LL | if (let 0 = 0)? {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:113:16 - | -LL | if true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions - --> $DIR/disallowed-positions.rs:113:13 - | -LL | if true || let 0 = 0 {} - | ^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:115:17 - | -LL | if (true || let 0 = 0) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:117:25 - | -LL | if true && (true || let 0 = 0) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:119:25 - | -LL | if true || (true && let 0 = 0) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:123:12 - | -LL | if x = let 0 = 0 {} - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:126:15 - | -LL | if true..(let 0 = 0) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:129:11 - | -LL | if ..(let 0 = 0) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:131:9 - | -LL | if (let 0 = 0).. {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:135:8 - | -LL | if let Range { start: _, end: _ } = true..true && false {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:138:8 - | -LL | if let Range { start: _, end: _ } = true..true || false {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:144:8 - | -LL | if let Range { start: F, end } = F..|| true {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:150:8 - | -LL | if let Range { start: true, end } = t..&&false {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:154:19 - | -LL | if let true = let true = true {} - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:160:12 - | -LL | while &let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:163:12 - | -LL | while !let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:165:12 - | -LL | while *let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:167:12 - | -LL | while -let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:175:12 - | -LL | while (let 0 = 0)? {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:178:19 - | -LL | while true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions - --> $DIR/disallowed-positions.rs:178:16 - | -LL | while true || let 0 = 0 {} - | ^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:180:20 - | -LL | while (true || let 0 = 0) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:182:28 - | -LL | while true && (true || let 0 = 0) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:184:28 - | -LL | while true || (true && let 0 = 0) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:188:15 - | -LL | while x = let 0 = 0 {} - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:191:18 - | -LL | while true..(let 0 = 0) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:194:14 - | -LL | while ..(let 0 = 0) {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:196:12 - | -LL | while (let 0 = 0).. {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:200:11 - | -LL | while let Range { start: _, end: _ } = true..true && false {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:203:11 - | -LL | while let Range { start: _, end: _ } = true..true || false {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:209:11 - | -LL | while let Range { start: F, end } = F..|| true {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:215:11 - | -LL | while let Range { start: true, end } = t..&&false {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:219:22 - | -LL | while let true = let true = true {} - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:236:6 - | -LL | &let 0 = 0; - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:239:6 - | -LL | !let 0 = 0; - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:241:6 - | -LL | *let 0 = 0; - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:243:6 - | -LL | -let 0 = 0; - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:245:13 - | -LL | let _ = let _ = 3; - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:253:6 - | -LL | (let 0 = 0)?; - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:256:13 - | -LL | true || let 0 = 0; - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:258:14 - | -LL | (true || let 0 = 0); - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:260:22 - | -LL | true && (true || let 0 = 0); - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:264:9 - | -LL | x = let 0 = 0; - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:267:12 - | -LL | true..(let 0 = 0); - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:269:8 - | -LL | ..(let 0 = 0); - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:271:6 - | -LL | (let 0 = 0)..; - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:274:6 - | -LL | (let Range { start: _, end: _ } = true..true || false); - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:278:6 - | -LL | (let true = let true = true); - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:278:17 - | -LL | (let true = let true = true); - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:284:25 - | -LL | let x = true && let y = 1; - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:290:19 - | -LL | [1, 2, 3][let _ = ()] - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:295:6 - | -LL | &let 0 = 0 - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:306:17 - | -LL | true && let 1 = 1 - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:311:17 - | -LL | true && let 1 = 1 - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:316:17 - | -LL | true && let 1 = 1 - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:327:17 - | -LL | true && let 1 = 1 - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expressions must be enclosed in braces to be used as const generic arguments - --> $DIR/disallowed-positions.rs:327:9 - | -LL | true && let 1 = 1 - | ^^^^^^^^^^^^^^^^^ - | -help: enclose the `const` expression in braces - | -LL | { true && let 1 = 1 } - | + + - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:337:9 - | -LL | if (let Some(a) = opt && true) { - | ^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:337:9 - | -LL | if (let Some(a) = opt && true) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:341:9 - | -LL | if (let Some(a) = opt) && true { - | ^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:341:9 - | -LL | if (let Some(a) = opt) && true { - | ^^^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:344:9 - | -LL | if (let Some(a) = opt) && (let Some(b) = a) { - | ^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:344:9 - | -LL | if (let Some(a) = opt) && (let Some(b) = a) { - | ^^^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:344:32 - | -LL | if (let Some(a) = opt) && (let Some(b) = a) { - | ^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:344:32 - | -LL | if (let Some(a) = opt) && (let Some(b) = a) { - | ^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:351:9 - | -LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { - | ^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:351:9 - | -LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:351:31 - | -LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { - | ^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:351:31 - | -LL | if (let Some(a) = opt && (let Some(b) = a)) && b == 1 { - | ^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:355:9 - | -LL | if (let Some(a) = opt && (let Some(b) = a)) && true { - | ^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:355:9 - | -LL | if (let Some(a) = opt && (let Some(b) = a)) && true { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:355:31 - | -LL | if (let Some(a) = opt && (let Some(b) = a)) && true { - | ^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:355:31 - | -LL | if (let Some(a) = opt && (let Some(b) = a)) && true { - | ^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:359:9 - | -LL | if (let Some(a) = opt && (true)) && true { - | ^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `let`s wrapped in parentheses are not supported in a context with let chains - --> $DIR/disallowed-positions.rs:359:9 - | -LL | if (let Some(a) = opt && (true)) && true { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:375:22 - | -LL | let x = (true && let y = 1); - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:380:20 - | -LL | ([1, 2, 3][let _ = ()]) - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:87:16 - | -LL | use_expr!((let 0 = 1 && 0 == 0)); - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:89:16 - | -LL | use_expr!((let 0 = 1)); - | ^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/disallowed-positions.rs:47:8 - | -LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^ - | - = note: see issue #53667 for more information - = help: add `#![feature(let_chains)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/disallowed-positions.rs:47:21 - | -LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^ - | - = note: see issue #53667 for more information - = help: add `#![feature(let_chains)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/disallowed-positions.rs:72:11 - | -LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^ - | - = note: see issue #53667 for more information - = help: add `#![feature(let_chains)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/disallowed-positions.rs:72:24 - | -LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {} - | ^^^^^^^^^ - | - = note: see issue #53667 for more information - = help: add `#![feature(let_chains)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/disallowed-positions.rs:348:8 - | -LL | if let Some(a) = opt && (true && true) { - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #53667 for more information - = help: add `#![feature(let_chains)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/disallowed-positions.rs:363:28 - | -LL | if (true && (true)) && let Some(a) = opt { - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #53667 for more information - = help: add `#![feature(let_chains)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/disallowed-positions.rs:365:18 - | -LL | if (true) && let Some(a) = opt { - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #53667 for more information - = help: add `#![feature(let_chains)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/disallowed-positions.rs:367:16 - | -LL | if true && let Some(a) = opt { - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #53667 for more information - = help: add `#![feature(let_chains)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `let` expressions in this position are unstable - --> $DIR/disallowed-positions.rs:371:8 - | -LL | if let true = (true && fun()) && (true) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #53667 for more information - = help: add `#![feature(let_chains)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:126:8 - | -LL | if true..(let 0 = 0) {} - | ^^^^^^^^^^^^^^^^^ expected `bool`, found `Range` - | - = note: expected type `bool` - found struct `std::ops::Range` - -error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:135:12 - | -LL | if let Range { start: _, end: _ } = true..true && false {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` - | | - | expected `bool`, found `Range<_>` - | - = note: expected type `bool` - found struct `std::ops::Range<_>` - -error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:138:12 - | -LL | if let Range { start: _, end: _ } = true..true || false {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` - | | - | expected `bool`, found `Range<_>` - | - = note: expected type `bool` - found struct `std::ops::Range<_>` - -error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:144:12 - | -LL | if let Range { start: F, end } = F..|| true {} - | ^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `fn() -> bool` - | | - | expected fn pointer, found `Range<_>` - | - = note: expected fn pointer `fn() -> bool` - found struct `std::ops::Range<_>` - -error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:150:12 - | -LL | if let Range { start: true, end } = t..&&false {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `&&bool` - | | - | expected `bool`, found `Range<_>` - | - = note: expected type `bool` - found struct `std::ops::Range<_>` - -error[E0277]: the `?` operator can only be applied to values that implement `Try` - --> $DIR/disallowed-positions.rs:106:20 - | -LL | if let 0 = 0? {} - | ^^ the `?` operator cannot be applied to type `{integer}` - | - = help: the trait `Try` is not implemented for `{integer}` - -error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:191:11 - | -LL | while true..(let 0 = 0) {} - | ^^^^^^^^^^^^^^^^^ expected `bool`, found `Range` - | - = note: expected type `bool` - found struct `std::ops::Range` - -error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:200:15 - | -LL | while let Range { start: _, end: _ } = true..true && false {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` - | | - | expected `bool`, found `Range<_>` - | - = note: expected type `bool` - found struct `std::ops::Range<_>` - -error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:203:15 - | -LL | while let Range { start: _, end: _ } = true..true || false {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` - | | - | expected `bool`, found `Range<_>` - | - = note: expected type `bool` - found struct `std::ops::Range<_>` - -error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:209:15 - | -LL | while let Range { start: F, end } = F..|| true {} - | ^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `fn() -> bool` - | | - | expected fn pointer, found `Range<_>` - | - = note: expected fn pointer `fn() -> bool` - found struct `std::ops::Range<_>` - -error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:215:15 - | -LL | while let Range { start: true, end } = t..&&false {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - this expression has type `&&bool` - | | - | expected `bool`, found `Range<_>` - | - = note: expected type `bool` - found struct `std::ops::Range<_>` - -error[E0277]: the `?` operator can only be applied to values that implement `Try` - --> $DIR/disallowed-positions.rs:171:23 - | -LL | while let 0 = 0? {} - | ^^ the `?` operator cannot be applied to type `{integer}` - | - = help: the trait `Try` is not implemented for `{integer}` - -error[E0308]: mismatched types - --> $DIR/disallowed-positions.rs:274:10 - | -LL | (let Range { start: _, end: _ } = true..true || false); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ---- this expression has type `bool` - | | - | expected `bool`, found `Range<_>` - | - = note: expected type `bool` - found struct `std::ops::Range<_>` - -error[E0277]: the `?` operator can only be applied to values that implement `Try` - --> $DIR/disallowed-positions.rs:249:17 - | -LL | let 0 = 0?; - | ^^ the `?` operator cannot be applied to type `{integer}` - | - = help: the trait `Try` is not implemented for `{integer}` - -error: aborting due to 114 previous errors - -Some errors have detailed explanations: E0277, E0308, E0658. -For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/rmeta/emit-artifact-notifications.polonius.stderr b/tests/ui/rmeta/emit-artifact-notifications.polonius.stderr deleted file mode 100644 index 255c7b370f9fd..0000000000000 --- a/tests/ui/rmeta/emit-artifact-notifications.polonius.stderr +++ /dev/null @@ -1 +0,0 @@ -{"artifact":"$TEST_BUILD_DIR/rmeta/emit-artifact-notifications.polonius/libemit_artifact_notifications.rmeta","emit":"metadata"} diff --git a/tests/ui/rust-2024/safe-outside-extern.gated.stderr b/tests/ui/rust-2024/safe-outside-extern.gated.stderr deleted file mode 100644 index e0b218281f365..0000000000000 --- a/tests/ui/rust-2024/safe-outside-extern.gated.stderr +++ /dev/null @@ -1,38 +0,0 @@ -error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier - --> $DIR/safe-outside-extern.rs:4:1 - | -LL | safe fn foo() {} - | ^^^^^^^^^^^^^^^^ - -error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier - --> $DIR/safe-outside-extern.rs:8:1 - | -LL | safe static FOO: i32 = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier - --> $DIR/safe-outside-extern.rs:13:5 - | -LL | safe fn foo(); - | ^^^^^^^^^^^^^^ - -error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier - --> $DIR/safe-outside-extern.rs:19:5 - | -LL | safe fn foo() {} - | ^^^^^^^^^^^^^^^^ - -error: function pointers cannot be declared with `safe` safety qualifier - --> $DIR/safe-outside-extern.rs:24:14 - | -LL | type FnPtr = safe fn(i32, i32) -> i32; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -error: static items cannot be declared with `unsafe` safety qualifier outside of `extern` block - --> $DIR/safe-outside-extern.rs:28:1 - | -LL | unsafe static LOL: u8 = 0; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 6 previous errors - diff --git a/tests/ui/rust-2024/safe-outside-extern.ungated.stderr b/tests/ui/rust-2024/safe-outside-extern.ungated.stderr deleted file mode 100644 index 98a4c0eab921a..0000000000000 --- a/tests/ui/rust-2024/safe-outside-extern.ungated.stderr +++ /dev/null @@ -1,89 +0,0 @@ -error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier - --> $DIR/safe-outside-extern.rs:4:1 - | -LL | safe fn foo() {} - | ^^^^^^^^^^^^^^^^ - -error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier - --> $DIR/safe-outside-extern.rs:8:1 - | -LL | safe static FOO: i32 = 1; - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier - --> $DIR/safe-outside-extern.rs:13:5 - | -LL | safe fn foo(); - | ^^^^^^^^^^^^^^ - -error: items outside of `unsafe extern { }` cannot be declared with `safe` safety qualifier - --> $DIR/safe-outside-extern.rs:19:5 - | -LL | safe fn foo() {} - | ^^^^^^^^^^^^^^^^ - -error: function pointers cannot be declared with `safe` safety qualifier - --> $DIR/safe-outside-extern.rs:24:14 - | -LL | type FnPtr = safe fn(i32, i32) -> i32; - | ^^^^^^^^^^^^^^^^^^^^^^^^ - -error: static items cannot be declared with `unsafe` safety qualifier outside of `extern` block - --> $DIR/safe-outside-extern.rs:28:1 - | -LL | unsafe static LOL: u8 = 0; - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0658]: `unsafe extern {}` blocks and `safe` keyword are experimental - --> $DIR/safe-outside-extern.rs:4:1 - | -LL | safe fn foo() {} - | ^^^^ - | - = note: see issue #123743 for more information - = help: add `#![feature(unsafe_extern_blocks)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `unsafe extern {}` blocks and `safe` keyword are experimental - --> $DIR/safe-outside-extern.rs:8:1 - | -LL | safe static FOO: i32 = 1; - | ^^^^ - | - = note: see issue #123743 for more information - = help: add `#![feature(unsafe_extern_blocks)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `unsafe extern {}` blocks and `safe` keyword are experimental - --> $DIR/safe-outside-extern.rs:13:5 - | -LL | safe fn foo(); - | ^^^^ - | - = note: see issue #123743 for more information - = help: add `#![feature(unsafe_extern_blocks)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `unsafe extern {}` blocks and `safe` keyword are experimental - --> $DIR/safe-outside-extern.rs:19:5 - | -LL | safe fn foo() {} - | ^^^^ - | - = note: see issue #123743 for more information - = help: add `#![feature(unsafe_extern_blocks)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: `unsafe extern {}` blocks and `safe` keyword are experimental - --> $DIR/safe-outside-extern.rs:24:14 - | -LL | type FnPtr = safe fn(i32, i32) -> i32; - | ^^^^ - | - = note: see issue #123743 for more information - = help: add `#![feature(unsafe_extern_blocks)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 11 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-impl-trait.ungated.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/safe-impl-trait.ungated.stderr deleted file mode 100644 index 80e7a45f57e79..0000000000000 --- a/tests/ui/rust-2024/unsafe-extern-blocks/safe-impl-trait.ungated.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: expected one of `!` or `::`, found keyword `impl` - --> $DIR/safe-impl-trait.rs:5:6 - | -LL | safe impl Bar for () { } - | ^^^^ expected one of `!` or `::` - -error: aborting due to 1 previous error - diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-trait.gated.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/safe-trait.gated.stderr deleted file mode 100644 index de84037f28c58..0000000000000 --- a/tests/ui/rust-2024/unsafe-extern-blocks/safe-trait.gated.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: expected one of `!` or `::`, found keyword `trait` - --> $DIR/safe-trait.rs:4:6 - | -LL | safe trait Foo {} - | ^^^^^ expected one of `!` or `::` - -error: aborting due to 1 previous error - diff --git a/tests/ui/rust-2024/unsafe-extern-blocks/safe-trait.ungated.stderr b/tests/ui/rust-2024/unsafe-extern-blocks/safe-trait.ungated.stderr deleted file mode 100644 index de84037f28c58..0000000000000 --- a/tests/ui/rust-2024/unsafe-extern-blocks/safe-trait.ungated.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: expected one of `!` or `::`, found keyword `trait` - --> $DIR/safe-trait.rs:4:6 - | -LL | safe trait Foo {} - | ^^^^^ expected one of `!` or `::` - -error: aborting due to 1 previous error - diff --git a/tests/ui/asan-odr-win/asan_odr_windows.rs b/tests/ui/sanitizer/asan_odr_windows.rs similarity index 100% rename from tests/ui/asan-odr-win/asan_odr_windows.rs rename to tests/ui/sanitizer/asan_odr_windows.rs diff --git a/tests/ui/asan-odr-win/auxiliary/asan_odr_win-2.rs b/tests/ui/sanitizer/auxiliary/asan_odr_win-2.rs similarity index 100% rename from tests/ui/asan-odr-win/auxiliary/asan_odr_win-2.rs rename to tests/ui/sanitizer/auxiliary/asan_odr_win-2.rs diff --git a/tests/ui/self/arbitrary_self_types_recursive_receiver.rs b/tests/ui/self/arbitrary_self_types_recursive_receiver.rs index f3e7f96d7c4de..8b1b6a8a105f3 100644 --- a/tests/ui/self/arbitrary_self_types_recursive_receiver.rs +++ b/tests/ui/self/arbitrary_self_types_recursive_receiver.rs @@ -1,6 +1,22 @@ //@ run-pass #![feature(arbitrary_self_types)] +// When probing for methods, we step forward through a chain of types. The first +// few of those steps can be reached by jumping through the chain of Derefs or the +// chain of Receivers. Later steps can only be reached by following the chain of +// Receivers. For instance, supposing A and B implement both Receiver and Deref, +// while C and D implement only Receiver: +// +// Type A>>> +// +// Deref chain: A -> B -> C +// Receiver chain: A -> B -> C -> D -> E +// +// We report bad type errors from the end of the chain. But at the end of which +// chain? We never morph the type as far as E so the correct behavior is to +// report errors from point C, i.e. the end of the Deref chain. This test case +// ensures we do that. + struct MyNonNull(*const T); impl std::ops::Receiver for MyNonNull { @@ -10,7 +26,13 @@ impl std::ops::Receiver for MyNonNull { #[allow(dead_code)] impl MyNonNull { fn foo(&self) -> *const U { - self.cast::().bar() + let mnn = self.cast::(); + // The following method call is the point of this test. + // If probe.rs reported errors from the last type discovered + // in the Receiver chain, it would be sad here because U is just + // a type variable. But this is a valid call so it ensures + // probe.rs doesn't make that mistake. + mnn.bar() } fn cast(&self) -> MyNonNull { MyNonNull(self.0 as *const U) diff --git a/tests/ui/specialization/specialization-default-items-drop-coherence.coherence.stderr b/tests/ui/specialization/specialization-default-items-drop-coherence.coherence.stderr deleted file mode 100644 index e9498a003179b..0000000000000 --- a/tests/ui/specialization/specialization-default-items-drop-coherence.coherence.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0119]: conflicting implementations of trait `Overlap` for type `u32` - --> $DIR/specialization-default-items-drop-coherence.rs:29:1 - | -LL | impl Overlap for u32 { - | -------------------- first implementation here -... -LL | impl Overlap for ::Id { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/specialization/specialization-overlap-projection.current.stderr b/tests/ui/specialization/specialization-overlap-projection.current.stderr deleted file mode 100644 index 4e77cb17fbb0a..0000000000000 --- a/tests/ui/specialization/specialization-overlap-projection.current.stderr +++ /dev/null @@ -1,30 +0,0 @@ -warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/specialization-overlap-projection.rs:4:12 - | -LL | #![feature(specialization)] - | ^^^^^^^^^^^^^^ - | - = note: see issue #31844 for more information - = help: consider using `min_specialization` instead, which is more stable and complete - = note: `#[warn(incomplete_features)]` on by default - -error[E0119]: conflicting implementations of trait `Foo` for type `u32` - --> $DIR/specialization-overlap-projection.rs:19:1 - | -LL | impl Foo for u32 {} - | ---------------- first implementation here -LL | impl Foo for ::Output {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32` - -error[E0119]: conflicting implementations of trait `Foo` for type `u32` - --> $DIR/specialization-overlap-projection.rs:21:1 - | -LL | impl Foo for u32 {} - | ---------------- first implementation here -... -LL | impl Foo for ::Output {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32` - -error: aborting due to 2 previous errors; 1 warning emitted - -For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/specialization/specialization-overlap-projection.next.stderr b/tests/ui/specialization/specialization-overlap-projection.next.stderr deleted file mode 100644 index 4e77cb17fbb0a..0000000000000 --- a/tests/ui/specialization/specialization-overlap-projection.next.stderr +++ /dev/null @@ -1,30 +0,0 @@ -warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/specialization-overlap-projection.rs:4:12 - | -LL | #![feature(specialization)] - | ^^^^^^^^^^^^^^ - | - = note: see issue #31844 for more information - = help: consider using `min_specialization` instead, which is more stable and complete - = note: `#[warn(incomplete_features)]` on by default - -error[E0119]: conflicting implementations of trait `Foo` for type `u32` - --> $DIR/specialization-overlap-projection.rs:19:1 - | -LL | impl Foo for u32 {} - | ---------------- first implementation here -LL | impl Foo for ::Output {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32` - -error[E0119]: conflicting implementations of trait `Foo` for type `u32` - --> $DIR/specialization-overlap-projection.rs:21:1 - | -LL | impl Foo for u32 {} - | ---------------- first implementation here -... -LL | impl Foo for ::Output {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `u32` - -error: aborting due to 2 previous errors; 1 warning emitted - -For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/stable-mir-print/operands.stdout b/tests/ui/stable-mir-print/operands.stdout index 3c27878b3cf0f..c3b1151ae24a3 100644 --- a/tests/ui/stable-mir-print/operands.stdout +++ b/tests/ui/stable-mir-print/operands.stdout @@ -5,187 +5,183 @@ fn operands(_1: u8) -> () { let _2: [u8; 10]; let _3: u8; let _4: usize; - let mut _5: usize; - let mut _6: bool; - let _7: u8; - let _8: usize; - let mut _9: (usize, bool); - let mut _10: usize; - let mut _11: bool; - let mut _12: (&u8, &u8); - let mut _13: &u8; - let mut _14: &u8; - let _15: &u8; - let _16: &u8; - let mut _17: bool; - let mut _18: u8; - let mut _19: u8; - let _20: core::panicking::AssertKind; - let _21: !; - let mut _22: Option>; - let _23: &u8; - let _24: u8; - let mut _25: (&u8, &u8); - let mut _26: &u8; - let mut _27: &u8; - let _28: &u8; - let _29: &u8; - let mut _30: bool; - let mut _31: u8; - let mut _32: u8; - let _33: core::panicking::AssertKind; - let _34: !; - let mut _35: Option>; - let _36: (u8, u8); - let _37: u8; - let _38: u8; - let mut _39: (&u8, &u8); - let mut _40: &u8; - let mut _41: &u8; - let _42: &u8; - let _43: &u8; - let mut _44: bool; - let mut _45: u8; - let mut _46: u8; - let _47: core::panicking::AssertKind; - let _48: !; - let mut _49: Option>; - let _50: usize; - let mut _51: &[u8]; - let mut _52: &[u8; 10]; - let _53: usize; - let _54: &usize; - let mut _55: (&usize, &usize); - let mut _56: &usize; - let mut _57: &usize; - let _58: &usize; - let _59: &usize; - let mut _60: bool; - let mut _61: usize; - let mut _62: usize; - let _63: core::panicking::AssertKind; - let _64: !; - let mut _65: Option>; + let mut _5: bool; + let _6: u8; + let _7: usize; + let mut _8: (usize, bool); + let mut _9: bool; + let mut _10: (&u8, &u8); + let mut _11: &u8; + let mut _12: &u8; + let _13: &u8; + let _14: &u8; + let mut _15: bool; + let mut _16: u8; + let mut _17: u8; + let _18: core::panicking::AssertKind; + let _19: !; + let mut _20: Option>; + let _21: &u8; + let _22: u8; + let mut _23: (&u8, &u8); + let mut _24: &u8; + let mut _25: &u8; + let _26: &u8; + let _27: &u8; + let mut _28: bool; + let mut _29: u8; + let mut _30: u8; + let _31: core::panicking::AssertKind; + let _32: !; + let mut _33: Option>; + let _34: (u8, u8); + let _35: u8; + let _36: u8; + let mut _37: (&u8, &u8); + let mut _38: &u8; + let mut _39: &u8; + let _40: &u8; + let _41: &u8; + let mut _42: bool; + let mut _43: u8; + let mut _44: u8; + let _45: core::panicking::AssertKind; + let _46: !; + let mut _47: Option>; + let _48: usize; + let mut _49: &[u8]; + let mut _50: &[u8; 10]; + let _51: usize; + let _52: &usize; + let mut _53: (&usize, &usize); + let mut _54: &usize; + let mut _55: &usize; + let _56: &usize; + let _57: &usize; + let mut _58: bool; + let mut _59: usize; + let mut _60: usize; + let _61: core::panicking::AssertKind; + let _62: !; + let mut _63: Option>; debug val => _1; debug array => _2; debug first => _3; - debug last => _7; - debug left_val => _15; - debug right_val => _16; - debug kind => _20; - debug reference => _23; - debug dereferenced => _24; - debug left_val => _28; - debug right_val => _29; - debug kind => _33; - debug tuple => _36; - debug first_again => _37; - debug first_again_again => _38; - debug left_val => _42; - debug right_val => _43; - debug kind => _47; - debug length => _50; - debug size_of => _53; - debug left_val => _58; - debug right_val => _59; - debug kind => _63; + debug last => _6; + debug left_val => _13; + debug right_val => _14; + debug kind => _18; + debug reference => _21; + debug dereferenced => _22; + debug left_val => _26; + debug right_val => _27; + debug kind => _31; + debug tuple => _34; + debug first_again => _35; + debug first_again_again => _36; + debug left_val => _40; + debug right_val => _41; + debug kind => _45; + debug length => _48; + debug size_of => _51; + debug left_val => _56; + debug right_val => _57; + debug kind => _61; bb0: { _2 = [_1; 10]; _4 = 0_usize; - _5 = 10_usize; - _6 = Lt(_4, _5); - assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind unreachable]; + _5 = Lt(_4, 10_usize); + assert(move _5, "index out of bounds: the length is {} but the index is {}", 10_usize, _4) -> [success: bb1, unwind unreachable]; } bb1: { _3 = _2[_4]; - _9 = CheckedSub(10_usize, 1_usize); - assert(!move (_9.1: bool), "attempt to compute `{} - {}`, which would overflow", 10_usize, 1_usize) -> [success: bb2, unwind unreachable]; + _8 = CheckedSub(10_usize, 1_usize); + assert(!move (_8.1: bool), "attempt to compute `{} - {}`, which would overflow", 10_usize, 1_usize) -> [success: bb2, unwind unreachable]; } bb2: { - _8 = move (_9.0: usize); - _10 = 10_usize; - _11 = Lt(_8, _10); - assert(move _11, "index out of bounds: the length is {} but the index is {}", move _10, _8) -> [success: bb3, unwind unreachable]; + _7 = move (_8.0: usize); + _9 = Lt(_7, 10_usize); + assert(move _9, "index out of bounds: the length is {} but the index is {}", 10_usize, _7) -> [success: bb3, unwind unreachable]; } bb3: { - _7 = _2[_8]; - _13 = &_3; - _14 = &_7; - _12 = (move _13, move _14); - _15 = (_12.0: &u8); - _16 = (_12.1: &u8); - _18 = (*_15); - _19 = (*_16); - _17 = Eq(move _18, move _19); - switchInt(move _17) -> [0: bb5, otherwise: bb4]; + _6 = _2[_7]; + _11 = &_3; + _12 = &_6; + _10 = (move _11, move _12); + _13 = (_10.0: &u8); + _14 = (_10.1: &u8); + _16 = (*_13); + _17 = (*_14); + _15 = Eq(move _16, move _17); + switchInt(move _15) -> [0: bb5, otherwise: bb4]; } bb4: { - _23 = &_3; - _24 = (*_23); - _26 = &_24; - _27 = &_3; - _25 = (move _26, move _27); - _28 = (_25.0: &u8); - _29 = (_25.1: &u8); - _31 = (*_28); - _32 = (*_29); - _30 = Eq(move _31, move _32); - switchInt(move _30) -> [0: bb7, otherwise: bb6]; + _21 = &_3; + _22 = (*_21); + _24 = &_22; + _25 = &_3; + _23 = (move _24, move _25); + _26 = (_23.0: &u8); + _27 = (_23.1: &u8); + _29 = (*_26); + _30 = (*_27); + _28 = Eq(move _29, move _30); + switchInt(move _28) -> [0: bb7, otherwise: bb6]; } bb5: { - _20 = core::panicking::AssertKind::Eq; - _22 = std::option::Option::None; - _21 = core::panicking::assert_failed::(move _20, _15, _16, move _22) -> unwind unreachable; + _18 = core::panicking::AssertKind::Eq; + _20 = std::option::Option::None; + _19 = core::panicking::assert_failed::(move _18, _13, _14, move _20) -> unwind unreachable; } bb6: { - _36 = (_3, _7); - _37 = (_36.0: u8); - _38 = (_36.0: u8); - _40 = &_37; - _41 = &_38; - _39 = (move _40, move _41); - _42 = (_39.0: &u8); - _43 = (_39.1: &u8); - _45 = (*_42); - _46 = (*_43); - _44 = Eq(move _45, move _46); - switchInt(move _44) -> [0: bb9, otherwise: bb8]; + _34 = (_3, _6); + _35 = (_34.0: u8); + _36 = (_34.0: u8); + _38 = &_35; + _39 = &_36; + _37 = (move _38, move _39); + _40 = (_37.0: &u8); + _41 = (_37.1: &u8); + _43 = (*_40); + _44 = (*_41); + _42 = Eq(move _43, move _44); + switchInt(move _42) -> [0: bb9, otherwise: bb8]; } bb7: { - _33 = core::panicking::AssertKind::Eq; - _35 = std::option::Option::None; - _34 = core::panicking::assert_failed::(move _33, _28, _29, move _35) -> unwind unreachable; + _31 = core::panicking::AssertKind::Eq; + _33 = std::option::Option::None; + _32 = core::panicking::assert_failed::(move _31, _26, _27, move _33) -> unwind unreachable; } bb8: { - _52 = &_2; - _51 = move _52 as &[u8]; - _50 = PtrMetadata(move _51); - _54 = &_50; - _53 = std::mem::size_of_val::(_54) -> [return: bb10, unwind unreachable]; + _50 = &_2; + _49 = move _50 as &[u8]; + _48 = PtrMetadata(move _49); + _52 = &_48; + _51 = std::mem::size_of_val::(_52) -> [return: bb10, unwind unreachable]; } bb9: { - _47 = core::panicking::AssertKind::Eq; - _49 = std::option::Option::None; - _48 = core::panicking::assert_failed::(move _47, _42, _43, move _49) -> unwind unreachable; + _45 = core::panicking::AssertKind::Eq; + _47 = std::option::Option::None; + _46 = core::panicking::assert_failed::(move _45, _40, _41, move _47) -> unwind unreachable; } bb10: { - _56 = &_50; - _57 = &_53; - _55 = (move _56, move _57); - _58 = (_55.0: &usize); - _59 = (_55.1: &usize); - _61 = (*_58); - _62 = (*_59); - _60 = Eq(move _61, move _62); - switchInt(move _60) -> [0: bb12, otherwise: bb11]; + _54 = &_48; + _55 = &_51; + _53 = (move _54, move _55); + _56 = (_53.0: &usize); + _57 = (_53.1: &usize); + _59 = (*_56); + _60 = (*_57); + _58 = Eq(move _59, move _60); + switchInt(move _58) -> [0: bb12, otherwise: bb11]; } bb11: { return; } bb12: { - _63 = core::panicking::AssertKind::Eq; - _65 = std::option::Option::None; - _64 = core::panicking::assert_failed::(move _63, _58, _59, move _65) -> unwind unreachable; + _61 = core::panicking::AssertKind::Eq; + _63 = std::option::Option::None; + _62 = core::panicking::assert_failed::(move _61, _56, _57, move _63) -> unwind unreachable; } } fn operands::{constant#0}() -> usize { diff --git a/tests/ui/issues/auxiliary/issue-111011.rs b/tests/ui/suggestions/dont-suggest-boxing-async-closure-body.rs similarity index 53% rename from tests/ui/issues/auxiliary/issue-111011.rs rename to tests/ui/suggestions/dont-suggest-boxing-async-closure-body.rs index 0c1a8ce1cf6e7..47a590668dde2 100644 --- a/tests/ui/issues/auxiliary/issue-111011.rs +++ b/tests/ui/suggestions/dont-suggest-boxing-async-closure-body.rs @@ -1,10 +1,12 @@ //@ edition:2021 +// issue: https://github.com/rust-lang/rust/issues/111011 fn foo(x: impl FnOnce() -> Box) {} // just to make sure async closures can still be suggested for boxing. fn bar(x: Box X>) {} fn main() { - foo(async move || {}); //~ ERROR mismatched types + foo(async move || {}); + //~^ ERROR expected `{async closure@dont-suggest-boxing-async-closure-body.rs:9:9}` to be a closure that returns `Box<_>` bar(async move || {}); //~ ERROR mismatched types } diff --git a/tests/ui/suggestions/dont-suggest-boxing-async-closure-body.stderr b/tests/ui/suggestions/dont-suggest-boxing-async-closure-body.stderr new file mode 100644 index 0000000000000..db2a3b9a9c15f --- /dev/null +++ b/tests/ui/suggestions/dont-suggest-boxing-async-closure-body.stderr @@ -0,0 +1,41 @@ +error[E0271]: expected `{async closure@dont-suggest-boxing-async-closure-body.rs:9:9}` to be a closure that returns `Box<_>`, but it returns `{async closure body@$DIR/dont-suggest-boxing-async-closure-body.rs:9:23: 9:25}` + --> $DIR/dont-suggest-boxing-async-closure-body.rs:9:9 + | +LL | foo(async move || {}); + | --- ^^^^^^^^^^^^^^^^ expected `Box<_>`, found `async` closure body + | | + | required by a bound introduced by this call + | + = note: expected struct `Box<_>` + found `async` closure body `{async closure body@$DIR/dont-suggest-boxing-async-closure-body.rs:9:23: 9:25}` +note: required by a bound in `foo` + --> $DIR/dont-suggest-boxing-async-closure-body.rs:4:31 + | +LL | fn foo(x: impl FnOnce() -> Box) {} + | ^^^^^^ required by this bound in `foo` + +error[E0308]: mismatched types + --> $DIR/dont-suggest-boxing-async-closure-body.rs:11:9 + | +LL | bar(async move || {}); + | --- ^^^^^^^^^^^^^^^^ expected `Box _>`, found `{async closure@dont-suggest-boxing-async-closure-body.rs:11:9}` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box<(dyn FnOnce() -> _ + 'static)>` + found closure `{async closure@$DIR/dont-suggest-boxing-async-closure-body.rs:11:9: 11:22}` + = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html +note: function defined here + --> $DIR/dont-suggest-boxing-async-closure-body.rs:6:4 + | +LL | fn bar(x: Box X>) {} + | ^^^ ------------------------- +help: store this in the heap by calling `Box::new` + | +LL | bar(Box::new(async move || {})); + | +++++++++ + + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0271, E0308. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/suggestions/suggest-ref-mut.rs b/tests/ui/suggestions/suggest-ref-mut.rs index b40439b8e372c..9f5df9303c330 100644 --- a/tests/ui/suggestions/suggest-ref-mut.rs +++ b/tests/ui/suggestions/suggest-ref-mut.rs @@ -3,7 +3,7 @@ struct X(usize); impl X { fn zap(&self) { //~^ HELP - //~| SUGGESTION &mut self + //~| SUGGESTION mut self.0 = 32; //~^ ERROR } diff --git a/tests/ui/suggestions/suggest-ref-mut.stderr b/tests/ui/suggestions/suggest-ref-mut.stderr index cc00022ab8e3d..935a04c052ac9 100644 --- a/tests/ui/suggestions/suggest-ref-mut.stderr +++ b/tests/ui/suggestions/suggest-ref-mut.stderr @@ -7,7 +7,7 @@ LL | self.0 = 32; help: consider changing this to be a mutable reference | LL | fn zap(&mut self) { - | ~~~~~~~~~ + | +++ error[E0594]: cannot assign to `*foo`, which is behind a `&` reference --> $DIR/suggest-ref-mut.rs:15:5 diff --git a/tests/ui/target-feature/feature-hierarchy.aarch64-sve2.stderr b/tests/ui/target-feature/feature-hierarchy.aarch64-sve2.stderr deleted file mode 100644 index b6c3ccdedfb00..0000000000000 --- a/tests/ui/target-feature/feature-hierarchy.aarch64-sve2.stderr +++ /dev/null @@ -1,7 +0,0 @@ -warning: target feature `neon` cannot be disabled with `-Ctarget-feature`: this feature is required by the target ABI - | - = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #116344 - -warning: 1 warning emitted - diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr index 72b2d03fe2036..7ec8b04cfce01 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-implied.stderr @@ -1,4 +1,4 @@ -warning: target feature `sse` cannot be disabled with `-Ctarget-feature`: this feature is required by the target ABI +warning: target feature `sse2` must be enabled to ensure that the ABI of the current target can be implemented correctly | = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #116344 diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr index b6c3ccdedfb00..b1186d5d5dc78 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable-neon.stderr @@ -1,4 +1,4 @@ -warning: target feature `neon` cannot be disabled with `-Ctarget-feature`: this feature is required by the target ABI +warning: target feature `neon` must be enabled to ensure that the ABI of the current target can be implemented correctly | = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #116344 diff --git a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr index 6191681286a36..02398d27501c8 100644 --- a/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr +++ b/tests/ui/target-feature/forbidden-hardfloat-target-feature-flag-disable.stderr @@ -1,11 +1,11 @@ -warning: unstable feature specified for `-Ctarget-feature`: `x87` - | - = note: this feature is not stably supported; its behavior can change in the future - -warning: target feature `x87` cannot be disabled with `-Ctarget-feature`: this feature is required by the target ABI +warning: target feature `x87` must be enabled to ensure that the ABI of the current target can be implemented correctly | = note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #116344 +warning: unstable feature specified for `-Ctarget-feature`: `x87` + | + = note: this feature is not stably supported; its behavior can change in the future + warning: 2 warnings emitted diff --git a/tests/ui/traits/const-traits/const-impl-trait.stderr b/tests/ui/traits/const-traits/const-impl-trait.stderr index 4e3200594485e..27d7957c00148 100644 --- a/tests/ui/traits/const-traits/const-impl-trait.stderr +++ b/tests/ui/traits/const-traits/const-impl-trait.stderr @@ -99,6 +99,16 @@ note: `PartialEq` can't be used with `~const` because it isn't annotated with `# --> $SRC_DIR/core/src/cmp.rs:LL:COL = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +error: `~const` can only be applied to `#[const_trait]` traits + --> $DIR/const-impl-trait.rs:27:22 + | +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy { + | ^^^^^^ can't be applied to `PartialEq` + | +note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error: `~const` can only be applied to `#[const_trait]` traits --> $DIR/const-impl-trait.rs:23:22 | @@ -120,9 +130,9 @@ note: `PartialEq` can't be used with `~const` because it isn't annotated with `# = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: `~const` can only be applied to `#[const_trait]` traits - --> $DIR/const-impl-trait.rs:27:22 + --> $DIR/const-impl-trait.rs:23:22 | -LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy { +LL | fn huh() -> impl ~const PartialEq + ~const Destruct + Copy; | ^^^^^^ can't be applied to `PartialEq` | note: `PartialEq` can't be used with `~const` because it isn't annotated with `#[const_trait]` @@ -181,7 +191,7 @@ LL | a == a | = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants -error: aborting due to 20 previous errors +error: aborting due to 21 previous errors Some errors have detailed explanations: E0015, E0635. For more information about an error, try `rustc --explain E0015`. diff --git a/tests/ui/traits/const-traits/staged-api-user-crate.rs b/tests/ui/traits/const-traits/staged-api-user-crate.rs index 7587042cf276d..4aa75a50355b0 100644 --- a/tests/ui/traits/const-traits/staged-api-user-crate.rs +++ b/tests/ui/traits/const-traits/staged-api-user-crate.rs @@ -11,6 +11,7 @@ fn non_const_context() { const fn stable_const_context() { Unstable::func(); //~^ ERROR cannot call conditionally-const associated function `::func` in constant functions + //~| ERROR `staged_api::MyTrait` is not yet stable as a const trait } fn main() {} diff --git a/tests/ui/traits/const-traits/staged-api-user-crate.stderr b/tests/ui/traits/const-traits/staged-api-user-crate.stderr index 400c76fcaf494..8ac83770cf7a4 100644 --- a/tests/ui/traits/const-traits/staged-api-user-crate.stderr +++ b/tests/ui/traits/const-traits/staged-api-user-crate.stderr @@ -9,6 +9,17 @@ LL | Unstable::func(); = help: add `#![feature(const_trait_impl)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date -error: aborting due to 1 previous error +error: `staged_api::MyTrait` is not yet stable as a const trait + --> $DIR/staged-api-user-crate.rs:12:5 + | +LL | Unstable::func(); + | ^^^^^^^^^^^^^^^^ + | +help: add `#![feature(unstable)]` to the crate attributes to enable + | +LL + #![feature(unstable)] + | + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/traits/const-traits/staged-api.rs b/tests/ui/traits/const-traits/staged-api.rs index 755a4e456bcf9..9a030dafd6bc4 100644 --- a/tests/ui/traits/const-traits/staged-api.rs +++ b/tests/ui/traits/const-traits/staged-api.rs @@ -22,7 +22,7 @@ impl const MyTrait for Foo { fn func() {} } -#[rustc_allow_const_fn_unstable(const_trait_impl)] +#[rustc_allow_const_fn_unstable(const_trait_impl, unstable)] const fn conditionally_const() { T::func(); } @@ -37,10 +37,13 @@ fn non_const_context() { const fn const_context() { Unstable::func(); //~^ ERROR cannot use `#[feature(const_trait_impl)]` + //~| ERROR cannot use `#[feature(unstable)]` Foo::func(); //~^ ERROR cannot use `#[feature(const_trait_impl)]` + //~| ERROR cannot use `#[feature(unstable)]` Unstable2::func(); //~^ ERROR cannot use `#[feature(const_trait_impl)]` + //~| ERROR cannot use `#[feature(unstable)]` conditionally_const::(); //~^ ERROR cannot use `#[feature(const_trait_impl)]` } @@ -59,8 +62,23 @@ pub const fn const_context_not_const_stable() { const fn stable_const_context() { Unstable::func(); //~^ ERROR cannot use `#[feature(const_trait_impl)]` + //~| ERROR cannot use `#[feature(unstable)]` Foo::func(); //~^ ERROR cannot use `#[feature(const_trait_impl)]` + //~| ERROR cannot use `#[feature(unstable)]` + const_context_not_const_stable(); + //~^ ERROR cannot use `#[feature(local_feature)]` + conditionally_const::(); + //~^ ERROR cannot use `#[feature(const_trait_impl)]` +} + +const fn implicitly_stable_const_context() { + Unstable::func(); + //~^ ERROR cannot use `#[feature(const_trait_impl)]` + //~| ERROR cannot use `#[feature(unstable)]` + Foo::func(); + //~^ ERROR cannot use `#[feature(const_trait_impl)]` + //~| ERROR cannot use `#[feature(unstable)]` const_context_not_const_stable(); //~^ ERROR cannot use `#[feature(local_feature)]` conditionally_const::(); diff --git a/tests/ui/traits/const-traits/staged-api.stderr b/tests/ui/traits/const-traits/staged-api.stderr index acc93f747a8ec..a7a7a1ee7215b 100644 --- a/tests/ui/traits/const-traits/staged-api.stderr +++ b/tests/ui/traits/const-traits/staged-api.stderr @@ -15,8 +15,25 @@ LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn const_context() { | +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(unstable)]` + --> $DIR/staged-api.rs:38:5 + | +LL | Unstable::func(); + | ^^^^^^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(unstable)] +LL | const fn const_context() { + | + error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` - --> $DIR/staged-api.rs:40:5 + --> $DIR/staged-api.rs:41:5 | LL | Foo::func(); | ^^^^^^^^^^^ @@ -32,8 +49,25 @@ LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn const_context() { | +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(unstable)]` + --> $DIR/staged-api.rs:41:5 + | +LL | Foo::func(); + | ^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(unstable)] +LL | const fn const_context() { + | + error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` - --> $DIR/staged-api.rs:42:5 + --> $DIR/staged-api.rs:44:5 | LL | Unstable2::func(); | ^^^^^^^^^^^^^^^^^ @@ -49,9 +83,26 @@ LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn const_context() { | -error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(unstable)]` --> $DIR/staged-api.rs:44:5 | +LL | Unstable2::func(); + | ^^^^^^^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(unstable)] +LL | const fn const_context() { + | + +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` + --> $DIR/staged-api.rs:47:5 + | LL | conditionally_const::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | @@ -67,7 +118,7 @@ LL | const fn const_context() { | error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` - --> $DIR/staged-api.rs:60:5 + --> $DIR/staged-api.rs:63:5 | LL | Unstable::func(); | ^^^^^^^^^^^^^^^^ @@ -83,8 +134,25 @@ LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn stable_const_context() { | +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(unstable)]` + --> $DIR/staged-api.rs:63:5 + | +LL | Unstable::func(); + | ^^^^^^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(unstable)] +LL | const fn stable_const_context() { + | + error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` - --> $DIR/staged-api.rs:62:5 + --> $DIR/staged-api.rs:66:5 | LL | Foo::func(); | ^^^^^^^^^^^ @@ -100,8 +168,25 @@ LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn stable_const_context() { | +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(unstable)]` + --> $DIR/staged-api.rs:66:5 + | +LL | Foo::func(); + | ^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(unstable)] +LL | const fn stable_const_context() { + | + error: const function that might be (indirectly) exposed to stable cannot use `#[feature(local_feature)]` - --> $DIR/staged-api.rs:64:5 + --> $DIR/staged-api.rs:69:5 | LL | const_context_not_const_stable(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -119,7 +204,7 @@ LL | const fn stable_const_context() { | error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` - --> $DIR/staged-api.rs:66:5 + --> $DIR/staged-api.rs:71:5 | LL | conditionally_const::(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -135,5 +220,108 @@ LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] LL | const fn stable_const_context() { | -error: aborting due to 8 previous errors +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` + --> $DIR/staged-api.rs:76:5 + | +LL | Unstable::func(); + | ^^^^^^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn implicitly_stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] +LL | const fn implicitly_stable_const_context() { + | + +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(unstable)]` + --> $DIR/staged-api.rs:76:5 + | +LL | Unstable::func(); + | ^^^^^^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn implicitly_stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(unstable)] +LL | const fn implicitly_stable_const_context() { + | + +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` + --> $DIR/staged-api.rs:79:5 + | +LL | Foo::func(); + | ^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn implicitly_stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] +LL | const fn implicitly_stable_const_context() { + | + +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(unstable)]` + --> $DIR/staged-api.rs:79:5 + | +LL | Foo::func(); + | ^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn implicitly_stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(unstable)] +LL | const fn implicitly_stable_const_context() { + | + +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(local_feature)]` + --> $DIR/staged-api.rs:82:5 + | +LL | const_context_not_const_stable(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: mark the callee as `#[rustc_const_stable_indirect]` if it does not itself require any unstable features +help: if the caller is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn implicitly_stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(local_feature)] +LL | const fn implicitly_stable_const_context() { + | + +error: const function that might be (indirectly) exposed to stable cannot use `#[feature(const_trait_impl)]` + --> $DIR/staged-api.rs:84:5 + | +LL | conditionally_const::(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: if the function is not (yet) meant to be exposed to stable, add `#[rustc_const_unstable]` (this is what you probably want to do) + | +LL + #[rustc_const_unstable(feature = "...", issue = "...")] +LL | const fn implicitly_stable_const_context() { + | +help: otherwise, as a last resort `#[rustc_allow_const_fn_unstable]` can be used to bypass stability checks (this requires team approval) + | +LL + #[rustc_allow_const_fn_unstable(const_trait_impl)] +LL | const fn implicitly_stable_const_context() { + | + +error: aborting due to 19 previous errors diff --git a/tests/ui/traits/next-solver/closure-signature-inference-hr-ambig-alias-naming-self.rs b/tests/ui/traits/next-solver/closure-signature-inference-hr-ambig-alias-naming-self.rs new file mode 100644 index 0000000000000..25649d9290326 --- /dev/null +++ b/tests/ui/traits/next-solver/closure-signature-inference-hr-ambig-alias-naming-self.rs @@ -0,0 +1,52 @@ +//@ check-pass +//@ revisions: current next +//@[next] compile-flags: -Znext-solver + +// When type checking a closure expr we look at the list of unsolved goals +// to determine if there are any bounds on the closure type to infer a signature from. +// +// We attempt to discard goals that name the closure type so as to avoid inferring the +// closure type to something like `?x = closure(sig=fn(?x))`. This test checks that when +// such a goal names the closure type inside of an ambiguous alias and there exists another +// potential goal to infer the closure signature from, we do that. + +trait Trait<'a> { + type Assoc; +} + +impl<'a, F> Trait<'a> for F { + type Assoc = u32; +} + +fn closure_typer1(_: F) +where + F: Fn(u32) + for<'a> Fn(>::Assoc), +{ +} + +fn closure_typer2(_: F) +where + F: for<'a> Fn(>::Assoc) + Fn(u32), +{ +} + +fn main() { + // Here we have some closure with a yet to be inferred type of `?c`. There are two goals + // involving `?c` that can be used to determine the closure signature: + // - `?c: for<'a> Fn<(>::Assoc,), Output = ()>` + // - `?c: Fn<(u32,), Output = ()>` + // + // If we were to infer the argument of the closure (`x` below) to `>::Assoc` + // then we would not be able to call `x.into()` as `x` is some unknown type. Instead we must + // use the `?c: Fn(u32)` goal to infer a signature in order for this code to compile. + // + // As the algorithm for picking a goal to infer the signature from is dependent on the ordering + // of pending goals in the type checker, we test both orderings of bounds to ensure we aren't + // testing that we just *happen* to pick `?c: Fn(u32)`. + closure_typer1(move |x| { + let _: u32 = x.into(); + }); + closure_typer2(move |x| { + let _: u32 = x.into(); + }); +} diff --git a/tests/ui/traits/next-solver/coherence/issue-102048.next.stderr b/tests/ui/traits/next-solver/coherence/issue-102048.next.stderr deleted file mode 100644 index 39fde307f23f1..0000000000000 --- a/tests/ui/traits/next-solver/coherence/issue-102048.next.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error[E0119]: conflicting implementations of trait `Trait fn(<_ as WithAssoc1<'a>>::Assoc, <_ as WithAssoc2<'a>>::Assoc)>` for type `(_, _)` - --> $DIR/issue-102048.rs:44:1 - | -LL | / impl Trait fn(>::Assoc, >::Assoc)> for (T, U) -LL | | where -LL | | T: for<'a> WithAssoc1<'a> + for<'a> WithAssoc2<'a, Assoc = i32>, -LL | | U: for<'a> WithAssoc2<'a>, - | |______________________________- first implementation here -... -LL | / impl Trait fn(>::Assoc, u32)> for (T, U) where -LL | | U: for<'a> WithAssoc1<'a> - | |_____________________________^ conflicting implementation for `(_, _)` - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-3.polonius.stderr b/tests/ui/traits/trait-upcasting/type-checking-test-3.polonius.stderr deleted file mode 100644 index e6cb6a753998f..0000000000000 --- a/tests/ui/traits/trait-upcasting/type-checking-test-3.polonius.stderr +++ /dev/null @@ -1,18 +0,0 @@ -error: lifetime may not live long enough - --> $DIR/type-checking-test-3.rs:11:13 - | -LL | fn test_wrong1<'a>(x: &dyn Foo<'static>, y: &'a u32) { - | -- lifetime `'a` defined here -LL | let _ = x as &dyn Bar<'a>; // Error - | ^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` - -error: lifetime may not live long enough - --> $DIR/type-checking-test-3.rs:16:13 - | -LL | fn test_wrong2<'a>(x: &dyn Foo<'a>) { - | -- lifetime `'a` defined here -LL | let _ = x as &dyn Bar<'static>; // Error - | ^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` - -error: aborting due to 2 previous errors - diff --git a/tests/ui/traits/trait-upcasting/type-checking-test-4.polonius.stderr b/tests/ui/traits/trait-upcasting/type-checking-test-4.polonius.stderr deleted file mode 100644 index 8d506e5807ece..0000000000000 --- a/tests/ui/traits/trait-upcasting/type-checking-test-4.polonius.stderr +++ /dev/null @@ -1,52 +0,0 @@ -error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:15:13 - | -LL | fn test_wrong1<'a>(x: &dyn Foo<'static>, y: &'a u32) { - | -- lifetime `'a` defined here -LL | let _ = x as &dyn Bar<'static, 'a>; // Error - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` - -error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:20:13 - | -LL | fn test_wrong2<'a>(x: &dyn Foo<'static>, y: &'a u32) { - | -- lifetime `'a` defined here -LL | let _ = x as &dyn Bar<'a, 'static>; // Error - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` - -error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:26:5 - | -LL | fn test_wrong3<'a>(x: &dyn Foo<'a>) -> Option<&'static u32> { - | -- lifetime `'a` defined here -LL | let y = x as &dyn Bar<'_, '_>; -LL | y.get_b() // ERROR - | ^^^^^^^^^ returning this value requires that `'a` must outlive `'static` - -error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:31:5 - | -LL | fn test_wrong4<'a>(x: &dyn Foo<'a>) -> Option<&'static u32> { - | -- lifetime `'a` defined here -LL | <_ as Bar>::get_b(x) // ERROR - | ^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static` - -error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:36:5 - | -LL | fn test_wrong5<'a>(x: &dyn Foo<'a>) -> Option<&'static u32> { - | -- lifetime `'a` defined here -LL | <_ as Bar<'_, '_>>::get_b(x) // ERROR - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static` - -error: lifetime may not live long enough - --> $DIR/type-checking-test-4.rs:44:5 - | -LL | fn test_wrong6<'a>(x: &dyn Foo<'a>) -> Option<&'static u32> { - | -- lifetime `'a` defined here -... -LL | z.get_b() // ERROR - | ^^^^^^^^^ returning this value requires that `'a` must outlive `'static` - -error: aborting due to 6 previous errors - diff --git a/tests/ui/treat-err-as-bug/panic-causes-oom-112708.stderr b/tests/ui/treat-err-as-bug/panic-causes-oom-112708.stderr deleted file mode 100644 index 2d49071ac49cf..0000000000000 --- a/tests/ui/treat-err-as-bug/panic-causes-oom-112708.stderr +++ /dev/null @@ -1,32 +0,0 @@ -error: denote infinite loops with `loop { ... }` - --> $DIR/panic-causes-oom-112708.rs:13:5 - | -LL | while true {} - | ^^^^^^^^^^ help: use `loop` - | -note: the lint level is defined here - --> $DIR/panic-causes-oom-112708.rs:12:12 - | -LL | #[deny(while_true)] - | ^^^^^^^^^^ - - -query stack during panic: -#0 [early_lint_checks] perform lints prior to macro expansion -#1 [hir_crate] getting the crate HIR -end of query stack - -error: the compiler unexpectedly panicked. this is a bug. - -query stack during panic: -#0 [early_lint_checks] perform lints prior to macro expansion -#1 [hir_crate] getting the crate HIR -end of query stack - -error: the compiler unexpectedly panicked. this is a bug. - -query stack during panic: -#0 [early_lint_checks] perform lints prior to macro expansion -#1 [hir_crate] getting the crate HIR -end of query stack -thread caused non-unwinding panic. aborting. diff --git a/tests/ui/type-alias-impl-trait/wf-nested.fail.stderr b/tests/ui/type-alias-impl-trait/wf-nested.fail.stderr deleted file mode 100644 index 79b726f83dde0..0000000000000 --- a/tests/ui/type-alias-impl-trait/wf-nested.fail.stderr +++ /dev/null @@ -1,17 +0,0 @@ -error[E0310]: the parameter type `T` may not live long enough - --> $DIR/wf-nested.rs:64:38 - | -LL | fn define() -> OuterOpaque {} - | ^^ - | | - | the parameter type `T` must be valid for the static lifetime... - | ...so that the type `T` will meet its required lifetime bounds - | -help: consider adding an explicit lifetime bound - | -LL | fn define() -> OuterOpaque {} - | +++++++++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/type-alias-impl-trait/wf-nested.pass.stderr b/tests/ui/type-alias-impl-trait/wf-nested.pass.stderr deleted file mode 100644 index b61b69d8e407e..0000000000000 --- a/tests/ui/type-alias-impl-trait/wf-nested.pass.stderr +++ /dev/null @@ -1,31 +0,0 @@ -error[E0310]: the parameter type `T` may not live long enough - --> $DIR/wf-nested.rs:34:38 - | -LL | fn define() -> OuterOpaque {} - | ^^ - | | - | the parameter type `T` must be valid for the static lifetime... - | ...so that the type `T` will meet its required lifetime bounds - | -help: consider adding an explicit lifetime bound - | -LL | fn define() -> OuterOpaque {} - | +++++++++ - -error[E0310]: the parameter type `T` may not live long enough - --> $DIR/wf-nested.rs:37:69 - | -LL | fn define_rpit() -> impl Trait<&'static T, Out = impl Sized> {} - | ^^ - | | - | the parameter type `T` must be valid for the static lifetime... - | ...so that the type `T` will meet its required lifetime bounds - | -help: consider adding an explicit lifetime bound - | -LL | fn define_rpit() -> impl Trait<&'static T, Out = impl Sized> {} - | +++++++++ - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr b/tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr deleted file mode 100644 index dbd3a1394f89b..0000000000000 --- a/tests/ui/type-alias-impl-trait/wf-nested.pass_sound.stderr +++ /dev/null @@ -1,46 +0,0 @@ -error[E0310]: the parameter type `T` may not live long enough - --> $DIR/wf-nested.rs:46:38 - | -LL | fn define() -> OuterOpaque {} - | ^^ - | | - | the parameter type `T` must be valid for the static lifetime... - | ...so that the type `T` will meet its required lifetime bounds - | -help: consider adding an explicit lifetime bound - | -LL | fn define() -> OuterOpaque {} - | +++++++++ - -error[E0310]: the parameter type `T` may not live long enough - --> $DIR/wf-nested.rs:51:17 - | -LL | let _ = outer.get(); - | ^^^^^^^^^^^ - | | - | the parameter type `T` must be valid for the static lifetime... - | ...so that the type `T` will meet its required lifetime bounds - | -help: consider adding an explicit lifetime bound - | -LL | fn test() { - | +++++++++ - -error[E0310]: the parameter type `T` may not live long enough - --> $DIR/wf-nested.rs:51:17 - | -LL | let _ = outer.get(); - | ^^^^^^^^^^^ - | | - | the parameter type `T` must be valid for the static lifetime... - | ...so that the type `T` will meet its required lifetime bounds - | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -help: consider adding an explicit lifetime bound - | -LL | fn test() { - | +++++++++ - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0310`. diff --git a/tests/ui/type/pattern_types/derives.noimpl.stderr b/tests/ui/type/pattern_types/derives.noimpl.stderr deleted file mode 100644 index 9450e5753446b..0000000000000 --- a/tests/ui/type/pattern_types/derives.noimpl.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0369]: binary operation `==` cannot be applied to type `(i32) is 0..=999999999` - --> $DIR/derives.rs:14:20 - | -LL | #[derive(Clone, Copy, PartialEq)] - | --------- in this derive macro expansion -LL | #[repr(transparent)] -LL | struct Nanoseconds(NanoI32); - | ^^^^^^^ - | - = note: this error originates in the derive macro `PartialEq` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0369`. diff --git a/tests/ui/type_length_limit.polonius.stderr b/tests/ui/type_length_limit.polonius.stderr deleted file mode 100644 index bc09f15918328..0000000000000 --- a/tests/ui/type_length_limit.polonius.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: reached the type-length limit while instantiating `std::mem::drop::>` - --> $SRC_DIR/core/src/mem/mod.rs:LL:COL - | -LL | pub fn drop(_x: T) {} - | ^^^^^^^^^^^^^^^^^^^^^ - | - = note: the full type name has been written to '$TEST_BUILD_DIR/type_length_limit.polonius/type_length_limit.long-type.txt' - = help: consider adding a `#![type_length_limit="8"]` attribute to your crate - -error: aborting due to 1 previous error - diff --git a/tests/ui/typeck/issue-107775.stderr b/tests/ui/typeck/issue-107775.stderr index 180b0183a3f2d..dad7e1581e79a 100644 --- a/tests/ui/typeck/issue-107775.stderr +++ b/tests/ui/typeck/issue-107775.stderr @@ -10,6 +10,8 @@ LL | Self { map } | = note: expected struct `HashMap Pin + Send + 'static)>>>` found struct `HashMap<{integer}, fn(_) -> Pin + Send>> {::do_something::<'_>}>` + = note: fn items are distinct from fn pointers + = help: consider casting the fn item to a fn pointer: `::do_something::<'_> as fn(u8) -> Pin + Send + 'static)>>` error: aborting due to 1 previous error diff --git a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.current.stderr b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.current.stderr index 1bcc0dbaf6726..92ad83c330000 100644 --- a/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.current.stderr +++ b/tests/ui/typeck/issue-114918/const-in-impl-fn-return-type.current.stderr @@ -9,6 +9,8 @@ error: the constant `N` is not of type `usize` | LL | fn func() -> [(); N]; | ^^^^^^^ expected `usize`, found `u32` + | + = note: the length of array `[(); N]` must be type `usize` error: aborting due to 2 previous errors diff --git a/tests/ui/uninhabited/uninhabited-irrefutable.min_exhaustive_patterns.stderr b/tests/ui/uninhabited/uninhabited-irrefutable.min_exhaustive_patterns.stderr deleted file mode 100644 index 67527ce1ac452..0000000000000 --- a/tests/ui/uninhabited/uninhabited-irrefutable.min_exhaustive_patterns.stderr +++ /dev/null @@ -1,26 +0,0 @@ -error[E0005]: refutable pattern in local binding - --> $DIR/uninhabited-irrefutable.rs:31:9 - | -LL | let Foo::D(_y, _z) = x; - | ^^^^^^^^^^^^^^ pattern `Foo::A(_)` not covered - | - = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant - = note: for more information, visit https://doc.rust-lang.org/book/ch19-02-refutability.html -note: `Foo` defined here - --> $DIR/uninhabited-irrefutable.rs:20:6 - | -LL | enum Foo { - | ^^^ -LL | -LL | A(foo::SecretlyEmpty), - | - not covered - = note: pattern `Foo::A(_)` is currently uninhabited, but this variant contains private fields which may become inhabited in the future - = note: the matched value is of type `Foo` -help: you might want to use `let else` to handle the variant that isn't matched - | -LL | let Foo::D(_y, _z) = x else { todo!() }; - | ++++++++++++++++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0005`.