diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 3459d39131ac..7caf7c4c3568 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 1d6d7330757d..232fd546de9a 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 f31e2c65c792..1267281f73eb 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1391,7 +1391,11 @@ impl<'hir> LoweringContext<'_, 'hir> { None, ); // Destructure like a unit struct. - let unit_struct_pat = hir::PatKind::Path(qpath); + let unit_struct_pat = hir::PatKind::Expr(self.arena.alloc(hir::PatExpr { + kind: hir::PatExprKind::Path(qpath), + hir_id: self.next_id(), + span: self.lower_span(lhs.span), + })); return self.pat_without_dbm(lhs.span, unit_struct_pat); } } @@ -2125,7 +2129,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 +2139,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_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index 3c78ed0497d7..cde8ddbfe03c 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -69,7 +69,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ImplTraitContext::Disallowed(ImplTraitPosition::Path), None, ); - break hir::PatKind::Path(qpath); + let kind = hir::PatExprKind::Path(qpath); + let span = self.lower_span(pattern.span); + let expr = hir::PatExpr { hir_id: pat_hir_id, span, kind }; + let expr = self.arena.alloc(expr); + return hir::Pat { + hir_id: self.next_id(), + kind: hir::PatKind::Expr(expr), + span, + default_binding_modes: true, + }; } PatKind::Struct(qself, path, fields, etc) => { let qpath = self.lower_qpath( @@ -304,16 +313,20 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { ) } Some(res) => { - let hir_id = self.next_id(); let res = self.lower_res(res); - hir::PatKind::Path(hir::QPath::Resolved( - None, - self.arena.alloc(hir::Path { - span: self.lower_span(ident.span), - res, - segments: arena_vec![self; hir::PathSegment::new(self.lower_ident(ident), hir_id, res)], - }), - )) + let span = self.lower_span(ident.span); + hir::PatKind::Expr(self.arena.alloc(hir::PatExpr { + kind: hir::PatExprKind::Path(hir::QPath::Resolved( + None, + self.arena.alloc(hir::Path { + span, + res, + segments: arena_vec![self; hir::PathSegment::new(self.lower_ident(ident), self.next_id(), res)], + }), + )), + hir_id: self.next_id(), + span, + })) } } } diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 236ca7ba7035..ea1f4a6559ac 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 897c275d850c..4cfcaa95233d 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/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index a6ca038282d9..e841a5e4c948 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/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index 40e801d03885..6d32ee17f4c2 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -1,14 +1,20 @@ use std::io; +use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; +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::mir::{Body, ClosureRegionRequirements, Location}; +use rustc_middle::ty::{RegionVid, TyCtxt}; +use rustc_mir_dataflow::points::PointIndex; use rustc_session::config::MirIncludeSpans; use crate::borrow_set::BorrowSet; +use crate::constraints::OutlivesConstraint; use crate::polonius::{LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet}; +use crate::region_infer::values::LivenessValues; +use crate::type_check::Locations; use crate::{BorrowckInferCtxt, RegionInferenceContext}; /// `-Zdump-mir=polonius` dumps MIR annotated with NLL and polonius specific information. @@ -50,6 +56,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,25 +76,54 @@ 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,
         regioncx,
         borrow_set,
-        localized_outlives_constraints,
+        &localized_outlives_constraints,
         closure_region_requirements,
         out,
     )?;
-    writeln!(out, "
")?; + writeln!(out, "
")?; writeln!(out, "
")?; - // Section 2: mermaid visualization of the CFG. + // Section 2: mermaid visualization of the polonius constraint graph. + writeln!(out, "
")?; + writeln!(out, "Polonius constraint graph")?; + writeln!(out, "
")?;
+    let edge_count = emit_mermaid_constraint_graph(
+        borrow_set,
+        regioncx.liveness_constraints(),
+        &localized_outlives_constraints,
+        out,
+    )?;
+    writeln!(out, "
")?; + writeln!(out, "
")?; + + // Section 3: 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 4: 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 5: 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. @@ -95,7 +132,11 @@ fn emit_polonius_dump<'tcx>( "" )?; writeln!(out, "")?; writeln!(out, "")?; @@ -110,7 +151,7 @@ fn emit_html_mir<'tcx>( body: &Body<'tcx>, regioncx: &RegionInferenceContext<'tcx>, borrow_set: &BorrowSet<'tcx>, - localized_outlives_constraints: LocalizedOutlivesConstraintSet, + localized_outlives_constraints: &LocalizedOutlivesConstraintSet, closure_region_requirements: &Option>, out: &mut dyn io::Write, ) -> io::Result<()> { @@ -138,7 +179,7 @@ fn emit_html_mir<'tcx>( regioncx, closure_region_requirements, borrow_set, - &localized_outlives_constraints, + localized_outlives_constraints, pass_where, out, ) @@ -261,3 +302,185 @@ 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(()) +} + +/// Emits a mermaid flowchart of the polonius localized outlives constraints, with subgraphs per +/// region, and loan introductions. +fn emit_mermaid_constraint_graph<'tcx>( + borrow_set: &BorrowSet<'tcx>, + liveness: &LivenessValues, + localized_outlives_constraints: &LocalizedOutlivesConstraintSet, + out: &mut dyn io::Write, +) -> io::Result { + let location_name = |location: Location| { + // A MIR location looks like `bb5[2]`. As that is not a syntactically valid mermaid node id, + // transform it into `BB5_2`. + format!("BB{}_{}", location.block.index(), location.statement_index) + }; + let region_name = |region: RegionVid| format!("'{}", region.index()); + let node_name = |region: RegionVid, point: PointIndex| { + let location = liveness.location_from_point(point); + format!("{}_{}", region_name(region), location_name(location)) + }; + + // The mermaid chart type: a top-down flowchart, which supports subgraphs. + writeln!(out, "flowchart TD")?; + + // The loans subgraph: a node per loan. + writeln!(out, " subgraph \"Loans\"")?; + for loan_idx in 0..borrow_set.len() { + writeln!(out, " L{loan_idx}")?; + } + writeln!(out, " end\n")?; + + // And an edge from that loan node to where it enters the constraint graph. + for (loan_idx, loan) in borrow_set.iter_enumerated() { + writeln!( + out, + " L{} --> {}_{}", + loan_idx.index(), + region_name(loan.region), + location_name(loan.reserve_location), + )?; + } + writeln!(out, "")?; + + // The regions subgraphs containing the region/point nodes. + let mut points_per_region: FxIndexMap> = + FxIndexMap::default(); + for constraint in &localized_outlives_constraints.outlives { + points_per_region.entry(constraint.source).or_default().insert(constraint.from); + points_per_region.entry(constraint.target).or_default().insert(constraint.to); + } + for (region, points) in points_per_region { + writeln!(out, " subgraph \"{}\"", region_name(region))?; + for point in points { + writeln!(out, " {}", node_name(region, point))?; + } + writeln!(out, " end\n")?; + } + + // The constraint graph edges. + for constraint in &localized_outlives_constraints.outlives { + // FIXME: add killed loans and constraint kind as edge labels. + writeln!( + out, + " {} --> {}", + node_name(constraint.source, constraint.from), + node_name(constraint.target, constraint.to), + )?; + } + + // Return the number of edges: this is the biggest graph in the dump and its edge count will be + // mermaid's max edge count to support. + let edge_count = borrow_set.len() + localized_outlives_constraints.outlives.len(); + Ok(edge_count) +} diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 5062cf55bb9a..eb5b345e49ec 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/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 5202fe26c401..a0ab6375a666 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_gcc/src/gcc_util.rs b/compiler/rustc_codegen_gcc/src/gcc_util.rs index 560aff43d653..4e8c8aaaf5c8 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 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_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index cfdfbdb7880b..6a339d695426 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -21,9 +21,10 @@ use super::error::*; use crate::errors::{LongRunning, LongRunningWarn}; use crate::fluent_generated as fluent; use crate::interpret::{ - self, AllocId, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, GlobalAlloc, ImmTy, - InterpCx, InterpResult, MPlaceTy, OpTy, RangeSet, Scalar, compile_time_machine, interp_ok, - throw_exhaust, throw_inval, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format, + self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame, + GlobalAlloc, ImmTy, InterpCx, InterpResult, MPlaceTy, OpTy, RangeSet, Scalar, + compile_time_machine, interp_ok, throw_exhaust, throw_inval, throw_ub, throw_ub_custom, + throw_unsup, throw_unsup_format, }; /// When hitting this many interpreted terminators we emit a deny by default lint @@ -420,6 +421,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { Size::from_bytes(size), align, interpret::MemoryKind::Machine(MemoryKind::Heap), + AllocInit::Uninit, )?; ecx.write_pointer(ptr, dest)?; } diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index 2772c94d52b0..4f4b6785844d 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -20,10 +20,10 @@ use rustc_middle::ty::{self, Instance, Ty, TyCtxt}; use tracing::{debug, instrument, trace}; use super::{ - AllocBytes, AllocId, AllocMap, AllocRange, Allocation, CheckAlignMsg, CheckInAllocMsg, - CtfeProvenance, GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak, Misalignment, Pointer, - PointerArithmetic, Provenance, Scalar, alloc_range, err_ub, err_ub_custom, interp_ok, throw_ub, - throw_ub_custom, throw_unsup, throw_unsup_format, + AllocBytes, AllocId, AllocInit, AllocMap, AllocRange, Allocation, CheckAlignMsg, + CheckInAllocMsg, CtfeProvenance, GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak, + Misalignment, Pointer, PointerArithmetic, Provenance, Scalar, alloc_range, err_ub, + err_ub_custom, interp_ok, throw_ub, throw_ub_custom, throw_unsup, throw_unsup_format, }; use crate::fluent_generated as fluent; @@ -230,11 +230,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { size: Size, align: Align, kind: MemoryKind, + init: AllocInit, ) -> InterpResult<'tcx, Pointer> { let alloc = if M::PANIC_ON_ALLOC_FAIL { - Allocation::uninit(size, align) + Allocation::new(size, align, init) } else { - Allocation::try_uninit(size, align)? + Allocation::try_new(size, align, init)? }; self.insert_allocation(alloc, kind) } @@ -270,6 +271,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { M::adjust_alloc_root_pointer(self, Pointer::from(id), Some(kind)) } + /// If this grows the allocation, `init_growth` determines + /// whether the additional space will be initialized. pub fn reallocate_ptr( &mut self, ptr: Pointer>, @@ -277,6 +280,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { new_size: Size, new_align: Align, kind: MemoryKind, + init_growth: AllocInit, ) -> InterpResult<'tcx, Pointer> { let (alloc_id, offset, _prov) = self.ptr_get_alloc_id(ptr, 0)?; if offset.bytes() != 0 { @@ -289,7 +293,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { // For simplicities' sake, we implement reallocate as "alloc, copy, dealloc". // This happens so rarely, the perf advantage is outweighed by the maintenance cost. - let new_ptr = self.allocate_ptr(new_size, new_align, kind)?; + // If requested, we zero-init the entire allocation, to ensure that a growing + // allocation has its new bytes properly set. For the part that is copied, + // `mem_copy` below will de-initialize things as necessary. + let new_ptr = self.allocate_ptr(new_size, new_align, kind, init_growth)?; let old_size = match old_size_and_align { Some((size, _align)) => size, None => self.get_alloc_raw(alloc_id)?.size(), @@ -830,9 +837,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/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index c97922ac132b..f5d3de7b1b27 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -12,9 +12,9 @@ use rustc_middle::{bug, mir, span_bug}; use tracing::{instrument, trace}; use super::{ - AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance, ImmTy, Immediate, InterpCx, InterpResult, - Machine, MemoryKind, Misalignment, OffsetMode, OpTy, Operand, Pointer, Projectable, Provenance, - Scalar, alloc_range, interp_ok, mir_assign_valid_types, + AllocInit, AllocRef, AllocRefMut, CheckAlignMsg, CtfeProvenance, ImmTy, Immediate, InterpCx, + InterpResult, Machine, MemoryKind, Misalignment, OffsetMode, OpTy, Operand, Pointer, + Projectable, Provenance, Scalar, alloc_range, interp_ok, mir_assign_valid_types, }; #[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)] @@ -983,7 +983,7 @@ where let Some((size, align)) = self.size_and_align_of(&meta, &layout)? else { span_bug!(self.cur_span(), "cannot allocate space for `extern` type, size is not known") }; - let ptr = self.allocate_ptr(size, align, kind)?; + let ptr = self.allocate_ptr(size, align, kind, AllocInit::Uninit)?; interp_ok(self.ptr_with_meta_to_mplace(ptr.into(), meta, layout, /*unaligned*/ false)) } diff --git a/compiler/rustc_const_eval/src/interpret/util.rs b/compiler/rustc_const_eval/src/interpret/util.rs index ecb7c3fc93cd..eb98e3b53805 100644 --- a/compiler/rustc_const_eval/src/interpret/util.rs +++ b/compiler/rustc_const_eval/src/interpret/util.rs @@ -2,7 +2,7 @@ use std::ops::ControlFlow; use rustc_hir::def_id::LocalDefId; use rustc_middle::mir; -use rustc_middle::mir::interpret::{Allocation, InterpResult, Pointer}; +use rustc_middle::mir::interpret::{AllocInit, Allocation, InterpResult, Pointer}; use rustc_middle::ty::layout::TyAndLayout; use rustc_middle::ty::{ self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, @@ -76,7 +76,7 @@ pub(crate) fn create_static_alloc<'tcx>( static_def_id: LocalDefId, layout: TyAndLayout<'tcx>, ) -> InterpResult<'tcx, MPlaceTy<'tcx>> { - let alloc = Allocation::try_uninit(layout.size, layout.align.abi)?; + let alloc = Allocation::try_new(layout.size, layout.align.abi, AllocInit::Uninit)?; let alloc_id = ecx.tcx.reserve_and_set_static_alloc(static_def_id.into()); assert_eq!(ecx.machine.static_root_ids, None); ecx.machine.static_root_ids = Some((alloc_id, static_def_id)); diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 20be21446098..c9d38a0f9325 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/src/hir.rs b/compiler/rustc_hir/src/hir.rs index a396d705cbb6..af2f86b67e00 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1437,7 +1437,7 @@ impl<'hir> Pat<'hir> { use PatKind::*; match self.kind { - Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => true, + Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Err(_) => true, Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_short_(it), Struct(_, fields, _) => fields.iter().all(|field| field.pat.walk_short_(it)), TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().all(|p| p.walk_short_(it)), @@ -1464,7 +1464,7 @@ impl<'hir> Pat<'hir> { use PatKind::*; match self.kind { - Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Path(_) | Err(_) => {} + Wild | Never | Expr(_) | Range(..) | Binding(.., None) | Err(_) => {} Box(s) | Deref(s) | Ref(s, _) | Binding(.., Some(s)) | Guard(s, _) => s.walk_(it), Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk_(it)), TupleStruct(_, s, _) | Tuple(s, _) | Or(s) => s.iter().for_each(|p| p.walk_(it)), @@ -1618,9 +1618,6 @@ pub enum PatKind<'hir> { /// A never pattern `!`. Never, - /// A path pattern for a unit struct/variant or a (maybe-associated) constant. - Path(QPath<'hir>), - /// A tuple pattern (e.g., `(a, b)`). /// If the `..` pattern fragment is present, then `Option` denotes its position. /// `0 <= position <= subpats.len()` diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index b0d80d0f8092..31764ab12094 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -709,9 +709,6 @@ pub fn walk_pat<'v, V: Visitor<'v>>(visitor: &mut V, pattern: &'v Pat<'v>) -> V: try_visit!(visitor.visit_qpath(qpath, pattern.hir_id, pattern.span)); walk_list!(visitor, visit_pat, children); } - PatKind::Path(ref qpath) => { - try_visit!(visitor.visit_qpath(qpath, pattern.hir_id, pattern.span)); - } PatKind::Struct(ref qpath, fields, _) => { try_visit!(visitor.visit_qpath(qpath, pattern.hir_id, pattern.span)); walk_list!(visitor, visit_pat_field, fields); diff --git a/compiler/rustc_hir/src/pat_util.rs b/compiler/rustc_hir/src/pat_util.rs index bb853985c7d8..8dd1e4e82927 100644 --- a/compiler/rustc_hir/src/pat_util.rs +++ b/compiler/rustc_hir/src/pat_util.rs @@ -105,7 +105,10 @@ impl hir::Pat<'_> { let mut variants = vec![]; self.walk(|p| match &p.kind { PatKind::Or(_) => false, - PatKind::Path(hir::QPath::Resolved(_, path)) + PatKind::Expr(hir::PatExpr { + kind: hir::PatExprKind::Path(hir::QPath::Resolved(_, path)), + .. + }) | PatKind::TupleStruct(hir::QPath::Resolved(_, path), ..) | PatKind::Struct(hir::QPath::Resolved(_, path), ..) => { if let Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Variant, ..), id) = diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 69b4aa47ebad..cc0b7fdd8dd1 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -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/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs index 83c69dc2ef41..d43c65c0023b 100644 --- a/compiler/rustc_hir_analysis/src/check/region.rs +++ b/compiler/rustc_hir_analysis/src/check/region.rs @@ -703,7 +703,6 @@ fn resolve_local<'tcx>( | PatKind::Binding(hir::BindingMode(hir::ByRef::No, _), ..) | PatKind::Wild | PatKind::Never - | PatKind::Path(_) | PatKind::Expr(_) | PatKind::Range(_, _, _) | PatKind::Err(_) => false, diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 447050ea7d20..cad7b2a1e57b 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(); diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index d3ff1f7bebe6..e37a11b68445 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/hir_ty_lowering/lint.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs index 539c5f6a20af..44f7a035a10f 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/lint.rs @@ -41,8 +41,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { kind: hir::ExprKind::Path(hir::QPath::TypeRelative(qself, _)), .. }) - | hir::Node::Pat(hir::Pat { - kind: hir::PatKind::Path(hir::QPath::TypeRelative(qself, _)), + | hir::Node::PatExpr(hir::PatExpr { + kind: hir::PatExprKind::Path(hir::QPath::TypeRelative(qself, _)), .. }) if qself.hir_id == self_ty.hir_id => true, _ => false, diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index afc0f627f696..a91afa51230d 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1906,9 +1906,6 @@ impl<'a> State<'a> { } self.pclose(); } - PatKind::Path(ref qpath) => { - self.print_qpath(qpath, true); - } PatKind::Struct(ref qpath, fields, etc) => { self.print_qpath(qpath, true); self.nbsp(); diff --git a/compiler/rustc_hir_typeck/src/closure.rs b/compiler/rustc_hir_typeck/src/closure.rs index 4dcc83d0aefb..62dfffb560ba 100644 --- a/compiler/rustc_hir_typeck/src/closure.rs +++ b/compiler/rustc_hir_typeck/src/closure.rs @@ -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)), ), @@ -1019,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 47abba1cc29c..153fd6001bbc 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 1c828591bcbd..f79667e59bae 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -480,7 +480,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { hir::PatKind::Binding(_, _, _, _) | hir::PatKind::Struct(_, _, _) | hir::PatKind::TupleStruct(_, _, _) - | hir::PatKind::Path(_) | hir::PatKind::Tuple(_, _) | hir::PatKind::Box(_) | hir::PatKind::Ref(_, _) diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index 1f48b703e4ac..d37c68f82fb2 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -11,10 +11,9 @@ use hir::def::DefKind; use hir::pat_util::EnumerateAndAdjustIterator as _; use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxIndexMap; -use rustc_hir as hir; use rustc_hir::def::{CtorOf, Res}; use rustc_hir::def_id::LocalDefId; -use rustc_hir::{HirId, PatKind}; +use rustc_hir::{self as hir, HirId, PatExpr, PatExprKind, PatKind}; use rustc_lint::LateContext; use rustc_middle::hir::place::ProjectionKind; // Export these here so that Clippy can use them. @@ -564,11 +563,11 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // FIXME(never_patterns): does this do what I expect? needs_to_be_read = true; } - PatKind::Path(qpath) => { + PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, span }) => { // A `Path` pattern is just a name like `Foo`. This is either a // named constant or else it refers to an ADT variant - let res = self.cx.typeck_results().qpath_res(qpath, pat.hir_id); + let res = self.cx.typeck_results().qpath_res(qpath, *hir_id); match res { Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => { // Named constants have to be equated with the value @@ -581,7 +580,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // Otherwise, this is a struct/enum variant, and so it's // only a read if we need to read the discriminant. needs_to_be_read |= - self.is_multivariant_adt(place.place.ty(), pat.span); + self.is_multivariant_adt(place.place.ty(), *span); } } } @@ -1801,8 +1800,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx } } - PatKind::Path(_) - | PatKind::Binding(.., None) + PatKind::Binding(.., None) | PatKind::Expr(..) | PatKind::Range(..) | PatKind::Never diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 928010c03c2e..aea2e0fd3a30 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -5,12 +5,12 @@ use hir::def_id::LocalDefId; use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::packed::Pu128; use rustc_errors::{Applicability, Diag, MultiSpan}; -use rustc_hir as hir; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ - Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, GenericBound, HirId, - Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicateKind, expr_needs_parens, + self as hir, Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, + GenericBound, HirId, Node, PatExpr, PatExprKind, Path, QPath, Stmt, StmtKind, TyKind, + WherePredicateKind, expr_needs_parens, }; use rustc_hir_analysis::collect::suggest_impl_trait; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; @@ -1422,8 +1422,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // since the user probably just misunderstood how `let else` // and `&&` work together. if let Some((_, hir::Node::LetStmt(local))) = cond_parent - && let hir::PatKind::Path(qpath) | hir::PatKind::TupleStruct(qpath, _, _) = - &local.pat.kind + && let hir::PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. }) + | hir::PatKind::TupleStruct(qpath, _, _) = &local.pat.kind && let hir::QPath::Resolved(None, path) = qpath && let Some(did) = path .res diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 5d4e67d1a0e6..e4a6a0fedc5a 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -177,8 +177,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) | hir::Node::Pat(&hir::Pat { kind: - hir::PatKind::Path(QPath::TypeRelative(rcvr, segment)) - | hir::PatKind::Struct(QPath::TypeRelative(rcvr, segment), ..) + hir::PatKind::Struct(QPath::TypeRelative(rcvr, segment), ..) | hir::PatKind::TupleStruct(QPath::TypeRelative(rcvr, segment), ..), span, .. diff --git a/compiler/rustc_hir_typeck/src/pat.rs b/compiler/rustc_hir_typeck/src/pat.rs index cbd1db2ca255..0aa2f1110fb3 100644 --- a/compiler/rustc_hir_typeck/src/pat.rs +++ b/compiler/rustc_hir_typeck/src/pat.rs @@ -11,8 +11,8 @@ use rustc_errors::{ use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::pat_util::EnumerateAndAdjustIterator; use rustc_hir::{ - self as hir, BindingMode, ByRef, ExprKind, HirId, LangItem, Mutability, Pat, PatKind, - expr_needs_parens, + self as hir, BindingMode, ByRef, ExprKind, HirId, LangItem, Mutability, Pat, PatExpr, + PatExprKind, PatKind, expr_needs_parens, }; use rustc_infer::infer; use rustc_middle::traits::PatternOriginExpr; @@ -312,9 +312,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_pat(&self, pat: &'tcx Pat<'tcx>, expected: Ty<'tcx>, pat_info: PatInfo<'_, 'tcx>) { let PatInfo { binding_mode, max_ref_mutbl, top_info: ti, current_depth, .. } = pat_info; - let path_res = match &pat.kind { - PatKind::Path(qpath) => { - Some(self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span)) + let path_res = match pat.kind { + PatKind::Expr(PatExpr { kind: PatExprKind::Path(ref qpath), hir_id, span }) => { + Some(self.resolve_ty_and_res_fully_qualified_call(qpath, *hir_id, *span)) } _ => None, }; @@ -333,6 +333,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::Wild | PatKind::Err(_) => expected, // We allow any type here; we ensure that the type is uninhabited during match checking. PatKind::Never => expected, + PatKind::Expr(PatExpr { kind: PatExprKind::Path(ref qpath), hir_id, span }) => { + let ty = self.check_pat_path( + *hir_id, + pat.hir_id, + *span, + qpath, + path_res.unwrap(), + expected, + ti, + ); + self.write_ty(*hir_id, ty); + ty + } PatKind::Expr(lt) => self.check_pat_lit(pat.span, lt, expected, ti), PatKind::Range(lhs, rhs, _) => self.check_pat_range(pat.span, lhs, rhs, expected, ti), PatKind::Binding(ba, var_id, ident, sub) => { @@ -341,9 +354,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::TupleStruct(ref qpath, subpats, ddpos) => { self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, pat_info) } - PatKind::Path(ref qpath) => { - self.check_pat_path(pat.hir_id, pat.span, qpath, path_res.unwrap(), expected, ti) - } PatKind::Struct(ref qpath, fields, has_rest_pat) => { self.check_pat_struct(pat, qpath, fields, has_rest_pat, expected, pat_info) } @@ -456,16 +466,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { | PatKind::Slice(..) => AdjustMode::Peel, // A never pattern behaves somewhat like a literal or unit variant. PatKind::Never => AdjustMode::Peel, - // String and byte-string literals result in types `&str` and `&[u8]` respectively. - // All other literals result in non-reference types. - // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}`. - // - // Call `resolve_vars_if_possible` here for inline const blocks. - PatKind::Expr(lt) => match self.resolve_vars_if_possible(self.check_pat_expr_unadjusted(lt)).kind() { - ty::Ref(..) => AdjustMode::Pass, - _ => AdjustMode::Peel, - }, - PatKind::Path(_) => match opt_path_res.unwrap() { + PatKind::Expr(PatExpr { kind: PatExprKind::Path(_), .. }) => match opt_path_res.unwrap() { // These constants can be of a reference type, e.g. `const X: &u8 = &0;`. // Peeling the reference types too early will cause type checking failures. // Although it would be possible to *also* peel the types of the constants too. @@ -476,6 +477,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // a reference type wherefore peeling doesn't give up any expressiveness. _ => AdjustMode::Peel, }, + + // String and byte-string literals result in types `&str` and `&[u8]` respectively. + // All other literals result in non-reference types. + // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo" {}`. + // + // Call `resolve_vars_if_possible` here for inline const blocks. + PatKind::Expr(lt) => match self.resolve_vars_if_possible(self.check_pat_expr_unadjusted(lt)).kind() { + ty::Ref(..) => AdjustMode::Pass, + _ => AdjustMode::Peel, + }, + // Ref patterns are complicated, we handle them in `check_pat_ref`. PatKind::Ref(..) => AdjustMode::Pass, // A `_` pattern works with any expected type, so there's no need to do anything. @@ -1001,7 +1013,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { PatKind::Wild | PatKind::Never | PatKind::Binding(..) - | PatKind::Path(..) | PatKind::Box(..) | PatKind::Deref(_) | PatKind::Ref(..) @@ -1139,7 +1150,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_pat_path( &self, - hir_id: HirId, + path_id: HirId, + pat_id_for_diag: HirId, span: Span, qpath: &hir::QPath<'_>, path_resolution: (Res, Option>, &'tcx [hir::PathSegment<'tcx>]), @@ -1193,11 +1205,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Type-check the path. let (pat_ty, pat_res) = - self.instantiate_value_path(segments, opt_ty, res, span, span, hir_id); + self.instantiate_value_path(segments, opt_ty, res, span, span, path_id); if let Err(err) = self.demand_suptype_with_origin(&self.pattern_cause(ti, span), expected, pat_ty) { - self.emit_bad_pat_path(err, hir_id, span, res, pat_res, pat_ty, segments); + self.emit_bad_pat_path(err, pat_id_for_diag, span, res, pat_res, pat_ty, segments); } pat_ty } diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index ba6350711357..e1bd9ae2e672 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 7a21c2883d1a..d68f3639176f 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 47dfbc1d7fbf..31123625369b 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 939980a932fd..b62950d67096 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 2113345eda3a..d9803236f85c 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/util.rs b/compiler/rustc_interface/src/util.rs index 984b8104f539..e900ec14fcab 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 c03de687a338..e8a4e9a84c46 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/internal.rs b/compiler/rustc_lint/src/internal.rs index 166ff60f7e1e..546df4497adf 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -6,7 +6,7 @@ use rustc_hir::def::Res; use rustc_hir::def_id::DefId; use rustc_hir::{ AmbigArg, BinOp, BinOpKind, Expr, ExprKind, GenericArg, HirId, Impl, Item, ItemKind, Node, Pat, - PatKind, Path, PathSegment, QPath, Ty, TyKind, + PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Ty, TyKind, }; use rustc_middle::ty::{self, GenericArgsRef, Ty as MiddleTy}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -164,11 +164,9 @@ impl<'tcx> LateLintPass<'tcx> for TyTyKind { TyKind::Path(QPath::Resolved(_, path)) => { if lint_ty_kind_usage(cx, &path.res) { let span = match cx.tcx.parent_hir_node(ty.hir_id) { - Node::Pat(Pat { - kind: - PatKind::Path(qpath) - | PatKind::TupleStruct(qpath, ..) - | PatKind::Struct(qpath, ..), + Node::PatExpr(PatExpr { kind: PatExprKind::Path(qpath), .. }) + | Node::Pat(Pat { + kind: PatKind::TupleStruct(qpath, ..) | PatKind::Struct(qpath, ..), .. }) | Node::Expr( diff --git a/compiler/rustc_lint/src/nonstandard_style.rs b/compiler/rustc_lint/src/nonstandard_style.rs index e09049f322fa..0c180ab95706 100644 --- a/compiler/rustc_lint/src/nonstandard_style.rs +++ b/compiler/rustc_lint/src/nonstandard_style.rs @@ -1,7 +1,7 @@ use rustc_abi::ExternAbi; use rustc_hir::def::{DefKind, Res}; use rustc_hir::intravisit::FnKind; -use rustc_hir::{AttrArgs, AttrItem, AttrKind, GenericParamKind, PatKind}; +use rustc_hir::{AttrArgs, AttrItem, AttrKind, GenericParamKind, PatExprKind, PatKind}; use rustc_middle::ty; use rustc_session::config::CrateType; use rustc_session::{declare_lint, declare_lint_pass}; @@ -527,7 +527,11 @@ impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals { fn check_pat(&mut self, cx: &LateContext<'_>, p: &hir::Pat<'_>) { // Lint for constants that look like binding identifiers (#7526) - if let PatKind::Path(hir::QPath::Resolved(None, path)) = p.kind { + if let PatKind::Expr(hir::PatExpr { + kind: PatExprKind::Path(hir::QPath::Resolved(None, path)), + .. + }) = p.kind + { if let Res::Def(DefKind::Const, _) = path.res { if let [segment] = path.segments { NonUpperCaseGlobals::check_upper_case( diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 3059adb3fda1..5696fcaed137 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; + 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))) + 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 527f2f10205a..da07ad8f6c07 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 c538ab99fb54..904409dd777e 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 4f9cdc9a474b..d1d356c5220a 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/interpret/allocation.rs b/compiler/rustc_middle/src/mir/interpret/allocation.rs index 1b07846e0cf6..2e4d258ffff2 100644 --- a/compiler/rustc_middle/src/mir/interpret/allocation.rs +++ b/compiler/rustc_middle/src/mir/interpret/allocation.rs @@ -270,6 +270,12 @@ impl AllocRange { } } +/// Whether a new allocation should be initialized with zero-bytes. +pub enum AllocInit { + Uninit, + Zero, +} + // The constructors are all without extra; the extra gets added by a machine hook later. impl Allocation { /// Creates an allocation initialized by the given bytes @@ -294,7 +300,12 @@ impl Allocation { Allocation::from_bytes(slice, Align::ONE, Mutability::Not) } - fn uninit_inner(size: Size, align: Align, fail: impl FnOnce() -> R) -> Result { + fn new_inner( + size: Size, + align: Align, + init: AllocInit, + fail: impl FnOnce() -> R, + ) -> Result { // We raise an error if we cannot create the allocation on the host. // This results in an error that can happen non-deterministically, since the memory // available to the compiler can change between runs. Normally queries are always @@ -306,7 +317,10 @@ impl Allocation { Ok(Allocation { bytes, provenance: ProvenanceMap::new(), - init_mask: InitMask::new(size, false), + init_mask: InitMask::new(size, match init { + AllocInit::Uninit => false, + AllocInit::Zero => true, + }), align, mutability: Mutability::Mut, extra: (), @@ -315,8 +329,8 @@ impl Allocation { /// Try to create an Allocation of `size` bytes, failing if there is not enough memory /// available to the compiler to do so. - pub fn try_uninit<'tcx>(size: Size, align: Align) -> InterpResult<'tcx, Self> { - Self::uninit_inner(size, align, || { + pub fn try_new<'tcx>(size: Size, align: Align, init: AllocInit) -> InterpResult<'tcx, Self> { + Self::new_inner(size, align, init, || { ty::tls::with(|tcx| tcx.dcx().delayed_bug("exhausted memory during interpretation")); InterpErrorKind::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted) }) @@ -328,8 +342,8 @@ impl Allocation { /// /// Example use case: To obtain an Allocation filled with specific data, /// first call this function and then call write_scalar to fill in the right data. - pub fn uninit(size: Size, align: Align) -> Self { - match Self::uninit_inner(size, align, || { + pub fn new(size: Size, align: Align, init: AllocInit) -> Self { + match Self::new_inner(size, align, init, || { panic!( "interpreter ran out of memory: cannot create allocation of {} bytes", size.bytes() diff --git a/compiler/rustc_middle/src/mir/interpret/mod.rs b/compiler/rustc_middle/src/mir/interpret/mod.rs index b88137544bca..f4f9f221a751 100644 --- a/compiler/rustc_middle/src/mir/interpret/mod.rs +++ b/compiler/rustc_middle/src/mir/interpret/mod.rs @@ -30,8 +30,8 @@ pub use { }; pub use self::allocation::{ - AllocBytes, AllocError, AllocRange, AllocResult, Allocation, ConstAllocation, InitChunk, - InitChunkIter, alloc_range, + AllocBytes, AllocError, AllocInit, AllocRange, AllocResult, Allocation, ConstAllocation, + InitChunk, InitChunkIter, alloc_range, }; pub use self::error::{ BadBytesAccess, CheckAlignMsg, CheckInAllocMsg, ErrorHandled, EvalStaticInitializerRawResult, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index e27a98236390..d83bc19a6a2f 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -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) } } diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 55d78e083e07..8a9110f842a9 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 1f0710e24152..0c22c056dab5 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, @@ -2577,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/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index ac900edefe12..027a4315b4bf 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 21c605f8296d..cbc02097d827 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_middle/src/ty/vtable.rs b/compiler/rustc_middle/src/ty/vtable.rs index 23e2e8ad3d33..455bd16ff8c2 100644 --- a/compiler/rustc_middle/src/ty/vtable.rs +++ b/compiler/rustc_middle/src/ty/vtable.rs @@ -4,7 +4,9 @@ use rustc_ast::Mutability; use rustc_macros::HashStable; use rustc_type_ir::elaborate; -use crate::mir::interpret::{AllocId, Allocation, CTFE_ALLOC_SALT, Pointer, Scalar, alloc_range}; +use crate::mir::interpret::{ + AllocId, AllocInit, Allocation, CTFE_ALLOC_SALT, Pointer, Scalar, alloc_range, +}; use crate::ty::{self, Instance, PolyTraitRef, Ty, TyCtxt}; #[derive(Clone, Copy, PartialEq, HashStable)] @@ -108,7 +110,7 @@ pub(super) fn vtable_allocation_provider<'tcx>( let ptr_align = tcx.data_layout.pointer_align.abi; let vtable_size = ptr_size * u64::try_from(vtable_entries.len()).unwrap(); - let mut vtable = Allocation::uninit(vtable_size, ptr_align); + let mut vtable = Allocation::new(vtable_size, ptr_align, AllocInit::Uninit); // No need to do any alignment checks on the memory accesses below, because we know the // allocation is correctly aligned as we created it above. Also we're only offsetting by diff --git a/compiler/rustc_mir_build/src/builder/matches/test.rs b/compiler/rustc_mir_build/src/builder/matches/test.rs index 8cca84d7fcc6..afe6b4475be3 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/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 44b038bb5faf..20a728d6d5b2 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -332,10 +332,6 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { .unwrap_or_else(PatKind::Error) } - hir::PatKind::Path(ref qpath) => { - return self.lower_path(qpath, pat.hir_id, pat.span); - } - hir::PatKind::Deref(subpattern) => { let mutable = self.typeck_results.pat_has_ref_mut_binding(subpattern); let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not }; diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index bb603df11294..d53848f7461f 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_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 63432dc199b9..d0b01b14d635 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_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 5418f054bebd..09c88e7f83bb 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 81e5bca0ba9f..fbb217b16fc3 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_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index e5b63b9b4a60..95f18eaa7ef1 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -10,11 +10,10 @@ use hir::def_id::{LocalDefIdMap, LocalDefIdSet}; use rustc_abi::FieldIdx; use rustc_data_structures::unord::UnordSet; use rustc_errors::MultiSpan; -use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Node, PatKind, TyKind}; +use rustc_hir::{self as hir, Node, PatKind, TyKind}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::privacy::Level; use rustc_middle::query::Providers; @@ -637,10 +636,6 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { let res = self.typeck_results().qpath_res(path, pat.hir_id); self.handle_field_pattern_match(pat, res, fields); } - PatKind::Path(ref qpath) => { - let res = self.typeck_results().qpath_res(qpath, pat.hir_id); - self.handle_res(res); - } PatKind::TupleStruct(ref qpath, fields, dotdot) => { let res = self.typeck_results().qpath_res(qpath, pat.hir_id); self.handle_tuple_field_pattern_match(pat, res, fields, dotdot); @@ -652,6 +647,17 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> { self.in_pat = false; } + fn visit_pat_expr(&mut self, expr: &'tcx rustc_hir::PatExpr<'tcx>) { + match &expr.kind { + rustc_hir::PatExprKind::Path(qpath) => { + let res = self.typeck_results().qpath_res(qpath, expr.hir_id); + self.handle_res(res); + } + _ => {} + } + intravisit::walk_pat_expr(self, expr); + } + fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) { self.handle_res(path.res); intravisit::walk_path(self, path); diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index 8b10543f6fdb..75b62a40ff9f 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -298,7 +298,6 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> { TupleStruct, Or, Never, - Path, Tuple, Box, Deref, diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 87024c487df7..16c0a345f879 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/late.rs b/compiler/rustc_resolve/src/late.rs index 68d3351f174c..4842cbd556c3 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; diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 2db8087fd832..57679d595da3 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) { @@ -3249,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 @@ -3290,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 @@ -3350,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_smir/src/rustc_smir/alloc.rs b/compiler/rustc_smir/src/rustc_smir/alloc.rs index 4e8db6096d4f..52c5b425c14f 100644 --- a/compiler/rustc_smir/src/rustc_smir/alloc.rs +++ b/compiler/rustc_smir/src/rustc_smir/alloc.rs @@ -1,6 +1,6 @@ use rustc_abi::{Align, Size}; use rustc_middle::mir::ConstValue; -use rustc_middle::mir::interpret::{AllocRange, Pointer, alloc_range}; +use rustc_middle::mir::interpret::{AllocInit, AllocRange, Pointer, alloc_range}; use stable_mir::Error; use stable_mir::mir::Mutability; use stable_mir::ty::{Allocation, ProvenanceMap}; @@ -44,7 +44,8 @@ pub(crate) fn try_new_allocation<'tcx>( .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty)) .map_err(|e| e.stable(tables))? .align; - let mut allocation = rustc_middle::mir::interpret::Allocation::uninit(size, align.abi); + let mut allocation = + rustc_middle::mir::interpret::Allocation::new(size, align.abi, AllocInit::Uninit); allocation .write_scalar(&tables.tcx, alloc_range(Size::ZERO, size), scalar) .map_err(|e| e.stable(tables))?; @@ -68,8 +69,11 @@ pub(crate) fn try_new_allocation<'tcx>( .tcx .layout_of(rustc_middle::ty::TypingEnv::fully_monomorphized().as_query_input(ty)) .map_err(|e| e.stable(tables))?; - let mut allocation = - rustc_middle::mir::interpret::Allocation::uninit(layout.size, layout.align.abi); + let mut allocation = rustc_middle::mir::interpret::Allocation::new( + layout.size, + layout.align.abi, + AllocInit::Uninit, + ); allocation .write_scalar( &tables.tcx, diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 9fd07c8634aa..0d8a4988dce0 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 750d2756b4a4..7c72318b4d78 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 bcb6ac13b8fa..9eacd377361a 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 1dd09fe7aafa..e8d14b89d698 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 af7e56961b72..231fecf7a4a8 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 5021fd8bf833..6d39cbce3b78 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 @@ -1969,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 4e0b097db4c6..3d79b0acf83a 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 @@ -799,7 +799,7 @@ impl<'tcx> OnUnimplementedFormatString { 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 @@ -950,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 471105773e2b..ab25bef4120e 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 2dfa72972ba1..c8672b9dbd2f 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/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 0cc0d7f786b0..6b6e0b32385c 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 9d32eb053860..20b675bcb76b 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_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index 4fec606a8315..0c3b0758f0f9 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/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 5c04e5a40df0..01a3c9d2ada7 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/std/src/io/pipe/tests.rs b/library/std/src/io/pipe/tests.rs index c1f3f192ca2d..f113b157459d 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/sys/pal/uefi/process.rs b/library/std/src/sys/pal/uefi/process.rs index 1a0754134dfb..3077a72eac66 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/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile index d408cd518a00..9234c6dc921e 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 4c4863e2b4b3..c6bc19f9e6c4 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -43,10 +43,6 @@ runners: os: windows-2022-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 diff --git a/src/doc/book b/src/doc/book index 82a4a49789bc..fa312a343fbf 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 d56e0f3a0656..4ed5a1a4a2a7 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 625b200e5b33..bc2298865544 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 293af9910037..93b921c7d321 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 8983915d78a9..b0f9af1b8d10 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 c894b60444ac..8766a8173446 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/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs index 80dc6b7250cc..77040aeb94d7 100644 --- a/src/librustdoc/clean/utils.rs +++ b/src/librustdoc/clean/utils.rs @@ -303,7 +303,8 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { return kw::Underscore; } PatKind::Binding(_, _, ident, _) => return ident.name, - PatKind::TupleStruct(ref p, ..) | PatKind::Path(ref p) => qpath_to_string(p), + PatKind::TupleStruct(ref p, ..) + | PatKind::Expr(PatExpr { kind: PatExprKind::Path(ref p), .. }) => qpath_to_string(p), PatKind::Or(pats) => { pats.iter().map(|p| name_from_pat(p).to_string()).collect::>().join(" | ") } diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs index 85f87f01afdf..a15ac155123d 100644 --- a/src/librustdoc/html/render/span_map.rs +++ b/src/librustdoc/html/render/span_map.rs @@ -4,7 +4,9 @@ use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{ExprKind, HirId, Item, ItemKind, Mod, Node, Pat, PatKind, QPath}; +use rustc_hir::{ + ExprKind, HirId, Item, ItemKind, Mod, Node, Pat, PatExpr, PatExprKind, PatKind, QPath, +}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::hygiene::MacroKind; @@ -191,17 +193,21 @@ impl SpanMapVisitor<'_> { } fn handle_pat(&mut self, p: &Pat<'_>) { + let mut check_qpath = |qpath, hir_id| match qpath { + QPath::TypeRelative(_, path) if matches!(path.res, Res::Err) => { + self.infer_id(path.hir_id, Some(hir_id), qpath.span()); + } + QPath::Resolved(_, path) => self.handle_path(path), + _ => {} + }; match p.kind { PatKind::Binding(_, _, _, Some(p)) => self.handle_pat(p), - PatKind::Struct(qpath, _, _) - | PatKind::TupleStruct(qpath, _, _) - | PatKind::Path(qpath) => match qpath { - QPath::TypeRelative(_, path) if matches!(path.res, Res::Err) => { - self.infer_id(path.hir_id, Some(p.hir_id), qpath.span()); - } - QPath::Resolved(_, path) => self.handle_path(path), - _ => {} - }, + PatKind::Struct(qpath, _, _) | PatKind::TupleStruct(qpath, _, _) => { + check_qpath(qpath, p.hir_id) + } + PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), hir_id, .. }) => { + check_qpath(*qpath, *hir_id) + } PatKind::Or(pats) => { for pat in pats { self.handle_pat(pat); diff --git a/src/librustdoc/html/static/js/README.md b/src/librustdoc/html/static/js/README.md index 1fd859ad7cf4..e99d7330f0ed 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 c4faca1c0c3b..000000000000 --- 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 984b0877d8de..ccf4002bb300 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 000000000000..18a3e22113b8 --- /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 98c53b8656f5..d08f15a5bfa8 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 660484c133c3..1ad32721e068 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 183663b94fc2..d7b0e4b4f541 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 3003f4c15033..8f712f4c20c7 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 d77804d045e3..4770ccc12799 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 000000000000..b81099bb9dfd --- /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/clippy_lints/src/equatable_if_let.rs b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs index 8a5cf7f56d5f..7ca2c9536998 100644 --- a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs +++ b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs @@ -56,7 +56,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool { PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)), PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a), PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x), - PatKind::Path(_) | PatKind::Expr(_) => true, + PatKind::Expr(_) => true, } } 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 bb2dc9995df7..3ccfa51ab70b 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/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs index 8503dde3fb6b..274785061b3f 100644 --- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs @@ -7,7 +7,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_lint_allowed, is_never_expr, msrvs, pat_and_expr_can_be_question_mark, peel_blocks}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind}; +use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; @@ -292,7 +292,12 @@ fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: boo // Only do the check if the type is "spelled out" in the pattern if !matches!( pat.kind, - PatKind::Struct(..) | PatKind::TupleStruct(..) | PatKind::Path(..) + PatKind::Struct(..) + | PatKind::TupleStruct(..) + | PatKind::Expr(PatExpr { + kind: PatExprKind::Path(..), + .. + },) ) { return; } diff --git a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs index 8f8390b6f3f9..7b95399c907c 100644 --- a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs +++ b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs @@ -1,6 +1,6 @@ use rustc_errors::Applicability; use rustc_hir::def::Res; -use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, MatchSource, Pat, PatKind, QPath}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::GenericArgKind; use rustc_session::declare_lint_pass; @@ -68,7 +68,7 @@ fn get_some<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>) -> Option { } fn get_none<'tcx>(cx: &LateContext<'tcx>, arm: &Arm<'tcx>) -> Option<&'tcx Expr<'tcx>> { - if let PatKind::Path(QPath::Resolved(_, path)) = arm.pat.kind + if let PatKind::Expr(PatExpr { kind: PatExprKind::Path(QPath::Resolved(_, path)), .. }) = arm.pat.kind && let Some(def_id) = path.res.opt_def_id() // Since it comes from a pattern binding, we need to get the parent to actually match // against it. diff --git a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs index 99a7b8c74be8..97e8423695d9 100644 --- a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs @@ -8,7 +8,7 @@ use clippy_utils::{ }; use rustc_errors::MultiSpan; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, Expr, HirId, Pat, PatKind}; +use rustc_hir::{Arm, Expr, HirId, Pat, PatExpr, PatExprKind, PatKind}; use rustc_lint::LateContext; use rustc_span::Span; @@ -119,7 +119,11 @@ fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { } match arm.pat.kind { PatKind::Binding(..) | PatKind::Wild => true, - PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone), + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(qpath), + hir_id, + .. + }) => is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), 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 index b1a555b91d1b..3deaaf96c1e8 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs @@ -6,7 +6,7 @@ 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_hir::{Arm, Expr, ExprKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::Ty; use rustc_span::symbol::Ident; @@ -60,7 +60,16 @@ pub(crate) fn check_match(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Exp /// 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::Wild + | PatKind::Expr(PatExpr { + kind: PatExprKind::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 }, diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs index 59d375200117..b69294d567d1 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs @@ -7,7 +7,7 @@ use clippy_utils::{is_res_lang_ctor, path_to_local_id, peel_blocks, sugg}; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, ResultErr}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Arm, Expr, Pat, PatKind}; +use rustc_hir::{Arm, Expr, Pat, PatExpr, PatExprKind, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; use rustc_span::sym; @@ -89,7 +89,11 @@ fn applicable_or_arm<'a>(cx: &LateContext<'_>, arms: &'a [Arm<'a>]) -> Option<(& if arms.len() == 2 && arms.iter().all(|arm| arm.guard.is_none()) && let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)| match arm.pat.kind { - PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone), + PatKind::Expr(PatExpr { + hir_id, + kind: PatExprKind::Path(qpath), + .. + }) => is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone), PatKind::TupleStruct(ref qpath, [pat], _) => { matches!(pat.kind, PatKind::Wild) && is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr) 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 0b57740064c1..2ad55d9bf1fd 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs @@ -11,7 +11,7 @@ use rustc_ast::util::parser::ExprPrecedence; use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::def::Res; -use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath}; +use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Mutability, Pat, PatExpr, PatExprKind, PatKind, Path, QPath}; use rustc_lint::LateContext; use rustc_span::{SyntaxContext, sym}; @@ -256,9 +256,11 @@ pub(super) fn try_parse_pattern<'tcx>( match pat.kind { PatKind::Wild => Some(OptionPat::Wild), PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), - PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone) => { - Some(OptionPat::None) - }, + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(qpath), + hir_id, + .. + }) if is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone) => Some(OptionPat::None), PatKind::TupleStruct(ref qpath, [pattern], _) if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionSome) && pat.span.ctxt() == ctxt => { diff --git a/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs b/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs index 6c123649afc2..b1889d26c932 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_as_ref.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_res_lang_ctor, path_res, peel_blocks}; use rustc_errors::Applicability; -use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatKind, QPath}; +use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, LangItem, Mutability, PatExpr, PatExprKind, PatKind, QPath}; use rustc_lint::LateContext; use rustc_middle::ty; @@ -59,7 +59,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { matches!( arm.pat.kind, - PatKind::Path(ref qpath) if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionNone) + PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. }) if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), LangItem::OptionNone) ) } diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index 28e05c273d5c..41e4c75f843e 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -7,7 +7,7 @@ use rustc_arena::DroplessArena; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; -use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatExprKind, PatKind, RangeEnd}; +use rustc_hir::{Arm, Expr, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatExpr, PatExprKind, PatKind, RangeEnd}; use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty; @@ -292,7 +292,11 @@ impl<'a> NormalizedPat<'a> { Self::Tuple(var_id, pats) }, PatKind::Or(pats) => Self::Or(arena.alloc_from_iter(pats.iter().map(|pat| Self::from_pat(cx, arena, pat)))), - PatKind::Path(ref path) => Self::Path(cx.qpath_res(path, pat.hir_id).opt_def_id()), + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(path), + hir_id, + .. + }) => Self::Path(cx.qpath_res(path, *hir_id).opt_def_id()), PatKind::Tuple(pats, wild_idx) => { let field_count = match cx.typeck_results().pat_ty(pat).kind() { ty::Tuple(subs) => subs.len(), 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 595655600890..11b588b33554 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 @@ -3,7 +3,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_refutable, peel_hir_pat_refs, recurse_or_patterns}; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, DefKind, Res}; -use rustc_hir::{Arm, Expr, PatKind, PathSegment, QPath, Ty, TyKind}; +use rustc_hir::{Arm, Expr, PatExpr, PatExprKind, PatKind, PathSegment, QPath, Ty, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, VariantDef}; use rustc_span::sym; @@ -60,8 +60,13 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { // covered by the set of guards that cover it, but that's really hard to do. recurse_or_patterns(arm.pat, |pat| { let path = match &peel_hir_pat_refs(pat).0.kind { - PatKind::Path(path) => { - let id = match cx.qpath_res(path, pat.hir_id) { + PatKind::Expr(PatExpr { + hir_id, + kind: PatExprKind::Path(path), + .. + }) => { + // FIXME(clippy): don't you want to use the hir id of the peeled pat? + let id = match cx.qpath_res(path, *hir_id) { Res::Def( DefKind::Const | DefKind::ConstParam | DefKind::AnonConst | DefKind::InlineConst, _, diff --git a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs index 0d5575efc220..7e65d586110e 100644 --- a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs @@ -8,7 +8,9 @@ use clippy_utils::{ }; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; -use rustc_hir::{Arm, BindingMode, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatExprKind, PatKind, Path, QPath}; +use rustc_hir::{ + Arm, BindingMode, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, +}; use rustc_lint::LateContext; use rustc_span::sym; @@ -183,7 +185,13 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool { return !matches!(annot, BindingMode(ByRef::Yes(_), _)) && pat_ident.name == first_seg.ident.name; }, // Example: `Custom::TypeA => Custom::TypeB`, or `None => None` - (PatKind::Path(QPath::Resolved(_, p_path)), ExprKind::Path(QPath::Resolved(_, e_path))) => { + ( + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(QPath::Resolved(_, p_path)), + .. + }), + ExprKind::Path(QPath::Resolved(_, e_path)), + ) => { return over(p_path.segments, e_path.segments, |p_seg, e_seg| { p_seg.ident.name == e_seg.ident.name }); diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs index edac97344a03..393399660131 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs @@ -9,7 +9,7 @@ use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatExprKind, PatKind, QPath, UnOp}; +use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatExpr, PatExprKind, PatKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::{self, GenericArgKind, Ty}; use rustc_span::{Span, Symbol, sym}; @@ -149,8 +149,12 @@ fn find_method_and_type<'tcx>( None } }, - PatKind::Path(ref path) => { - if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(path, check_pat.hir_id) + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(path), + hir_id, + .. + }) => { + if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(path, *hir_id) && let Some(variant_id) = cx.tcx.opt_parent(ctor_id) { let method = if cx.tcx.lang_items().option_none_variant() == Some(variant_id) { @@ -351,10 +355,20 @@ fn found_good_method<'tcx>( None } }, - (PatKind::TupleStruct(path_left, patterns, _), PatKind::Path(path_right)) - | (PatKind::Path(path_left), PatKind::TupleStruct(path_right, patterns, _)) - if patterns.len() == 1 => - { + ( + PatKind::TupleStruct(path_left, patterns, _), + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(path_right), + .. + }), + ) + | ( + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(path_left), + .. + }), + PatKind::TupleStruct(path_right, patterns, _), + ) if patterns.len() == 1 => { if let PatKind::Wild = patterns[0].kind { find_good_method_for_match( cx, @@ -389,7 +403,13 @@ fn found_good_method<'tcx>( None } }, - (PatKind::Path(path_left), PatKind::Wild) => get_good_method(cx, arms, path_left), + ( + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(path_left), + .. + }), + PatKind::Wild, + ) => get_good_method(cx, arms, path_left), _ => None, } } diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs index 38f876fed802..2f46eaaabb36 100644 --- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs @@ -114,7 +114,7 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp } let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat); - let (msg, sugg) = if let PatKind::Path(_) | PatKind::Expr(_) = pat.kind + let (msg, sugg) = if let PatKind::Expr(_) = pat.kind && let (ty, ty_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(ex)) && let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait() && let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait() @@ -331,14 +331,16 @@ impl<'a> PatState<'a> { #[expect(clippy::similar_names)] fn add_pat<'tcx>(&mut self, cx: &'a PatCtxt<'tcx>, pat: &'tcx Pat<'_>) -> bool { match pat.kind { - PatKind::Path(_) - if match *cx.typeck.pat_ty(pat).peel_refs().kind() { - ty::Adt(adt, _) => adt.is_enum() || (adt.is_struct() && !adt.non_enum_variant().fields.is_empty()), - ty::Tuple(tys) => !tys.is_empty(), - ty::Array(_, len) => len.try_to_target_usize(cx.tcx) != Some(1), - ty::Slice(..) => true, - _ => false, - } => + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(_), + .. + }) if match *cx.typeck.pat_ty(pat).peel_refs().kind() { + ty::Adt(adt, _) => adt.is_enum() || (adt.is_struct() && !adt.non_enum_variant().fields.is_empty()), + ty::Tuple(tys) => !tys.is_empty(), + ty::Array(_, len) => len.try_to_target_usize(cx.tcx) != Some(1), + ty::Slice(..) => true, + _ => false, + } => { matches!(self, Self::Wild) }, @@ -386,7 +388,6 @@ impl<'a> PatState<'a> { | PatKind::Binding(_, _, _, None) | PatKind::Expr(_) | PatKind::Range(..) - | PatKind::Path(_) | PatKind::Never | PatKind::Err(_) => { *self = PatState::Wild; 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 882ab2dda7aa..0e1980a6acb6 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/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs index 6d9e75f51d66..de9f055863cb 100644 --- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs @@ -7,7 +7,9 @@ use clippy_utils::{ use rustc_errors::Applicability; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::def::Res; -use rustc_hir::{Arm, BindingMode, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, UnOp}; +use rustc_hir::{ + Arm, BindingMode, Expr, ExprKind, MatchSource, Mutability, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, UnOp, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::SyntaxContext; @@ -281,7 +283,11 @@ fn try_convert_match<'tcx>( fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool { match arm.pat.kind { - PatKind::Path(ref qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone), + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(qpath), + hir_id, + .. + }) => is_res_lang_ctor(cx, cx.qpath_res(qpath, *hir_id), OptionNone), PatKind::TupleStruct(ref qpath, [first_pat], _) => { is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), ResultErr) && matches!(first_pat.kind, PatKind::Wild) diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index 84b6430294f3..47ce2243aa09 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -10,7 +10,7 @@ use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty}; use rustc_hir::{ self as hir, AmbigArg, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParam, GenericParamKind, - HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind, + HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, Ty, TyKind, }; use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; @@ -258,7 +258,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { && self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS) && let Some(&StackItem::Check { impl_id, .. }) = self.stack.last() // get the path from the pattern - && let PatKind::Path(QPath::Resolved(_, path)) + && let PatKind::Expr(&PatExpr { kind: PatExprKind::Path(QPath::Resolved(_, path)), .. }) | PatKind::TupleStruct(QPath::Resolved(_, path), _, _) | PatKind::Struct(QPath::Resolved(_, path), _, _) = pat.kind && cx.typeck_results().pat_ty(pat) == cx.tcx.type_of(impl_id).instantiate_identity() diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 4dcc8ac7fb0a..6bad78cf8718 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -708,11 +708,6 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.qpath(qpath); self.slice(fields, |pat| self.pat(pat)); }, - PatKind::Path(ref qpath) => { - bind!(self, qpath); - kind!("Path(ref {qpath})"); - self.qpath(qpath); - }, PatKind::Tuple(fields, skip_pos) => { bind!(self, fields); kind!("Tuple({fields}, {skip_pos:?})"); diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index d76231a6eeaa..d0eb5318e645 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -524,7 +524,6 @@ impl HirEqInterExpr<'_, '_, '_> { } eq }, - (PatKind::Path(l), PatKind::Path(r)) => self.eq_qpath(l, r), (&PatKind::Expr(l), &PatKind::Expr(r)) => self.eq_pat_expr(l, r), (&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)), (&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => { @@ -1120,7 +1119,6 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_pat(pat); } }, - PatKind::Path(ref qpath) => self.hash_qpath(qpath), PatKind::Range(s, e, i) => { if let Some(s) = s { self.hash_pat_expr(s); diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index e471dfb6ef19..5a5227af9074 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -106,8 +106,8 @@ use rustc_hir::{ self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, ConstContext, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat, - PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef, - TyKind, UnOp, def, + PatExpr, PatExprKind, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, + TraitItemRef, TraitRef, TyKind, UnOp, def, }; use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::{LateContext, Level, Lint, LintContext}; @@ -560,7 +560,20 @@ macro_rules! maybe_path { }; } maybe_path!(Expr, ExprKind); -maybe_path!(Pat, PatKind); +impl<'hir> MaybePath<'hir> for Pat<'hir> { + fn hir_id(&self) -> HirId { + self.hir_id + } + fn qpath_opt(&self) -> Option<&QPath<'hir>> { + match &self.kind { + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(qpath), + .. + }) => Some(qpath), + _ => None, + } + } +} maybe_path!(Ty, TyKind); /// If `maybe_path` is a path node, resolves it, otherwise returns `Res::Err` @@ -1753,7 +1766,11 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool { PatKind::Wild | PatKind::Never => false, // If `!` typechecked then the type is empty, so not refutable. PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)), PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat), - PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id), + PatKind::Expr(PatExpr { + kind: PatExprKind::Path(qpath), + hir_id, + .. + }) => is_enum_variant(cx, qpath, *hir_id), PatKind::Or(pats) => { // TODO: should be the honest check, that pats is exhaustive set are_refutable(cx, pats) diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index f2bbdda70585..e9a05c45747d 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`. @@ -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)), diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs index 25c0b52d0618..0fda13e06160 100644 --- a/src/tools/miri/src/shims/alloc.rs +++ b/src/tools/miri/src/shims/alloc.rs @@ -1,5 +1,3 @@ -use std::iter; - use rustc_abi::{Align, Size}; use rustc_ast::expand::allocator::AllocatorKind; @@ -80,18 +78,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } } - fn malloc(&mut self, size: u64, zero_init: bool) -> InterpResult<'tcx, Pointer> { + fn malloc(&mut self, size: u64, init: AllocInit) -> InterpResult<'tcx, Pointer> { let this = self.eval_context_mut(); let align = this.malloc_align(size); - let ptr = this.allocate_ptr(Size::from_bytes(size), align, MiriMemoryKind::C.into())?; - if zero_init { - // We just allocated this, the access is definitely in-bounds and fits into our address space. - this.write_bytes_ptr( - ptr.into(), - iter::repeat(0u8).take(usize::try_from(size).unwrap()), - ) - .unwrap(); - } + let ptr = this.allocate_ptr(Size::from_bytes(size), align, MiriMemoryKind::C.into(), init)?; interp_ok(ptr.into()) } @@ -115,6 +105,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::C.into(), + AllocInit::Uninit )?; this.write_pointer(ptr, &memptr)?; interp_ok(Scalar::from_i32(0)) @@ -134,7 +125,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let new_align = this.malloc_align(new_size); if this.ptr_is_null(old_ptr)? { // Here we must behave like `malloc`. - self.malloc(new_size, /*zero_init*/ false) + self.malloc(new_size, AllocInit::Uninit) } else { if new_size == 0 { // C, in their infinite wisdom, made this UB. @@ -147,6 +138,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(new_size), new_align, MiriMemoryKind::C.into(), + AllocInit::Uninit )?; interp_ok(new_ptr.into()) } @@ -187,6 +179,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::C.into(), + AllocInit::Uninit )?; interp_ok(ptr.into()) } diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 6c8ccc839859..1ce0c209de9e 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -1,6 +1,5 @@ use std::collections::hash_map::Entry; use std::io::Write; -use std::iter; use std::path::Path; use rustc_abi::{Align, AlignFromBytesError, Size}; @@ -9,6 +8,7 @@ use rustc_ast::expand::allocator::alloc_error_handler_name; use rustc_hir::def::DefKind; use rustc_hir::def_id::CrateNum; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; +use rustc_middle::mir::interpret::AllocInit; use rustc_middle::ty::Ty; use rustc_middle::{mir, ty}; use rustc_span::Symbol; @@ -442,7 +442,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let [size] = this.check_shim(abi, Conv::C, link_name, args)?; let size = this.read_target_usize(size)?; if size <= this.max_size_of_val().bytes() { - let res = this.malloc(size, /*zero_init:*/ false)?; + let res = this.malloc(size, AllocInit::Uninit)?; this.write_pointer(res, dest)?; } else { // If this does not fit in an isize, return null and, on Unix, set errno. @@ -457,7 +457,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let items = this.read_target_usize(items)?; let elem_size = this.read_target_usize(elem_size)?; if let Some(size) = this.compute_size_in_bytes(Size::from_bytes(elem_size), items) { - let res = this.malloc(size.bytes(), /*zero_init:*/ true)?; + let res = this.malloc(size.bytes(), AllocInit::Zero)?; this.write_pointer(res, dest)?; } else { // On size overflow, return null and, on Unix, set errno. @@ -509,6 +509,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), memory_kind.into(), + AllocInit::Uninit )?; ecx.write_pointer(ptr, dest) @@ -537,14 +538,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::Rust.into(), + AllocInit::Zero )?; - - // We just allocated this, the access is definitely in-bounds. - this.write_bytes_ptr( - ptr.into(), - iter::repeat(0u8).take(usize::try_from(size).unwrap()), - ) - .unwrap(); this.write_pointer(ptr, dest) }); } @@ -604,6 +599,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(new_size), align, MiriMemoryKind::Rust.into(), + AllocInit::Uninit )?; this.write_pointer(new_ptr, dest) }); diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index d3777a3f7a33..3874cda26973 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -1112,6 +1112,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), dirent_layout.align.abi, MiriMemoryKind::Runtime.into(), + AllocInit::Uninit )?; let entry: Pointer = entry.into(); diff --git a/src/tools/miri/src/shims/unix/linux/mem.rs b/src/tools/miri/src/shims/unix/linux/mem.rs index 8e796d5dce55..6418d749d3d9 100644 --- a/src/tools/miri/src/shims/unix/linux/mem.rs +++ b/src/tools/miri/src/shims/unix/linux/mem.rs @@ -49,16 +49,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(new_size), align, MiriMemoryKind::Mmap.into(), + AllocInit::Zero )?; - if let Some(increase) = new_size.checked_sub(old_size) { - // We just allocated this, the access is definitely in-bounds and fits into our address space. - // mmap guarantees new mappings are zero-init. - this.write_bytes_ptr( - ptr.wrapping_offset(Size::from_bytes(old_size), this).into(), - std::iter::repeat(0u8).take(usize::try_from(increase).unwrap()), - ) - .unwrap(); - } interp_ok(Scalar::from_pointer(ptr, this)) } diff --git a/src/tools/miri/src/shims/unix/mem.rs b/src/tools/miri/src/shims/unix/mem.rs index 5531b944e17d..2d5d3a6471ab 100644 --- a/src/tools/miri/src/shims/unix/mem.rs +++ b/src/tools/miri/src/shims/unix/mem.rs @@ -111,15 +111,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(this.eval_libc("MAP_FAILED")); } - let ptr = - this.allocate_ptr(Size::from_bytes(map_length), align, MiriMemoryKind::Mmap.into())?; - // We just allocated this, the access is definitely in-bounds and fits into our address space. - // mmap guarantees new mappings are zero-init. - this.write_bytes_ptr( - ptr.into(), - std::iter::repeat(0u8).take(usize::try_from(map_length).unwrap()), - ) - .unwrap(); + let ptr = this.allocate_ptr( + Size::from_bytes(map_length), + align, + MiriMemoryKind::Mmap.into(), + // mmap guarantees new mappings are zero-init. + AllocInit::Zero + )?; interp_ok(Scalar::from_pointer(ptr, this)) } diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 0bf56c3d005f..4462d025bead 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -253,8 +253,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.read_target_isize(handle)?; let flags = this.read_scalar(flags)?.to_u32()?; let size = this.read_target_usize(size)?; - let heap_zero_memory = 0x00000008; // HEAP_ZERO_MEMORY - let zero_init = (flags & heap_zero_memory) == heap_zero_memory; + const HEAP_ZERO_MEMORY: u32 = 0x00000008; + let init = if (flags & HEAP_ZERO_MEMORY) == HEAP_ZERO_MEMORY { + AllocInit::Zero + } else { + AllocInit::Uninit + }; // Alignment is twice the pointer size. // Source: let align = this.tcx.pointer_size().bytes().strict_mul(2); @@ -262,13 +266,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::WinHeap.into(), + init )?; - if zero_init { - this.write_bytes_ptr( - ptr.into(), - iter::repeat(0u8).take(usize::try_from(size).unwrap()), - )?; - } this.write_pointer(ptr, dest)?; } "HeapFree" => { @@ -300,6 +299,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Size::from_bytes(size), Align::from_bytes(align).unwrap(), MiriMemoryKind::WinHeap.into(), + AllocInit::Uninit )?; this.write_pointer(new_ptr, dest)?; } 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 4d5ddd75f385..6ca53c0eb6fc 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/run-make-support/src/command.rs b/src/tools/run-make-support/src/command.rs index b4dc753ab534..70a72bd1abeb 100644 --- a/src/tools/run-make-support/src/command.rs +++ b/src/tools/run-make-support/src/command.rs @@ -388,9 +388,13 @@ impl CompletedProcess { self } + /// Check the **exit status** of the process. On Unix, this is *not* the **wait status**. + /// + /// See [`std::process::ExitStatus::code`]. This is not to be confused with + /// [`std::process::ExitCode`]. #[track_caller] pub fn assert_exit_code(&self, code: i32) -> &Self { - assert!(self.output.status.code() == Some(code)); + assert_eq!(self.output.status.code(), Some(code)); self } } diff --git a/src/tools/run-make-support/src/external_deps/rustc.rs b/src/tools/run-make-support/src/external_deps/rustc.rs index 8894ea7fb209..b70db7130f67 100644 --- a/src/tools/run-make-support/src/external_deps/rustc.rs +++ b/src/tools/run-make-support/src/external_deps/rustc.rs @@ -1,5 +1,6 @@ use std::ffi::{OsStr, OsString}; -use std::path::Path; +use std::path::{Path, PathBuf}; +use std::str::FromStr as _; use crate::command::Command; use crate::env::env_var; @@ -390,3 +391,10 @@ impl Rustc { self } } + +/// Query the sysroot path corresponding `rustc --print=sysroot`. +#[track_caller] +pub fn sysroot() -> PathBuf { + let path = rustc().print("sysroot").run().stdout_utf8(); + PathBuf::from_str(path.trim()).unwrap() +} diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index f92668a6a978..2dfca7c4803e 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 1029844cd3ab..c42ae171d866 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 1327bb3ab59c..16c7b5ca00a0 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 68c7173d1e40..994ba2aa069d 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 f483efa85179..e136dd18a55e 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 5d67902c8ac1..c30ad0163b9d 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 e64e498c1707..28c824fd31d7 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 ac262950f13c..34635997bdff 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 0fec7674109b..65a39c565611 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,35 +31,103 @@ pub struct PerNsGlobImports { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ImportOrExternCrate { + Glob(GlobId), Import(ImportId), ExternCrate(ExternCrateId), } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub(crate) enum ImportType { - Import(ImportId), - Glob(UseId), - 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 into_import(self) -> Option { + 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 enum ImportOrGlob { + Glob(GlobId), + Import(ImportId), +} + +impl ImportOrGlob { + pub fn into_import(self) -> Option { + match self { + ImportOrGlob::Import(it) => Some(it), + _ => None, + } + } } #[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 afdc49a2dc59..e83ce6dc42ce 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 84c105a0a346..c78818c642ce 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 1e4b42dff5fb..06276335b718 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 ab4ffbb2c1e4..d7e4ca41cd5d 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 318aee04f7b7..73fc6787bfe8 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 8963a5767942..ddb9d4a134d3 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 a05c4dcf9bd7..610886d55f40 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 899dd4afffef..c2d3f67f17e7 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 0b9b6da8d513..8c556d8a8c3f 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 89eae862bd96..f0cf7ebf479f 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 cc53d2e34aac..848870c3a384 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()), + } } - /// Resolve a name from the text of token. - fn resolve(raw_text: &str) -> Name { - Name::new_text(raw_text.trim_start_matches("r#")) + 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 { + 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 2b5342314a65..62feca5f8cbb 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 4991d173b9c4..774991560e9c 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 3545bf767767..ae8fbe2ce6d7 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); + 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) => { - match tr.self_type_parameter(Interner).kind(Interner) { - TyKind::Alias(AliasTy::Projection(proj)) => proj == self, - _ => false, + 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 2523aba53833..9283c46d0f61 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 2fe90a8a9243..d40816ba8ced 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 6b6c0348dcb4..b951443897cb 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 182032f04812..1cea67ee9641 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 74acf23b75ab..8866de22dfb9 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 db3121d3cd35..0cbc75726bf3 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 523bc6f10aab..09470bed9cfb 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 6b78d7a3631f..b699ccde4128 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 a6b8ed70c363..2ebd88edae2d 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 82d8db425892..fb533077d962 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 a5c5b08d5b0c..eb784cd1226f 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 bb04a43cf961..d34cf895cd90 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 615b5d3f98b5..d4f2ea3bd941 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 97321f4ec1ef..7b6f76d00452 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 7a92d8911bf8..47e4a68293f0 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 8a7a06b380f5..10915f8aafb8 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 9692b7059291..bbf18e21948e 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 2925e2334b44..7b38c795dc80 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 849b8a42c694..2a8465f634cf 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 972303c2a041..a79a82be4507 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 eb1d538f8743..c3404173eafe 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 40669c65c576..a22e7b272ea0 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 7679d9076ded..d12654665ce9 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 db18b531d7c3..e71017517019 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 73313eeaa6b7..24243f57b46a 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 6d1945c45341..831f5665f4aa 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 bafe32942098..cca6a22f290d 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 67ea05e002b7..2c39a8fdfed7 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 9d62622add20..b384987c51ce 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 8b1ce11e8a45..45aab38e8ea0 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 3a2a4a23a198..2f1860cbb59a 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 3c4d489c0ff8..f5a50ae81907 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 dc2f9a768029..41a82409597b 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 56d7eeaf8ea0..8051d48ca5fe 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 61e8114d381a..dc7eacbfbafd 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 415d87c6239b..e357ab24d22d 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 3b97d67169ec..c3354902c3b7 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 c71356e5300f..aab54ca5e014 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 6490171fbb48..e265e92f9794 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 5675dfb5c6ff..124abb17b6a1 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 09eb19201c5b..1b952f31360c 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 e053e299d903..742036265211 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 04bb178c658f..866b83a61460 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 b7dbf0a6306c..9d91f95eb65b 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 e117dbf4bdf0..663a038580d5 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 d491e438feff..2e7c53def7fc 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 04b3a47a64da..593b1edde5cc 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 49d26dfe25c1..d12bda0816fd 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 9e3506d6f53b..2f4d07446f2c 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 a045c22c2dff..f045e44dd318 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 42efbd68e33d..59914bedde43 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 a75aba137be6..7fc563a42410 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 c94644eeb89b..e5ce10a771ef 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 557c95f704b9..0a7141c19b6b 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 535777dfcbea..7dce95592b81 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 515bc418cb46..2fdd8358637d 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 dca889d1a8ef..f22041ebe233 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 fd1044e51bc2..938b7182bc94 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 13591dfb2eeb..f3109b9bb73a 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 1e99d7ad6e68..50c91a69602c 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 4edc3633fbe6..4bead14e31d4 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 bc9843f3f35a..cfd8919730ad 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 f804cc367727..d18732a6b846 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 def = Definition::from(target_method); + 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 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 18a3fed07ece..9d4c103fc2e0 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 46242b75dd0b..40f3406b72d3 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 014b751f95b0..8c32cc9720af 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 6d83a747d766..1f723c85df7a 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 ab5464156f0a..01a1a4545c47 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 429ddd31cbd0..e9b728bcaa75 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 7fa7ab1a94d6..8471547727fe 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 90b8be64a46d..3767d34e2c7a 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 906f2acf0c44..3e91618d08e6 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 037b328d971f..762a4c265518 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 1358d3722f89..27c7c3d49818 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 346e2862b0fd..e942f5a6aac7 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 ba739df3092b..07dfd83c4eb7 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 5e7ee54c6af7..c0358ef929b4 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 200b14027f80..b3bf285edfb1 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 b3b46421b50f..9bc78ff87b8a 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 00446b27cf2f..5654c04a5928 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 c7614849e015..59293ee3f965 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 466eb14b55ea..409cf3cc7813 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 6a88cf022dfb..a39639676104 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 4bf9b59e7d03..e472da0c89b0 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 f98d983ac060..dcd62753cb2f 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 bcaec5201959..18c27c844964 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 3dc4379258fa..44325fa1a29e 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 22f06d68d80d..2309f94a7429 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 98efc637c2c8..84ba89d9f31f 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 190015d7faad..39cbf53eaa21 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 fcfd06679bf2..5cdc51a1c199 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 61ec576dd4f9..ccffa7a671e6 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 134de92feab3..ca4372aa83f8 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 a5516e7f9d4f..bff53cf98b7b 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 dca231604fa1..ff027ac5848b 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 fd06736a2524..4ed68d18e807 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 3ba492e09597..c990212d585d 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 a632fc6f5fb8..c7ee4e402363 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 4770e70cc2b7..4ff03d819e60 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 ae7db1554b19..fbe63eb0563c 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 8bc5c9a87fce..57664cfe893b 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 4770e70cc2b7..4ff03d819e60 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 ae7db1554b19..fbe63eb0563c 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 7f0fc00d5489..7d032d277aa9 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 3ed73918de31..0d57d9d0142d 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 000000000000..0d57d9d0142d --- /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 6b3fd7e898d1..fe135031b313 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/borrowck/issue-93093.rs b/tests/ui/borrowck/issue-93093.rs index e85b296c983f..1521b2072389 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 b6a2768b61da..d788ce331973 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 5c70eccfbd35..190ddeaa8f28 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/const-generics/bad-subst-const-kind.stderr b/tests/ui/const-generics/bad-subst-const-kind.stderr index 5c8d9c903635..b36052696425 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 e03580ec007c..7cb67252da52 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 000000000000..91b89cd3fff3 --- /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 000000000000..ceea09733776 --- /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 978a9744e88a..0e26daa3a0f1 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 d1bb5c1242f0..bd169ed2ec8f 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 25d14d80c3ec..c3ff216432ee 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/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 000000000000..fa1663d49eb2 --- /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 000000000000..d069d39514dc --- /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 a0392113ab15..6def86e4ba8f 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 8ccb4cbb0c16..62dc027e31f2 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-const-items/def-site-eval.fail.stderr b/tests/ui/generic-const-items/def-site-eval.fail.stderr new file mode 100644 index 000000000000..22a5f2916977 --- /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 000000000000..3ed7f96aed02 --- /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 000000000000..f10d450f6bdb --- /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/lint/dead-code/lint-dead-code-1.rs b/tests/ui/lint/dead-code/lint-dead-code-1.rs index ddcafedf7bc5..a7f654b5d8bf 100644 --- a/tests/ui/lint/dead-code/lint-dead-code-1.rs +++ b/tests/ui/lint/dead-code/lint-dead-code-1.rs @@ -29,6 +29,7 @@ const used_const: isize = 0; pub const used_const2: isize = used_const; const USED_CONST: isize = 1; const CONST_USED_IN_ENUM_DISCRIMINANT: isize = 11; +const CONST_USED_IN_RANGE_PATTERN: isize = 12; pub type typ = *const UsedStruct4; pub struct PubStruct; @@ -81,6 +82,7 @@ pub fn pub_fn() { match i { USED_STATIC => (), USED_CONST => (), + CONST_USED_IN_RANGE_PATTERN..100 => {} _ => () } f::(); diff --git a/tests/ui/lint/dead-code/lint-dead-code-1.stderr b/tests/ui/lint/dead-code/lint-dead-code-1.stderr index eb728b5b9305..c4410114cea8 100644 --- a/tests/ui/lint/dead-code/lint-dead-code-1.stderr +++ b/tests/ui/lint/dead-code/lint-dead-code-1.stderr @@ -17,19 +17,19 @@ LL | const priv_const: isize = 0; | ^^^^^^^^^^ error: struct `PrivStruct` is never constructed - --> $DIR/lint-dead-code-1.rs:35:8 + --> $DIR/lint-dead-code-1.rs:36:8 | LL | struct PrivStruct; | ^^^^^^^^^^ error: enum `priv_enum` is never used - --> $DIR/lint-dead-code-1.rs:64:6 + --> $DIR/lint-dead-code-1.rs:65:6 | LL | enum priv_enum { foo2, bar2 } | ^^^^^^^^^ error: variant `bar3` is never constructed - --> $DIR/lint-dead-code-1.rs:67:5 + --> $DIR/lint-dead-code-1.rs:68:5 | LL | enum used_enum { | --------- variant in this enum @@ -38,25 +38,25 @@ LL | bar3 | ^^^^ error: function `priv_fn` is never used - --> $DIR/lint-dead-code-1.rs:88:4 + --> $DIR/lint-dead-code-1.rs:90:4 | LL | fn priv_fn() { | ^^^^^^^ error: function `foo` is never used - --> $DIR/lint-dead-code-1.rs:93:4 + --> $DIR/lint-dead-code-1.rs:95:4 | LL | fn foo() { | ^^^ error: function `bar` is never used - --> $DIR/lint-dead-code-1.rs:98:4 + --> $DIR/lint-dead-code-1.rs:100:4 | LL | fn bar() { | ^^^ error: function `baz` is never used - --> $DIR/lint-dead-code-1.rs:102:4 + --> $DIR/lint-dead-code-1.rs:104:4 | LL | fn baz() -> impl Copy { | ^^^ diff --git a/tests/ui/mut/mutable-class-fields-2.stderr b/tests/ui/mut/mutable-class-fields-2.stderr index eb0c54f885ba..7a6ff4da2bf5 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/pattern/issue-110508.rs b/tests/ui/pattern/issue-110508.rs index 6ed0476183ec..74a8d673e837 100644 --- a/tests/ui/pattern/issue-110508.rs +++ b/tests/ui/pattern/issue-110508.rs @@ -1,5 +1,7 @@ //@ run-pass +#![deny(dead_code)] + #[derive(PartialEq, Eq)] pub enum Foo { FooA(()), @@ -11,6 +13,7 @@ impl Foo { const A2: Foo = Self::FooA(()); const A3: Self = Foo::FooA(()); const A4: Self = Self::FooA(()); + const A5: u32 = 1; } fn main() { @@ -35,4 +38,9 @@ fn main() { Foo::A4 => {}, _ => {}, } + + match 3 { + Foo::A5..5 => {} + _ => {} + } } 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/suggestions/suggest-ref-mut.rs b/tests/ui/suggestions/suggest-ref-mut.rs index b40439b8e372..9f5df9303c33 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 cc00022ab8e3..935a04c052ac 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 b6c3ccdedfb0..000000000000 --- 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 72b2d03fe203..7ec8b04cfce0 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 b6c3ccdedfb0..b1186d5d5dc7 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 6191681286a3..02398d27501c 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/thir-print/thir-tree-match.stdout b/tests/ui/thir-print/thir-tree-match.stdout index 916f296ccfc6..910582ae4d9e 100644 --- a/tests/ui/thir-print/thir-tree-match.stdout +++ b/tests/ui/thir-print/thir-tree-match.stdout @@ -26,16 +26,16 @@ params: [ body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:15:32: 21:2 (#0) kind: Scope { - region_scope: Node(26) - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).26)) + region_scope: Node(28) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).28)) value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:15:32: 21:2 (#0) kind: Block { @@ -47,7 +47,7 @@ body: expr: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:16:5: 20:6 (#0) kind: Scope { @@ -56,14 +56,14 @@ body: value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:16:5: 20:6 (#0) kind: Match { scrutinee: Expr { ty: Foo - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:16:11: 16:14 (#0) kind: Scope { @@ -72,7 +72,7 @@ body: value: Expr { ty: Foo - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(28)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:16:11: 16:14 (#0) kind: VarRef { @@ -123,16 +123,16 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(13)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(14)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) kind: Scope { - region_scope: Node(14) - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).14)) + region_scope: Node(15) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).15)) value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(13)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(14)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) kind: Literal( lit: Spanned { node: Bool(true), span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) }, neg: false) @@ -140,8 +140,8 @@ body: } } } - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).13)) - scope: Node(13) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).14)) + scope: Node(14) span: $DIR/thir-tree-match.rs:17:9: 17:40 (#0) } Arm { @@ -175,16 +175,16 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(19)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(20)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) kind: Scope { - region_scope: Node(20) - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).20)) + region_scope: Node(21) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).21)) value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(19)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(20)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) kind: Literal( lit: Spanned { node: Bool(false), span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) }, neg: false) @@ -192,8 +192,8 @@ body: } } } - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).19)) - scope: Node(19) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).20)) + scope: Node(20) span: $DIR/thir-tree-match.rs:18:9: 18:32 (#0) } Arm { @@ -219,16 +219,16 @@ body: body: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) kind: Scope { - region_scope: Node(25) - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).25)) + region_scope: Node(27) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).27)) value: Expr { ty: bool - temp_lifetime: TempLifetime { temp_lifetime: Some(Node(24)), backwards_incompatible: None } + temp_lifetime: TempLifetime { temp_lifetime: Some(Node(26)), backwards_incompatible: None } span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) kind: Literal( lit: Spanned { node: Bool(true), span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) }, neg: false) @@ -236,8 +236,8 @@ body: } } } - lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).24)) - scope: Node(24) + lint_level: Explicit(HirId(DefId(0:16 ~ thir_tree_match[fcf8]::has_match).26)) + scope: Node(26) span: $DIR/thir-tree-match.rs:19:9: 19:28 (#0) } ] diff --git a/tests/ui/traits/const-traits/const-impl-trait.stderr b/tests/ui/traits/const-traits/const-impl-trait.stderr index 4e3200594485..27d7957c0014 100644 --- a/tests/ui/traits/const-traits/const-impl-trait.stderr +++ b/tests/ui/traits/const-traits/const-impl-trait.stderr @@ -99,26 +99,6 @@ 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:23: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 - | -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:27:22 | @@ -149,6 +129,36 @@ 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:23: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 + | +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 + | +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[E0015]: cannot call non-const operator in constants --> $DIR/const-impl-trait.rs:35:13 | @@ -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/typeck/issue-107775.stderr b/tests/ui/typeck/issue-107775.stderr index 180b0183a3f2..dad7e1581e79 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 1bcc0dbaf672..92ad83c33000 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