diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index 40e801d03885c..f71e6f3e6f3a6 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -1,14 +1,18 @@ use std::io; +use rustc_data_structures::fx::FxHashSet; +use rustc_index::IndexVec; use rustc_middle::mir::pretty::{ PassWhere, PrettyPrintMirOptions, create_dump_file, dump_enabled, dump_mir_to_writer, }; use rustc_middle::mir::{Body, ClosureRegionRequirements}; -use rustc_middle::ty::TyCtxt; +use rustc_middle::ty::{RegionVid, TyCtxt}; use rustc_session::config::MirIncludeSpans; use crate::borrow_set::BorrowSet; +use crate::constraints::OutlivesConstraint; use crate::polonius::{LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSet}; +use crate::type_check::Locations; use crate::{BorrowckInferCtxt, RegionInferenceContext}; /// `-Zdump-mir=polonius` dumps MIR annotated with NLL and polonius specific information. @@ -50,6 +54,8 @@ pub(crate) fn dump_polonius_mir<'tcx>( /// - the NLL MIR /// - the list of polonius localized constraints /// - a mermaid graph of the CFG +/// - a mermaid graph of the NLL regions and the constraints between them +/// - a mermaid graph of the NLL SCCs and the constraints between them fn emit_polonius_dump<'tcx>( tcx: TyCtxt<'tcx>, body: &Body<'tcx>, @@ -68,7 +74,7 @@ fn emit_polonius_dump<'tcx>( // Section 1: the NLL + Polonius MIR. writeln!(out, "
")?; writeln!(out, "Raw MIR dump")?; - writeln!(out, "
")?;
+    writeln!(out, "
")?;
     emit_html_mir(
         tcx,
         body,
@@ -78,15 +84,31 @@ fn emit_polonius_dump<'tcx>(
         closure_region_requirements,
         out,
     )?;
-    writeln!(out, "
")?; + writeln!(out, "
")?; writeln!(out, "
")?; // Section 2: mermaid visualization of the CFG. writeln!(out, "
")?; writeln!(out, "Control-flow graph")?; - writeln!(out, "
")?;
+    writeln!(out, "
")?;
     emit_mermaid_cfg(body, out)?;
-    writeln!(out, "
")?; + writeln!(out, "
")?; + writeln!(out, "
")?; + + // Section 3: mermaid visualization of the NLL region graph. + writeln!(out, "
")?; + writeln!(out, "NLL regions")?; + writeln!(out, "
")?;
+    emit_mermaid_nll_regions(regioncx, out)?;
+    writeln!(out, "
")?; + writeln!(out, "
")?; + + // Section 4: mermaid visualization of the NLL SCC graph. + writeln!(out, "
")?; + writeln!(out, "NLL SCCs")?; + writeln!(out, "
")?;
+    emit_mermaid_nll_sccs(regioncx, out)?;
+    writeln!(out, "
")?; writeln!(out, "
")?; // Finalize the dump with the HTML epilogue. @@ -261,3 +283,112 @@ fn emit_mermaid_cfg(body: &Body<'_>, out: &mut dyn io::Write) -> io::Result<()> Ok(()) } + +/// Emits a region's label: index, universe, external name. +fn render_region( + region: RegionVid, + regioncx: &RegionInferenceContext<'_>, + out: &mut dyn io::Write, +) -> io::Result<()> { + let def = regioncx.region_definition(region); + let universe = def.universe; + + write!(out, "'{}", region.as_usize())?; + if !universe.is_root() { + write!(out, "/{universe:?}")?; + } + if let Some(name) = def.external_name.and_then(|e| e.get_name()) { + write!(out, " ({name})")?; + } + Ok(()) +} + +/// Emits a mermaid flowchart of the NLL regions and the outlives constraints between them, similar +/// to the graphviz version. +fn emit_mermaid_nll_regions<'tcx>( + regioncx: &RegionInferenceContext<'tcx>, + out: &mut dyn io::Write, +) -> io::Result<()> { + // The mermaid chart type: a top-down flowchart. + writeln!(out, "flowchart TD")?; + + // Emit the region nodes. + for region in regioncx.var_infos.indices() { + write!(out, "{}[\"", region.as_usize())?; + render_region(region, regioncx, out)?; + writeln!(out, "\"]")?; + } + + // Get a set of edges to check for the reverse edge being present. + let edges: FxHashSet<_> = regioncx.outlives_constraints().map(|c| (c.sup, c.sub)).collect(); + + // Order (and deduplicate) edges for traversal, to display them in a generally increasing order. + let constraint_key = |c: &OutlivesConstraint<'_>| { + let min = c.sup.min(c.sub); + let max = c.sup.max(c.sub); + (min, max) + }; + let mut ordered_edges: Vec<_> = regioncx.outlives_constraints().collect(); + ordered_edges.sort_by_key(|c| constraint_key(c)); + ordered_edges.dedup_by_key(|c| constraint_key(c)); + + for outlives in ordered_edges { + // Source node. + write!(out, "{} ", outlives.sup.as_usize())?; + + // The kind of arrow: bidirectional if the opposite edge exists in the set. + if edges.contains(&(outlives.sub, outlives.sup)) { + write!(out, "<")?; + } + write!(out, "-- ")?; + + // Edge label from its `Locations`. + match outlives.locations { + Locations::All(_) => write!(out, "All")?, + Locations::Single(location) => write!(out, "{:?}", location)?, + } + + // Target node. + writeln!(out, " --> {}", outlives.sub.as_usize())?; + } + Ok(()) +} + +/// Emits a mermaid flowchart of the NLL SCCs and the outlives constraints between them, similar +/// to the graphviz version. +fn emit_mermaid_nll_sccs<'tcx>( + regioncx: &RegionInferenceContext<'tcx>, + out: &mut dyn io::Write, +) -> io::Result<()> { + // The mermaid chart type: a top-down flowchart. + writeln!(out, "flowchart TD")?; + + // Gather and emit the SCC nodes. + let mut nodes_per_scc: IndexVec<_, _> = + regioncx.constraint_sccs().all_sccs().map(|_| Vec::new()).collect(); + for region in regioncx.var_infos.indices() { + let scc = regioncx.constraint_sccs().scc(region); + nodes_per_scc[scc].push(region); + } + for (scc, regions) in nodes_per_scc.iter_enumerated() { + // The node label: the regions contained in the SCC. + write!(out, "{scc}[\"SCC({scc}) = {{", scc = scc.as_usize())?; + for (idx, ®ion) in regions.iter().enumerate() { + render_region(region, regioncx, out)?; + if idx < regions.len() - 1 { + write!(out, ",")?; + } + } + writeln!(out, "}}\"]")?; + } + + // Emit the edges between SCCs. + let edges = regioncx.constraint_sccs().all_sccs().flat_map(|source| { + regioncx.constraint_sccs().successors(source).iter().map(move |&target| (source, target)) + }); + for (source, target) in edges { + writeln!(out, "{} --> {}", source.as_usize(), target.as_usize())?; + } + + Ok(()) +} diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 2c99597922e8f..fdcd9caf4ac87 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -126,7 +126,7 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { &mut self, name: &str, params: Vec, - returns: Vec, + mut returns: Vec, args: &[Value], ) -> Cow<'_, [Value]> { // Pass i128 arguments by-ref on Windows. @@ -150,15 +150,19 @@ impl<'tcx> FunctionCx<'_, '_, 'tcx> { (params, args.into()) }; - // Return i128 using a return area pointer on Windows and s390x. - let adjust_ret_param = - if self.tcx.sess.target.is_like_windows || self.tcx.sess.target.arch == "s390x" { - returns.len() == 1 && returns[0].value_type == types::I128 - } else { - false - }; + let ret_single_i128 = returns.len() == 1 && returns[0].value_type == types::I128; + if ret_single_i128 && self.tcx.sess.target.is_like_windows { + // Return i128 using the vector ABI on Windows + returns[0].value_type = types::I64X2; + + let ret = self.lib_call_unadjusted(name, params, returns, &args)[0]; - if adjust_ret_param { + // FIXME(bytecodealliance/wasmtime#6104) use bitcast instead of store to get from i64x2 to i128 + let ret_ptr = self.create_stack_slot(16, 16); + ret_ptr.store(self, ret, MemFlags::trusted()); + Cow::Owned(vec![ret_ptr.load(self, types::I128, MemFlags::trusted())]) + } else if ret_single_i128 && self.tcx.sess.target.arch == "s390x" { + // Return i128 using a return area pointer on s390x. let mut params = params; let mut args = args.to_vec(); diff --git a/compiler/rustc_codegen_cranelift/src/cast.rs b/compiler/rustc_codegen_cranelift/src/cast.rs index 0b5cb1547fc69..4463631c524be 100644 --- a/compiler/rustc_codegen_cranelift/src/cast.rs +++ b/compiler/rustc_codegen_cranelift/src/cast.rs @@ -96,25 +96,9 @@ pub(crate) fn clif_int_or_float_cast( }, ); - if fx.tcx.sess.target.is_like_windows { - let ret = fx.lib_call( - &name, - vec![AbiParam::new(from_ty)], - vec![AbiParam::new(types::I64X2)], - &[from], - )[0]; - // FIXME(bytecodealliance/wasmtime#6104) use bitcast instead of store to get from i64x2 to i128 - let ret_ptr = fx.create_stack_slot(16, 16); - ret_ptr.store(fx, ret, MemFlags::trusted()); - ret_ptr.load(fx, types::I128, MemFlags::trusted()) - } else { - fx.lib_call( - &name, - vec![AbiParam::new(from_ty)], - vec![AbiParam::new(types::I128)], - &[from], - )[0] - } + fx.lib_call(&name, vec![AbiParam::new(from_ty)], vec![AbiParam::new(types::I128)], &[ + from, + ])[0] } else if to_ty == types::I8 || to_ty == types::I16 { // FIXME implement fcvt_to_*int_sat.i8/i16 let val = if to_signed { diff --git a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs index dcfd7ddabbc42..df5a79086fa3e 100644 --- a/compiler/rustc_codegen_cranelift/src/codegen_i128.rs +++ b/compiler/rustc_codegen_cranelift/src/codegen_i128.rs @@ -33,28 +33,14 @@ pub(crate) fn maybe_codegen<'tcx>( (BinOp::Rem, true) => "__modti3", _ => unreachable!(), }; - if fx.tcx.sess.target.is_like_windows { - let args = [lhs.load_scalar(fx), rhs.load_scalar(fx)]; - let ret = fx.lib_call( - name, - vec![AbiParam::new(types::I128), AbiParam::new(types::I128)], - vec![AbiParam::new(types::I64X2)], - &args, - )[0]; - // FIXME(bytecodealliance/wasmtime#6104) use bitcast instead of store to get from i64x2 to i128 - let ret_place = CPlace::new_stack_slot(fx, lhs.layout()); - ret_place.to_ptr().store(fx, ret, MemFlags::trusted()); - Some(ret_place.to_cvalue(fx)) - } else { - let args = [lhs.load_scalar(fx), rhs.load_scalar(fx)]; - let ret_val = fx.lib_call( - name, - vec![AbiParam::new(types::I128), AbiParam::new(types::I128)], - vec![AbiParam::new(types::I128)], - &args, - )[0]; - Some(CValue::by_val(ret_val, lhs.layout())) - } + let args = [lhs.load_scalar(fx), rhs.load_scalar(fx)]; + let ret_val = fx.lib_call( + name, + vec![AbiParam::new(types::I128), AbiParam::new(types::I128)], + vec![AbiParam::new(types::I128)], + &args, + )[0]; + Some(CValue::by_val(ret_val, lhs.layout())) } BinOp::Lt | BinOp::Le | BinOp::Eq | BinOp::Ge | BinOp::Gt | BinOp::Ne | BinOp::Cmp => None, BinOp::Shl | BinOp::ShlUnchecked | BinOp::Shr | BinOp::ShrUnchecked => None, diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 74d02ac22276c..9495030f124b5 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -797,7 +797,6 @@ fn test_unstable_options_tracking_hash() { tracked!(function_sections, Some(false)); tracked!(human_readable_cgu_names, true); tracked!(incremental_ignore_spans, true); - tracked!(inline_in_all_cgus, Some(true)); tracked!(inline_mir, Some(true)); tracked!(inline_mir_hint_threshold, Some(123)); tracked!(inline_mir_threshold, Some(123)); diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 3eccf56d8c4dd..6fa3fa2432de5 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -91,13 +91,8 @@ impl<'tcx> MonoItem<'tcx> { } pub fn instantiation_mode(&self, tcx: TyCtxt<'tcx>) -> InstantiationMode { - let generate_cgu_internal_copies = tcx - .sess - .opts - .unstable_opts - .inline_in_all_cgus - .unwrap_or_else(|| tcx.sess.opts.optimize != OptLevel::No) - && !tcx.sess.link_dead_code(); + let generate_cgu_internal_copies = + (tcx.sess.opts.optimize != OptLevel::No) && !tcx.sess.link_dead_code(); match *self { MonoItem::Fn(ref instance) => { @@ -121,8 +116,8 @@ impl<'tcx> MonoItem<'tcx> { } // At this point we don't have explicit linkage and we're an - // inlined function. If we're inlining into all CGUs then we'll - // be creating a local copy per CGU. + // inlined function. If this crate's build settings permit, + // we'll be creating a local copy per CGU. if generate_cgu_internal_copies { return InstantiationMode::LocalCopy; } diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 50287b706ce83..2373ab67d42ed 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2830,9 +2830,10 @@ pub(crate) struct DynAfterMut { pub(crate) struct FnPointerCannotBeConst { #[primary_span] pub span: Span, - #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")] #[label] pub qualifier: Span, + #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")] + pub suggestion: Span, } #[derive(Diagnostic)] @@ -2840,9 +2841,10 @@ pub(crate) struct FnPointerCannotBeConst { pub(crate) struct FnPointerCannotBeAsync { #[primary_span] pub span: Span, - #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")] #[label] pub qualifier: Span, + #[suggestion(code = "", applicability = "maybe-incorrect", style = "verbose")] + pub suggestion: Span, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 6497d19a173ca..dc5919b3630ce 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -609,16 +609,58 @@ impl<'a> Parser<'a> { let span_start = self.token.span; let ast::FnHeader { ext, safety, constness, coroutine_kind } = self.parse_fn_front_matter(&inherited_vis, Case::Sensitive)?; + let fn_start_lo = self.prev_token.span.lo(); if self.may_recover() && self.token == TokenKind::Lt { self.recover_fn_ptr_with_generics(lo, &mut params, param_insertion_point)?; } let decl = self.parse_fn_decl(|_| false, AllowPlus::No, recover_return_sign)?; let whole_span = lo.to(self.prev_token.span); - if let ast::Const::Yes(span) = constness { - self.dcx().emit_err(FnPointerCannotBeConst { span: whole_span, qualifier: span }); + + // Order/parsing of "front matter" follows: + // ` fn()` + // ^ ^ ^ ^ ^ + // | | | | fn_start_lo + // | | | ext_sp.lo + // | | safety_sp.lo + // | coroutine_sp.lo + // const_sp.lo + if let ast::Const::Yes(const_span) = constness { + let next_token_lo = if let Some( + ast::CoroutineKind::Async { span, .. } + | ast::CoroutineKind::Gen { span, .. } + | ast::CoroutineKind::AsyncGen { span, .. }, + ) = coroutine_kind + { + span.lo() + } else if let ast::Safety::Unsafe(span) | ast::Safety::Safe(span) = safety { + span.lo() + } else if let ast::Extern::Implicit(span) | ast::Extern::Explicit(_, span) = ext { + span.lo() + } else { + fn_start_lo + }; + let sugg_span = const_span.with_hi(next_token_lo); + self.dcx().emit_err(FnPointerCannotBeConst { + span: whole_span, + qualifier: const_span, + suggestion: sugg_span, + }); } - if let Some(ast::CoroutineKind::Async { span, .. }) = coroutine_kind { - self.dcx().emit_err(FnPointerCannotBeAsync { span: whole_span, qualifier: span }); + if let Some(ast::CoroutineKind::Async { span: async_span, .. }) = coroutine_kind { + let next_token_lo = if let ast::Safety::Unsafe(span) | ast::Safety::Safe(span) = safety + { + span.lo() + } else if let ast::Extern::Implicit(span) | ast::Extern::Explicit(_, span) = ext { + span.lo() + } else { + fn_start_lo + }; + let sugg_span = async_span.with_hi(next_token_lo); + self.dcx().emit_err(FnPointerCannotBeAsync { + span: whole_span, + qualifier: async_span, + suggestion: sugg_span, + }); } // FIXME(gen_blocks): emit a similar error for `gen fn()` let decl_span = span_start.to(self.prev_token.span); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 63aaa3abc8e56..4ce6382512978 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1870,8 +1870,6 @@ options! { "verify extended properties for incr. comp. (default: no): - hashes of green query instances - hash collisions of query keys"), - inline_in_all_cgus: Option = (None, parse_opt_bool, [TRACKED], - "control whether `#[inline]` functions are in all CGUs"), inline_llvm: bool = (true, parse_bool, [TRACKED], "enable LLVM inlining (default: yes)"), inline_mir: Option = (None, parse_opt_bool, [TRACKED], diff --git a/compiler/rustc_target/src/callconv/x86_win64.rs b/compiler/rustc_target/src/callconv/x86_win64.rs index 0944bda26875d..816564d2fed8b 100644 --- a/compiler/rustc_target/src/callconv/x86_win64.rs +++ b/compiler/rustc_target/src/callconv/x86_win64.rs @@ -1,4 +1,4 @@ -use rustc_abi::{BackendRepr, Float, Primitive}; +use rustc_abi::{BackendRepr, Float, Integer, Primitive, RegKind, Size}; use crate::abi::call::{ArgAbi, FnAbi, Reg}; use crate::spec::HasTargetSpec; @@ -6,7 +6,7 @@ use crate::spec::HasTargetSpec; // Win64 ABI: https://docs.microsoft.com/en-us/cpp/build/parameter-passing pub(crate) fn compute_abi_info(_cx: &impl HasTargetSpec, fn_abi: &mut FnAbi<'_, Ty>) { - let fixup = |a: &mut ArgAbi<'_, Ty>| { + let fixup = |a: &mut ArgAbi<'_, Ty>, is_ret: bool| { match a.layout.backend_repr { BackendRepr::Uninhabited | BackendRepr::Memory { sized: false } => {} BackendRepr::ScalarPair(..) | BackendRepr::Memory { sized: true } => { @@ -23,11 +23,16 @@ pub(crate) fn compute_abi_info(_cx: &impl HasTargetSpec, fn_abi: &mut FnAbi< // (probably what clang calls "illegal vectors"). } BackendRepr::Scalar(scalar) => { - // Match what LLVM does for `f128` so that `compiler-builtins` builtins match up - // with what LLVM expects. - if a.layout.size.bytes() > 8 + if is_ret && matches!(scalar.primitive(), Primitive::Int(Integer::I128, _)) { + // `i128` is returned in xmm0 by Clang and GCC + // FIXME(#134288): This may change for the `-msvc` targets in the future. + let reg = Reg { kind: RegKind::Vector, size: Size::from_bits(128) }; + a.cast_to(reg); + } else if a.layout.size.bytes() > 8 && !matches!(scalar.primitive(), Primitive::Float(Float::F128)) { + // Match what LLVM does for `f128` so that `compiler-builtins` builtins match up + // with what LLVM expects. a.make_indirect(); } else { a.extend_integer_width_to(32); @@ -37,8 +42,9 @@ pub(crate) fn compute_abi_info(_cx: &impl HasTargetSpec, fn_abi: &mut FnAbi< }; if !fn_abi.ret.is_ignore() { - fixup(&mut fn_abi.ret); + fixup(&mut fn_abi.ret, true); } + for arg in fn_abi.args.iter_mut() { if arg.is_ignore() && arg.layout.is_zst() { // Windows ABIs do not talk about ZST since such types do not exist in MSVC. @@ -49,7 +55,7 @@ pub(crate) fn compute_abi_info(_cx: &impl HasTargetSpec, fn_abi: &mut FnAbi< arg.make_indirect_from_ignore(); continue; } - fixup(arg); + fixup(arg, false); } // FIXME: We should likely also do something about ZST return types, similar to above. // However, that's non-trivial due to `()`. diff --git a/library/core/src/iter/sources/from_fn.rs b/library/core/src/iter/sources/from_fn.rs index 5f3d404d7dca2..75cc0ffe3c77c 100644 --- a/library/core/src/iter/sources/from_fn.rs +++ b/library/core/src/iter/sources/from_fn.rs @@ -1,7 +1,7 @@ use crate::fmt; -/// Creates a new iterator where each iteration calls the provided closure -/// `F: FnMut() -> Option`. +/// Creates an iterator with the provided closure +/// `F: FnMut() -> Option` as its `[next](Iterator::next)` method. /// /// The iterator will yield the `T`s returned from the closure. /// diff --git a/library/std/src/f128.rs b/library/std/src/f128.rs index 4f37e18a8cd76..54a992c9cf4b5 100644 --- a/library/std/src/f128.rs +++ b/library/std/src/f128.rs @@ -324,6 +324,18 @@ impl f128 { /// /// The precision of this function is non-deterministic. This means it varies by platform, /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f128)] + /// + /// let x = 2.0_f128; + /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f128::EPSILON); + /// + /// assert_eq!(f128::powi(f128::NAN, 0), 1.0); + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f128", issue = "116909")] @@ -347,8 +359,10 @@ impl f128 { /// /// let x = 2.0_f128; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// /// assert!(abs_difference <= f128::EPSILON); + /// + /// assert_eq!(f128::powf(1.0, f128::NAN), 1.0); + /// assert_eq!(f128::powf(f128::NAN, 0.0), 1.0); /// # } /// ``` #[inline] diff --git a/library/std/src/f16.rs b/library/std/src/f16.rs index 42cd6e3fe2a5f..e354f2dd98217 100644 --- a/library/std/src/f16.rs +++ b/library/std/src/f16.rs @@ -324,6 +324,18 @@ impl f16 { /// /// The precision of this function is non-deterministic. This means it varies by platform, /// Rust version, and can even differ within the same execution from one invocation to the next. + /// + /// # Examples + /// + /// ``` + /// #![feature(f16)] + /// + /// let x = 2.0_f16; + /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f16::EPSILON); + /// + /// assert_eq!(f16::powi(f16::NAN, 0), 1.0); + /// ``` #[inline] #[rustc_allow_incoherent_impl] #[unstable(feature = "f16", issue = "116909")] @@ -347,8 +359,10 @@ impl f16 { /// /// let x = 2.0_f16; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// /// assert!(abs_difference <= f16::EPSILON); + /// + /// assert_eq!(f16::powf(1.0, f16::NAN), 1.0); + /// assert_eq!(f16::powf(f16::NAN, 0.0), 1.0); /// # } /// ``` #[inline] diff --git a/library/std/src/f32.rs b/library/std/src/f32.rs index 438d77b1626be..f9b6723788ae3 100644 --- a/library/std/src/f32.rs +++ b/library/std/src/f32.rs @@ -306,8 +306,9 @@ impl f32 { /// ``` /// let x = 2.0_f32; /// let abs_difference = (x.powi(2) - (x * x)).abs(); - /// /// assert!(abs_difference <= f32::EPSILON); + /// + /// assert_eq!(f32::powi(f32::NAN, 0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -329,8 +330,10 @@ impl f32 { /// ``` /// let x = 2.0_f32; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); - /// /// assert!(abs_difference <= f32::EPSILON); + /// + /// assert_eq!(f32::powf(1.0, f32::NAN), 1.0); + /// assert_eq!(f32::powf(f32::NAN, 0.0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] diff --git a/library/std/src/f64.rs b/library/std/src/f64.rs index 9bb4bfbab2a0f..0de55a15d48e8 100644 --- a/library/std/src/f64.rs +++ b/library/std/src/f64.rs @@ -306,8 +306,9 @@ impl f64 { /// ``` /// let x = 2.0_f64; /// let abs_difference = (x.powi(2) - (x * x)).abs(); + /// assert!(abs_difference <= f64::EPSILON); /// - /// assert!(abs_difference < 1e-10); + /// assert_eq!(f64::powi(f64::NAN, 0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] @@ -329,8 +330,10 @@ impl f64 { /// ``` /// let x = 2.0_f64; /// let abs_difference = (x.powf(2.0) - (x * x)).abs(); + /// assert!(abs_difference <= f64::EPSILON); /// - /// assert!(abs_difference < 1e-10); + /// assert_eq!(f64::powf(1.0, f64::NAN), 1.0); + /// assert_eq!(f64::powf(f64::NAN, 0.0), 1.0); /// ``` #[rustc_allow_incoherent_impl] #[must_use = "method returns a new number and does not mutate the original value"] diff --git a/src/doc/book b/src/doc/book index 82a4a49789bc9..fa312a343fbff 160000 --- a/src/doc/book +++ b/src/doc/book @@ -1 +1 @@ -Subproject commit 82a4a49789bc96db1a1b2a210b4c5ed7c9ef0c0d +Subproject commit fa312a343fbff01bc6cef393e326817f70719813 diff --git a/src/doc/edition-guide b/src/doc/edition-guide index d56e0f3a0656b..4ed5a1a4a2a7e 160000 --- a/src/doc/edition-guide +++ b/src/doc/edition-guide @@ -1 +1 @@ -Subproject commit d56e0f3a0656b7702ca466d4b191e16c28262b82 +Subproject commit 4ed5a1a4a2a7ecc2e529a5baaef04f7bc7917eda diff --git a/src/doc/nomicon b/src/doc/nomicon index 625b200e5b33a..bc22988655446 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 625b200e5b33a5af35589db0bc454203a3d46d20 +Subproject commit bc2298865544695c63454fc1f9f98a3dc22e9948 diff --git a/src/doc/reference b/src/doc/reference index 293af99100377..93b921c7d3213 160000 --- a/src/doc/reference +++ b/src/doc/reference @@ -1 +1 @@ -Subproject commit 293af991003772bdccf2d6b980182d84dd055942 +Subproject commit 93b921c7d3213d38d920f7f905a3bec093d2217d diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index f92668a6a9786..2dfca7c4803e2 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -846,7 +846,6 @@ dependencies = [ "dashmap", "hashbrown", "rustc-hash 2.0.0", - "sptr", "triomphe", ] @@ -1927,12 +1926,6 @@ dependencies = [ "vfs", ] -[[package]] -name = "sptr" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" - [[package]] name = "stdx" version = "0.0.0" diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index 1029844cd3ab4..c42ae171d8668 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -4,7 +4,7 @@ exclude = ["crates/proc-macro-srv/proc-macro-test/imp"] resolver = "2" [workspace.package] -rust-version = "1.83" +rust-version = "1.84" edition = "2021" license = "MIT OR Apache-2.0" authors = ["rust-analyzer team"] diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index 1327bb3ab59c0..16c7b5ca00a07 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -1381,6 +1381,9 @@ impl ExprCollector<'_> { } } ast::Stmt::Item(ast::Item::MacroDef(macro_)) => { + if self.check_cfg(¯o_).is_none() { + return; + } let Some(name) = macro_.name() else { statements.push(Statement::Item(Item::Other)); return; @@ -1390,6 +1393,9 @@ impl ExprCollector<'_> { self.collect_macro_def(statements, macro_id); } ast::Stmt::Item(ast::Item::MacroRules(macro_)) => { + if self.check_cfg(¯o_).is_none() { + return; + } let Some(name) = macro_.name() else { statements.push(Statement::Item(Item::Other)); return; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs index f483efa85179b..e136dd18a55e5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/tests/block.rs @@ -475,7 +475,7 @@ fn outer() { block scope::tests name: _ - outer: v + outer: vg crate outer: v diff --git a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs index 5d67902c8ac12..c30ad0163b9db 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/find_path.rs @@ -445,6 +445,10 @@ fn find_in_dep( }; cov_mark::hit!(partially_imported); if info.is_unstable { + if !ctx.cfg.allow_unstable { + // the item is unstable and we are not allowed to use unstable items + continue; + } choice.stability = Unstable; } @@ -670,6 +674,7 @@ mod tests { prefer_prelude: bool, prefer_absolute: bool, prefer_no_std: bool, + allow_unstable: bool, expect: Expect, ) { let (db, pos) = TestDB::with_position(ra_fixture); @@ -711,7 +716,7 @@ mod tests { module, prefix, ignore_local_imports, - ImportPathConfig { prefer_no_std, prefer_prelude, prefer_absolute }, + ImportPathConfig { prefer_no_std, prefer_prelude, prefer_absolute, allow_unstable }, ); format_to!( res, @@ -732,7 +737,7 @@ mod tests { path: &str, expect: Expect, ) { - check_found_path_(ra_fixture, path, false, false, false, expect); + check_found_path_(ra_fixture, path, false, false, false, false, expect); } fn check_found_path_prelude( @@ -740,7 +745,7 @@ mod tests { path: &str, expect: Expect, ) { - check_found_path_(ra_fixture, path, true, false, false, expect); + check_found_path_(ra_fixture, path, true, false, false, false, expect); } fn check_found_path_absolute( @@ -748,7 +753,7 @@ mod tests { path: &str, expect: Expect, ) { - check_found_path_(ra_fixture, path, false, true, false, expect); + check_found_path_(ra_fixture, path, false, true, false, false, expect); } fn check_found_path_prefer_no_std( @@ -756,7 +761,15 @@ mod tests { path: &str, expect: Expect, ) { - check_found_path_(ra_fixture, path, false, false, true, expect); + check_found_path_(ra_fixture, path, false, false, true, false, expect); + } + + fn check_found_path_prefer_no_std_allow_unstable( + #[rust_analyzer::rust_fixture] ra_fixture: &str, + path: &str, + expect: Expect, + ) { + check_found_path_(ra_fixture, path, false, false, true, true, expect); } #[test] @@ -1951,7 +1964,7 @@ pub mod ops { #[test] fn respect_unstable_modules() { - check_found_path_prefer_no_std( + check_found_path_prefer_no_std_allow_unstable( r#" //- /main.rs crate:main deps:std,core extern crate std; diff --git a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs index ac262950f13c6..34635997bdff3 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/import_map.rs @@ -10,7 +10,6 @@ use rustc_hash::FxHashSet; use smallvec::SmallVec; use span::Edition; use stdx::{format_to, TupleExt}; -use syntax::ToSmolStr; use triomphe::Arc; use crate::{ @@ -88,9 +87,9 @@ impl ImportMap { .iter() // We've only collected items, whose name cannot be tuple field so unwrapping is fine. .flat_map(|(&item, (info, _))| { - info.iter().enumerate().map(move |(idx, info)| { - (item, info.name.unescaped().display(db.upcast()).to_smolstr(), idx as u32) - }) + info.iter() + .enumerate() + .map(move |(idx, info)| (item, info.name.as_str(), idx as u32)) }) .collect(); importables.sort_by(|(_, l_info, _), (_, r_info, _)| { @@ -168,7 +167,8 @@ impl ImportMap { let attr_id = if let Some(import) = import { match import { ImportOrExternCrate::ExternCrate(id) => Some(id.into()), - ImportOrExternCrate::Import(id) => Some(id.import.into()), + ImportOrExternCrate::Import(id) => Some(id.use_.into()), + ImportOrExternCrate::Glob(id) => Some(id.use_.into()), } } else { match item { @@ -441,7 +441,7 @@ pub fn search_dependencies( } fn search_maps( - db: &dyn DefDatabase, + _db: &dyn DefDatabase, import_maps: &[Arc], mut stream: fst::map::Union<'_>, query: &Query, @@ -464,11 +464,7 @@ fn search_maps( .then(|| (item, &import_infos[info_idx as usize])) }) .filter(|&(_, info)| { - query.search_mode.check( - &query.query, - query.case_sensitive, - &info.name.unescaped().display(db.upcast()).to_smolstr(), - ) + query.search_mode.check(&query.query, query.case_sensitive, info.name.as_str()) }); res.extend(iter.map(TupleExt::head)); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs index 0fec7674109bc..65a39c565611b 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/item_scope.rs @@ -31,21 +31,62 @@ pub struct PerNsGlobImports { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ImportOrExternCrate { + Glob(GlobId), Import(ImportId), ExternCrate(ExternCrateId), } +impl From for ImportOrExternCrate { + fn from(value: ImportOrGlob) -> Self { + match value { + ImportOrGlob::Glob(it) => ImportOrExternCrate::Glob(it), + ImportOrGlob::Import(it) => ImportOrExternCrate::Import(it), + } + } +} + +impl ImportOrExternCrate { + pub fn import_or_glob(self) -> Option { + match self { + ImportOrExternCrate::Import(it) => Some(ImportOrGlob::Import(it)), + ImportOrExternCrate::Glob(it) => Some(ImportOrGlob::Glob(it)), + _ => None, + } + } + + pub fn import(self) -> Option { + match self { + ImportOrExternCrate::Import(it) => Some(it), + _ => None, + } + } + + pub fn glob(self) -> Option { + match self { + ImportOrExternCrate::Glob(id) => Some(id), + _ => None, + } + } + + pub fn use_(self) -> Option { + match self { + ImportOrExternCrate::Glob(id) => Some(id.use_), + ImportOrExternCrate::Import(id) => Some(id.use_), + _ => None, + } + } +} + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] -pub(crate) enum ImportType { +pub enum ImportOrGlob { + Glob(GlobId), Import(ImportId), - Glob(UseId), - ExternCrate(ExternCrateId), } -impl ImportOrExternCrate { +impl ImportOrGlob { pub fn into_import(self) -> Option { match self { - ImportOrExternCrate::Import(it) => Some(it), + ImportOrGlob::Import(it) => Some(it), _ => None, } } @@ -54,12 +95,39 @@ impl ImportOrExternCrate { #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum ImportOrDef { Import(ImportId), + Glob(GlobId), ExternCrate(ExternCrateId), Def(ModuleDefId), } + +impl From for ImportOrDef { + fn from(value: ImportOrExternCrate) -> Self { + match value { + ImportOrExternCrate::Import(it) => ImportOrDef::Import(it), + ImportOrExternCrate::Glob(it) => ImportOrDef::Glob(it), + ImportOrExternCrate::ExternCrate(it) => ImportOrDef::ExternCrate(it), + } + } +} + +impl From for ImportOrDef { + fn from(value: ImportOrGlob) -> Self { + match value { + ImportOrGlob::Import(it) => ImportOrDef::Import(it), + ImportOrGlob::Glob(it) => ImportOrDef::Glob(it), + } + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] pub struct ImportId { - pub import: UseId, + pub use_: UseId, + pub idx: Idx, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] +pub struct GlobId { + pub use_: UseId, pub idx: Idx, } @@ -96,8 +164,8 @@ pub struct ItemScope { // the resolutions of the imports of this scope use_imports_types: FxHashMap, - use_imports_values: FxHashMap, - use_imports_macros: FxHashMap, + use_imports_values: FxHashMap, + use_imports_macros: FxHashMap, use_decls: Vec, extern_crate_decls: Vec, @@ -162,7 +230,7 @@ impl ItemScope { .map(move |name| (name, self.get(name))) } - pub fn values(&self) -> impl Iterator)> + '_ { + pub fn values(&self) -> impl Iterator)> + '_ { self.values.iter().map(|(n, &i)| (n, i)) } @@ -172,7 +240,7 @@ impl ItemScope { self.types.iter().map(|(n, &i)| (n, i)) } - pub fn macros(&self) -> impl Iterator)> + '_ { + pub fn macros(&self) -> impl Iterator)> + '_ { self.macros.iter().map(|(n, &i)| (n, i)) } @@ -180,9 +248,10 @@ impl ItemScope { self.use_imports_types .keys() .copied() - .filter_map(ImportOrExternCrate::into_import) + .filter_map(ImportOrExternCrate::import_or_glob) .chain(self.use_imports_values.keys().copied()) .chain(self.use_imports_macros.keys().copied()) + .filter_map(ImportOrGlob::into_import) .sorted() .dedup() } @@ -192,10 +261,10 @@ impl ItemScope { let mut def_map; let mut scope = self; - while let Some(&m) = scope.use_imports_macros.get(&import) { + while let Some(&m) = scope.use_imports_macros.get(&ImportOrGlob::Import(import)) { match m { ImportOrDef::Import(i) => { - let module_id = i.import.lookup(db).container; + let module_id = i.use_.lookup(db).container; def_map = module_id.def_map(db); scope = &def_map[module_id.local_id].scope; import = i; @@ -211,7 +280,7 @@ impl ItemScope { while let Some(&m) = scope.use_imports_types.get(&ImportOrExternCrate::Import(import)) { match m { ImportOrDef::Import(i) => { - let module_id = i.import.lookup(db).container; + let module_id = i.use_.lookup(db).container; def_map = module_id.def_map(db); scope = &def_map[module_id.local_id].scope; import = i; @@ -224,10 +293,10 @@ impl ItemScope { } } let mut scope = self; - while let Some(&m) = scope.use_imports_values.get(&import) { + while let Some(&m) = scope.use_imports_values.get(&ImportOrGlob::Import(import)) { match m { ImportOrDef::Import(i) => { - let module_id = i.import.lookup(db).container; + let module_id = i.use_.lookup(db).container; def_map = module_id.def_map(db); scope = &def_map[module_id.local_id].scope; import = i; @@ -488,9 +557,13 @@ impl ItemScope { self.unnamed_trait_imports.get(&tr).map(|trait_| trait_.vis) } - pub(crate) fn push_unnamed_trait(&mut self, tr: TraitId, vis: Visibility) { - // FIXME: import - self.unnamed_trait_imports.insert(tr, Item { def: (), vis, import: None }); + pub(crate) fn push_unnamed_trait( + &mut self, + tr: TraitId, + vis: Visibility, + import: Option, + ) { + self.unnamed_trait_imports.insert(tr, Item { def: (), vis, import }); } pub(crate) fn push_res_with_import( @@ -498,7 +571,7 @@ impl ItemScope { glob_imports: &mut PerNsGlobImports, lookup: (LocalModuleId, Name), def: PerNs, - import: Option, + import: Option, ) -> bool { let mut changed = false; @@ -509,41 +582,22 @@ impl ItemScope { match existing { Entry::Vacant(entry) => { match import { - Some(ImportType::Glob(_)) => { + Some(ImportOrExternCrate::Glob(_)) => { glob_imports.types.insert(lookup.clone()); } _ => _ = glob_imports.types.remove(&lookup), } - let import = match import { - Some(ImportType::ExternCrate(extern_crate)) => { - Some(ImportOrExternCrate::ExternCrate(extern_crate)) - } - Some(ImportType::Import(import)) => { - Some(ImportOrExternCrate::Import(import)) - } - None | Some(ImportType::Glob(_)) => None, - }; let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { - self.use_imports_types.insert( - import, - match prev { - Some(ImportOrExternCrate::Import(import)) => { - ImportOrDef::Import(import) - } - Some(ImportOrExternCrate::ExternCrate(import)) => { - ImportOrDef::ExternCrate(import) - } - None => ImportOrDef::Def(fld.def), - }, - ); + self.use_imports_types + .insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into)); } entry.insert(fld); changed = true; } Entry::Occupied(mut entry) => { match import { - Some(ImportType::Glob(..)) => { + Some(ImportOrExternCrate::Glob(..)) => { // Multiple globs may import the same item and they may // override visibility from previously resolved globs. This is // currently handled by `DefCollector`, because we need to @@ -552,28 +606,11 @@ impl ItemScope { } _ => { if glob_imports.types.remove(&lookup) { - let import = match import { - Some(ImportType::ExternCrate(extern_crate)) => { - Some(ImportOrExternCrate::ExternCrate(extern_crate)) - } - Some(ImportType::Import(import)) => { - Some(ImportOrExternCrate::Import(import)) - } - None | Some(ImportType::Glob(_)) => None, - }; let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_types.insert( import, - match prev { - Some(ImportOrExternCrate::Import(import)) => { - ImportOrDef::Import(import) - } - Some(ImportOrExternCrate::ExternCrate(import)) => { - ImportOrDef::ExternCrate(import) - } - None => ImportOrDef::Def(fld.def), - }, + prev.map_or(ImportOrDef::Def(fld.def), Into::into), ); } cov_mark::hit!(import_shadowed); @@ -591,44 +628,31 @@ impl ItemScope { match existing { Entry::Vacant(entry) => { match import { - Some(ImportType::Glob(_)) => { + Some(ImportOrExternCrate::Glob(_)) => { glob_imports.values.insert(lookup.clone()); } _ => _ = glob_imports.values.remove(&lookup), } - let import = match import { - Some(ImportType::Import(import)) => Some(import), - _ => None, - }; + let import = import.and_then(ImportOrExternCrate::import_or_glob); let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { - self.use_imports_values.insert( - import, - match prev { - Some(import) => ImportOrDef::Import(import), - None => ImportOrDef::Def(fld.def), - }, - ); + self.use_imports_values + .insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into)); } entry.insert(fld); changed = true; } - Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => { + Entry::Occupied(mut entry) + if !matches!(import, Some(ImportOrExternCrate::Glob(..))) => + { if glob_imports.values.remove(&lookup) { cov_mark::hit!(import_shadowed); - let import = match import { - Some(ImportType::Import(import)) => Some(import), - _ => None, - }; + + let import = import.and_then(ImportOrExternCrate::import_or_glob); let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { - self.use_imports_values.insert( - import, - match prev { - Some(import) => ImportOrDef::Import(import), - None => ImportOrDef::Def(fld.def), - }, - ); + self.use_imports_values + .insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into)); } entry.insert(fld); changed = true; @@ -643,43 +667,33 @@ impl ItemScope { match existing { Entry::Vacant(entry) => { match import { - Some(ImportType::Glob(_)) => { + Some(ImportOrExternCrate::Glob(_)) => { glob_imports.macros.insert(lookup.clone()); } _ => _ = glob_imports.macros.remove(&lookup), } - let import = match import { - Some(ImportType::Import(import)) => Some(import), - _ => None, - }; + let import = import.and_then(ImportOrExternCrate::import_or_glob); let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_macros.insert( import, - match prev { - Some(import) => ImportOrDef::Import(import), - None => ImportOrDef::Def(fld.def.into()), - }, + prev.map_or_else(|| ImportOrDef::Def(fld.def.into()), Into::into), ); } entry.insert(fld); changed = true; } - Entry::Occupied(mut entry) if !matches!(import, Some(ImportType::Glob(..))) => { + Entry::Occupied(mut entry) + if !matches!(import, Some(ImportOrExternCrate::Glob(..))) => + { if glob_imports.macros.remove(&lookup) { cov_mark::hit!(import_shadowed); - let import = match import { - Some(ImportType::Import(import)) => Some(import), - _ => None, - }; + let import = import.and_then(ImportOrExternCrate::import_or_glob); let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_macros.insert( import, - match prev { - Some(import) => ImportOrDef::Import(import), - None => ImportOrDef::Def(fld.def.into()), - }, + prev.map_or_else(|| ImportOrDef::Def(fld.def.into()), Into::into), ); } entry.insert(fld); @@ -704,16 +718,27 @@ impl ItemScope { .map(|def| &mut def.vis) .chain(self.values.values_mut().map(|def| &mut def.vis)) .chain(self.unnamed_trait_imports.values_mut().map(|def| &mut def.vis)) - .for_each(|vis| { - *vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit) + .for_each(|vis| match vis { + &mut Visibility::Module(_, visibility_explicitness) => { + *vis = Visibility::Module(this_module, visibility_explicitness) + } + Visibility::Public => { + *vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit) + } }); for mac in self.macros.values_mut() { if matches!(mac.def, MacroId::ProcMacroId(_) if mac.import.is_none()) { continue; } - - mac.vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit); + match mac.vis { + Visibility::Module(_, visibility_explicitness) => { + mac.vis = Visibility::Module(this_module, visibility_explicitness) + } + Visibility::Public => { + mac.vis = Visibility::Module(this_module, VisibilityExplicitness::Implicit) + } + } } } @@ -732,20 +757,25 @@ impl ItemScope { buf.push_str(" t"); match import { Some(ImportOrExternCrate::Import(_)) => buf.push('i'), + Some(ImportOrExternCrate::Glob(_)) => buf.push('g'), Some(ImportOrExternCrate::ExternCrate(_)) => buf.push('e'), None => (), } } if let Some(Item { import, .. }) = def.values { buf.push_str(" v"); - if import.is_some() { - buf.push('i'); + match import { + Some(ImportOrGlob::Import(_)) => buf.push('i'), + Some(ImportOrGlob::Glob(_)) => buf.push('g'), + None => (), } } if let Some(Item { import, .. }) = def.macros { buf.push_str(" m"); - if import.is_some() { - buf.push('i'); + match import { + Some(ImportOrGlob::Import(_)) => buf.push('i'), + Some(ImportOrGlob::Glob(_)) => buf.push('g'), + None => (), } } if def.is_none() { @@ -828,7 +858,7 @@ impl PerNs { match def { ModuleDefId::ModuleId(_) => PerNs::types(def, v, import), ModuleDefId::FunctionId(_) => { - PerNs::values(def, v, import.and_then(ImportOrExternCrate::into_import)) + PerNs::values(def, v, import.and_then(ImportOrExternCrate::import_or_glob)) } ModuleDefId::AdtId(adt) => match adt { AdtId::UnionId(_) => PerNs::types(def, v, import), @@ -843,14 +873,14 @@ impl PerNs { }, ModuleDefId::EnumVariantId(_) => PerNs::both(def, def, v, import), ModuleDefId::ConstId(_) | ModuleDefId::StaticId(_) => { - PerNs::values(def, v, import.and_then(ImportOrExternCrate::into_import)) + PerNs::values(def, v, import.and_then(ImportOrExternCrate::import_or_glob)) } ModuleDefId::TraitId(_) => PerNs::types(def, v, import), ModuleDefId::TraitAliasId(_) => PerNs::types(def, v, import), ModuleDefId::TypeAliasId(_) => PerNs::types(def, v, import), ModuleDefId::BuiltinType(_) => PerNs::types(def, v, import), ModuleDefId::MacroId(mac) => { - PerNs::macros(mac, v, import.and_then(ImportOrExternCrate::into_import)) + PerNs::macros(mac, v, import.and_then(ImportOrExternCrate::import_or_glob)) } } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index afdc49a2dc59a..e83ce6dc42cee 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -372,6 +372,7 @@ language_item_table! { DerefMut, sym::deref_mut, deref_mut_trait, Target::Trait, GenericRequirement::Exact(0); DerefTarget, sym::deref_target, deref_target, Target::AssocTy, GenericRequirement::None; Receiver, sym::receiver, receiver_trait, Target::Trait, GenericRequirement::None; + ReceiverTarget, sym::receiver_target, receiver_target, Target::AssocTy, GenericRequirement::None; Fn, sym::fn_, fn_trait, Target::Trait, GenericRequirement::Exact(1); FnMut, sym::fn_mut, fn_mut_trait, Target::Trait, GenericRequirement::Exact(1); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 84c105a0a3467..c78818c642ceb 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -114,6 +114,9 @@ pub struct ImportPathConfig { pub prefer_prelude: bool, /// If true, prefer abs path (starting with `::`) where it is available. pub prefer_absolute: bool, + /// If true, paths containing `#[unstable]` segments may be returned, but only if if there is no + /// stable path. This does not check, whether the item itself that is being imported is `#[unstable]`. + pub allow_unstable: bool, } #[derive(Debug)] @@ -910,6 +913,7 @@ pub enum AssocItemId { ConstId(ConstId), TypeAliasId(TypeAliasId), } + // FIXME: not every function, ... is actually an assoc item. maybe we should make // sure that you can only turn actual assoc items into AssocItemIds. This would // require not implementing From, and instead having some checked way of diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 1e4b42dff5fb7..06276335b7188 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -28,7 +28,7 @@ use triomphe::Arc; use crate::{ attr::Attrs, db::DefDatabase, - item_scope::{ImportId, ImportOrExternCrate, ImportType, PerNsGlobImports}, + item_scope::{GlobId, ImportId, ImportOrExternCrate, PerNsGlobImports}, item_tree::{ self, AttrOwner, FieldsShape, FileItemTreeId, ImportKind, ItemTree, ItemTreeId, ItemTreeNode, Macro2, MacroCall, MacroRules, Mod, ModItem, ModKind, TreeId, UseTreeKind, @@ -208,7 +208,7 @@ struct DefCollector<'a> { def_map: DefMap, // The dependencies of the current crate, including optional deps like `test`. deps: FxHashMap, - glob_imports: FxHashMap>, + glob_imports: FxHashMap>, unresolved_imports: Vec, indeterminate_imports: Vec<(ImportDirective, PerNs)>, unresolved_macros: Vec, @@ -524,11 +524,7 @@ impl DefCollector<'_> { match per_ns.types { Some(Item { def: ModuleDefId::ModuleId(m), import, .. }) => { - // FIXME: This should specifically look for a glob import somehow and record that here - self.def_map.prelude = Some(( - m, - import.and_then(ImportOrExternCrate::into_import).map(|it| it.import), - )); + self.def_map.prelude = Some((m, import.and_then(ImportOrExternCrate::use_))); } types => { tracing::debug!( @@ -845,13 +841,14 @@ impl DefCollector<'_> { def.values = None; def.macros = None; } - let imp = ImportType::Import(ImportId { import: id, idx: use_tree }); + let imp = ImportOrExternCrate::Import(ImportId { use_: id, idx: use_tree }); tracing::debug!("resolved import {:?} ({:?}) to {:?}", name, import, def); self.update(module_id, &[(name.cloned(), def)], vis, Some(imp)); } - ImportSource { kind: ImportKind::Glob, id, is_prelude, .. } => { + ImportSource { kind: ImportKind::Glob, id, is_prelude, use_tree } => { tracing::debug!("glob import: {:?}", import); + let glob = GlobId { use_: id, idx: use_tree }; match def.take_types() { Some(ModuleDefId::ModuleId(m)) => { if is_prelude { @@ -875,7 +872,12 @@ impl DefCollector<'_> { .filter(|(_, res)| !res.is_none()) .collect::>(); - self.update(module_id, &items, vis, Some(ImportType::Glob(id))); + self.update( + module_id, + &items, + vis, + Some(ImportOrExternCrate::Glob(glob)), + ); } else { // glob import from same crate => we do an initial // import, and then need to propagate any further @@ -907,11 +909,16 @@ impl DefCollector<'_> { .filter(|(_, res)| !res.is_none()) .collect::>(); - self.update(module_id, &items, vis, Some(ImportType::Glob(id))); + self.update( + module_id, + &items, + vis, + Some(ImportOrExternCrate::Glob(glob)), + ); // record the glob import in case we add further items - let glob = self.glob_imports.entry(m.local_id).or_default(); - match glob.iter_mut().find(|(mid, _, _)| *mid == module_id) { - None => glob.push((module_id, vis, id)), + let glob_imports = self.glob_imports.entry(m.local_id).or_default(); + match glob_imports.iter_mut().find(|(mid, _, _)| *mid == module_id) { + None => glob_imports.push((module_id, vis, glob)), Some((_, old_vis, _)) => { if let Some(new_vis) = old_vis.max(vis, &self.def_map) { *old_vis = new_vis; @@ -944,7 +951,12 @@ impl DefCollector<'_> { (Some(name), res) }) .collect::>(); - self.update(module_id, &resolutions, vis, Some(ImportType::Glob(id))); + self.update( + module_id, + &resolutions, + vis, + Some(ImportOrExternCrate::Glob(glob)), + ); } Some(d) => { tracing::debug!("glob import {:?} from non-module/enum {:?}", import, d); @@ -964,7 +976,7 @@ impl DefCollector<'_> { resolutions: &[(Option, PerNs)], // Visibility this import will have vis: Visibility, - import: Option, + import: Option, ) { self.db.unwind_if_cancelled(); self.update_recursive(module_id, resolutions, vis, import, 0) @@ -978,7 +990,7 @@ impl DefCollector<'_> { // All resolutions are imported with this visibility; the visibilities in // the `PerNs` values are ignored and overwritten vis: Visibility, - import: Option, + import: Option, depth: usize, ) { if GLOB_RECURSION_LIMIT.check(depth).is_err() { @@ -994,8 +1006,10 @@ impl DefCollector<'_> { self.push_res_and_update_glob_vis(module_id, name, *res, vis, import); } None => { - let tr = match res.take_types() { - Some(ModuleDefId::TraitId(tr)) => tr, + let (tr, import) = match res.take_types_full() { + Some(Item { def: ModuleDefId::TraitId(tr), vis: _, import }) => { + (tr, import) + } Some(other) => { tracing::debug!("non-trait `_` import of {:?}", other); continue; @@ -1021,7 +1035,11 @@ impl DefCollector<'_> { if should_update { changed = true; - self.def_map.modules[module_id].scope.push_unnamed_trait(tr, vis); + self.def_map.modules[module_id].scope.push_unnamed_trait( + tr, + vis, + import.and_then(ImportOrExternCrate::import), + ); } } } @@ -1043,13 +1061,13 @@ impl DefCollector<'_> { .cloned() .collect::>(); - for (glob_importing_module, glob_import_vis, use_) in glob_imports { + for (glob_importing_module, glob_import_vis, glob) in glob_imports { let vis = glob_import_vis.min(vis, &self.def_map).unwrap_or(glob_import_vis); self.update_recursive( glob_importing_module, resolutions, vis, - Some(ImportType::Glob(use_)), + Some(ImportOrExternCrate::Glob(glob)), depth + 1, ); } @@ -1061,7 +1079,7 @@ impl DefCollector<'_> { name: &Name, mut defs: PerNs, vis: Visibility, - def_import_type: Option, + def_import_type: Option, ) -> bool { // `extern crate crate_name` things can be re-exported as `pub use crate_name`. // But they cannot be re-exported as `pub use self::crate_name`, `pub use crate::crate_name` @@ -1074,10 +1092,10 @@ impl DefCollector<'_> { let Some(ImportOrExternCrate::ExternCrate(_)) = def.import else { return false; }; - let Some(ImportType::Import(id)) = def_import_type else { + let Some(ImportOrExternCrate::Import(id)) = def_import_type else { return false; }; - let use_id = id.import.lookup(self.db).id; + let use_id = id.use_.lookup(self.db).id; let item_tree = use_id.item_tree(self.db); let use_kind = item_tree[use_id.value].use_tree.kind(); let UseTreeKind::Single { path, .. } = use_kind else { @@ -1100,7 +1118,7 @@ impl DefCollector<'_> { let mut changed = false; - if let Some(ImportType::Glob(_)) = def_import_type { + if let Some(ImportOrExternCrate::Glob(_)) = def_import_type { let prev_defs = self.def_map[module_id].scope.get(name); // Multiple globs may import the same item and they may override visibility from @@ -1727,7 +1745,7 @@ impl ModCollector<'_, '_> { ), )], vis, - Some(ImportType::ExternCrate(id)), + Some(ImportOrExternCrate::ExternCrate(id)), ); } else { if let Some(name) = name { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs index ab4ffbb2c1e4a..d7e4ca41cd5d5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/mod_resolution.rs @@ -4,7 +4,6 @@ use base_db::AnchoredPath; use hir_expand::{name::Name, HirFileIdExt}; use limit::Limit; use span::EditionedFileId; -use syntax::ToSmolStr as _; use crate::{db::DefDatabase, HirFileId}; @@ -35,7 +34,7 @@ impl ModDir { let path = match attr_path { None => { let mut path = self.dir_path.clone(); - path.push(&name.unescaped().display_no_db().to_smolstr()); + path.push(name.as_str()); path } Some(attr_path) => { @@ -66,7 +65,7 @@ impl ModDir { name: &Name, attr_path: Option<&str>, ) -> Result<(EditionedFileId, bool, ModDir), Box<[String]>> { - let name = name.unescaped(); + let name = name.as_str(); let mut candidate_files = ArrayVec::<_, 2>::new(); match attr_path { @@ -74,16 +73,8 @@ impl ModDir { candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner)) } None => { - candidate_files.push(format!( - "{}{}.rs", - self.dir_path.0, - name.display(db.upcast()) - )); - candidate_files.push(format!( - "{}{}/mod.rs", - self.dir_path.0, - name.display(db.upcast()) - )); + candidate_files.push(format!("{}{}.rs", self.dir_path.0, name)); + candidate_files.push(format!("{}{}/mod.rs", self.dir_path.0, name)); } }; @@ -97,7 +88,7 @@ impl ModDir { let dir_path = if root_dir_owner { DirPath::empty() } else { - DirPath::new(format!("{}/", name.display(db.upcast()))) + DirPath::new(format!("{}/", name)) }; if let Some(mod_dir) = self.child(dir_path, !root_dir_owner) { return Ok(( diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs index 318aee04f7b7f..73fc6787bfe81 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs @@ -103,8 +103,8 @@ mod a { c: t crate::a::b::c - A: v - b: t + A: vg + b: tg "#]], ); } @@ -256,8 +256,8 @@ pub enum Foo { Bar, Baz } "#, expect![[r#" crate - Bar: t v - Baz: t v + Bar: tg vg + Baz: tg vg "#]], ); } @@ -421,10 +421,10 @@ pub struct NotExported; "#, expect![[r#" crate - Exported: t v - PublicItem: t v - allowed_reexport: t - exported: t + Exported: tg vg + PublicItem: tg vg + allowed_reexport: tg + exported: tg not_allowed_reexport1: _ not_allowed_reexport2: _ "#]], @@ -692,7 +692,7 @@ mod b { b: t crate::a - T: t v + T: t vg crate::b T: v diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs index 8963a5767942e..ddb9d4a134d33 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/globs.rs @@ -18,9 +18,9 @@ pub struct Baz; "#, expect![[r#" crate - Baz: t v - Foo: t v - bar: t + Baz: tg vg + Foo: tg vg + bar: tg foo: t crate::foo @@ -53,20 +53,20 @@ pub use super::*; "#, expect![[r#" crate - Baz: t v - Foo: t v - bar: t + Baz: tg vg + Foo: tg vg + bar: tg foo: t crate::foo - Baz: t v + Baz: tg vg Foo: t v bar: t crate::foo::bar Baz: t v - Foo: t v - bar: t + Foo: tg vg + bar: tg "#]], ); } @@ -91,20 +91,20 @@ pub use super::*; ", expect![[r#" crate - Baz: t v - bar: t + Baz: tg vg + bar: tg foo: t crate::foo - Baz: t v + Baz: tg vg PrivateStructFoo: t v bar: t crate::foo::bar Baz: t v PrivateStructBar: t v - PrivateStructFoo: t v - bar: t + PrivateStructFoo: tg vg + bar: tg "#]], ); } @@ -130,9 +130,9 @@ pub(crate) struct PubCrateStruct; ", expect![[r#" crate - Foo: t - PubCrateStruct: t v - bar: t + Foo: tg + PubCrateStruct: tg vg + bar: tg foo: t crate::foo @@ -160,7 +160,7 @@ pub struct Baz; "#, expect![[r#" crate - Baz: t v + Baz: tg vg "#]], ); } @@ -178,7 +178,7 @@ struct Foo; "#, expect![[r#" crate - Baz: t v + Baz: tg vg "#]], ); } @@ -193,8 +193,8 @@ use self::Foo::*; "#, expect![[r#" crate - Bar: t v - Baz: t v + Bar: tg vg + Baz: tg vg Foo: t "#]], ); @@ -210,8 +210,8 @@ use self::Foo::{*}; "#, expect![[r#" crate - Bar: t v - Baz: t v + Bar: tg vg + Baz: tg vg Foo: t "#]], ); @@ -359,7 +359,7 @@ use event::Event; event: t crate::event - Event: t v + Event: t vg serenity: t crate::event::serenity @@ -388,10 +388,10 @@ use reexport::*; "#, expect![[r#" crate - Trait: t + Trait: tg defs: t - function: v - makro: m + function: vg + makro: mg reexport: t crate::defs @@ -400,10 +400,10 @@ use reexport::*; makro: m crate::reexport - Trait: t - function: v + Trait: tg + function: vg inner: t - makro: m + makro: mg crate::reexport::inner Trait: ti @@ -442,12 +442,12 @@ mod glob_target { ShouldBePrivate: t v crate::outer - ShouldBePrivate: t v + ShouldBePrivate: tg vg inner_superglob: t crate::outer::inner_superglob - ShouldBePrivate: t v - inner_superglob: t + ShouldBePrivate: tg vg + inner_superglob: tg "#]], ); } @@ -473,20 +473,20 @@ use reexport_2::*; "#, expect![[r#" crate - Placeholder: t v + Placeholder: tg vg libs: t - reexport_1: t + reexport_1: tg reexport_2: t crate::libs Placeholder: t v crate::reexport_2 - Placeholder: t v + Placeholder: tg vg reexport_1: t crate::reexport_2::reexport_1 - Placeholder: t v + Placeholder: tg vg "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs index a05c4dcf9bd70..610886d55f40f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/macros.rs @@ -97,9 +97,9 @@ macro_rules! structs { bar: t crate::bar - Bar: t - Foo: t - bar: t + Bar: tg + Foo: tg + bar: tg "#]], ); } @@ -130,9 +130,9 @@ macro_rules! structs { bar: t crate::bar - Bar: t - Foo: t - bar: t + Bar: tg + Foo: tg + bar: tg "#]], ); } @@ -169,9 +169,9 @@ macro_rules! inner { bar: t crate::bar - Bar: t - Foo: t - bar: t + Bar: tg + Foo: tg + bar: tg "#]], ); } @@ -794,7 +794,7 @@ pub trait Clone {} "#, expect![[r#" crate - Clone: t m + Clone: tg mg "#]], ); } @@ -1075,9 +1075,9 @@ macro_rules! mbe { "#, expect![[r#" crate - DummyTrait: m - attribute_macro: m - function_like_macro: m + DummyTrait: mg + attribute_macro: mg + function_like_macro: mg "#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs index 899dd4afffef6..c2d3f67f17e77 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs @@ -6,7 +6,7 @@ use bitflags::bitflags; use crate::{ - item_scope::{ImportId, ImportOrExternCrate, ItemInNs}, + item_scope::{ImportId, ImportOrExternCrate, ImportOrGlob, ItemInNs}, visibility::Visibility, MacroId, ModuleDefId, }; @@ -36,8 +36,8 @@ pub struct Item { } pub type TypesItem = Item; -pub type ValuesItem = Item; -pub type MacrosItem = Item; +pub type ValuesItem = Item; +pub type MacrosItem = Item; #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct PerNs { @@ -59,7 +59,7 @@ impl PerNs { PerNs { types: None, values: None, macros: None } } - pub fn values(def: ModuleDefId, vis: Visibility, import: Option) -> PerNs { + pub fn values(def: ModuleDefId, vis: Visibility, import: Option) -> PerNs { PerNs { types: None, values: Some(Item { def, vis, import }), macros: None } } @@ -78,13 +78,13 @@ impl PerNs { values: Some(Item { def: values, vis, - import: import.and_then(ImportOrExternCrate::into_import), + import: import.and_then(ImportOrExternCrate::import_or_glob), }), macros: None, } } - pub fn macros(def: MacroId, vis: Visibility, import: Option) -> PerNs { + pub fn macros(def: MacroId, vis: Visibility, import: Option) -> PerNs { PerNs { types: None, values: None, macros: Some(Item { def, vis, import }) } } @@ -108,7 +108,7 @@ impl PerNs { self.values.map(|it| it.def) } - pub fn take_values_import(self) -> Option<(ModuleDefId, Option)> { + pub fn take_values_import(self) -> Option<(ModuleDefId, Option)> { self.values.map(|it| (it.def, it.import)) } @@ -116,7 +116,7 @@ impl PerNs { self.macros.map(|it| it.def) } - pub fn take_macros_import(self) -> Option<(MacroId, Option)> { + pub fn take_macros_import(self) -> Option<(MacroId, Option)> { self.macros.map(|it| (it.def, it.import)) } @@ -159,14 +159,12 @@ impl PerNs { .map(|it| (ItemInNs::Types(it.def), it.import)) .into_iter() .chain( - self.values.map(|it| { - (ItemInNs::Values(it.def), it.import.map(ImportOrExternCrate::Import)) - }), + self.values + .map(|it| (ItemInNs::Values(it.def), it.import.map(ImportOrExternCrate::from))), ) .chain( - self.macros.map(|it| { - (ItemInNs::Macros(it.def), it.import.map(ImportOrExternCrate::Import)) - }), + self.macros + .map(|it| (ItemInNs::Macros(it.def), it.import.map(ImportOrExternCrate::from))), ) } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs index 0b9b6da8d5133..8c556d8a8c3f4 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs @@ -19,7 +19,7 @@ use crate::{ db::DefDatabase, generics::{GenericParams, TypeOrConstParamData}, hir::{BindingId, ExprId, LabelId}, - item_scope::{BuiltinShadowMode, ImportId, ImportOrExternCrate, BUILTIN_SCOPE}, + item_scope::{BuiltinShadowMode, ImportOrExternCrate, ImportOrGlob, BUILTIN_SCOPE}, lang_item::LangItemTarget, nameres::{DefMap, MacroSubNs, ResolvePathResultPrefixInfo}, path::{ModPath, Path, PathKind}, @@ -107,7 +107,7 @@ pub enum TypeNs { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ResolveValueResult { - ValueNs(ValueNs, Option), + ValueNs(ValueNs, Option), Partial(TypeNs, usize, Option), } @@ -485,7 +485,7 @@ impl Resolver { db: &dyn DefDatabase, path: &ModPath, expected_macro_kind: Option, - ) -> Option<(MacroId, Option)> { + ) -> Option<(MacroId, Option)> { let (item_map, module) = self.item_scope(); item_map .resolve_path(db, module, path, BuiltinShadowMode::Other, expected_macro_kind) @@ -1014,7 +1014,7 @@ impl ModuleItemMap { } } -fn to_value_ns(per_ns: PerNs) -> Option<(ValueNs, Option)> { +fn to_value_ns(per_ns: PerNs) -> Option<(ValueNs, Option)> { let (def, import) = per_ns.take_values_import()?; let res = match def { ModuleDefId::FunctionId(it) => ValueNs::FunctionId(it), diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs index 89eae862bd96c..f0cf7ebf479f8 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs @@ -23,15 +23,6 @@ pub struct ModPath { segments: SmallVec<[Name; 1]>, } -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct UnescapedModPath<'a>(&'a ModPath); - -impl<'a> UnescapedModPath<'a> { - pub fn display(&'a self, db: &'a dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a { - UnescapedDisplay { db, path: self } - } -} - #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum PathKind { Plain, @@ -135,9 +126,11 @@ impl ModPath { _ => None, } } - - pub fn unescaped(&self) -> UnescapedModPath<'_> { - UnescapedModPath(self) + pub fn display_verbatim<'a>( + &'a self, + db: &'a dyn crate::db::ExpandDatabase, + ) -> impl fmt::Display + 'a { + Display { db, path: self, edition: None } } pub fn display<'a>( @@ -145,7 +138,7 @@ impl ModPath { db: &'a dyn crate::db::ExpandDatabase, edition: Edition, ) -> impl fmt::Display + 'a { - Display { db, path: self, edition } + Display { db, path: self, edition: Some(edition) } } } @@ -158,23 +151,12 @@ impl Extend for ModPath { struct Display<'a> { db: &'a dyn ExpandDatabase, path: &'a ModPath, - edition: Edition, + edition: Option, } impl fmt::Display for Display<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - display_fmt_path(self.db, self.path, f, Escape::IfNeeded(self.edition)) - } -} - -struct UnescapedDisplay<'a> { - db: &'a dyn ExpandDatabase, - path: &'a UnescapedModPath<'a>, -} - -impl fmt::Display for UnescapedDisplay<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - display_fmt_path(self.db, self.path.0, f, Escape::No) + display_fmt_path(self.db, self.path, f, self.edition) } } @@ -184,16 +166,11 @@ impl From for ModPath { } } -enum Escape { - No, - IfNeeded(Edition), -} - fn display_fmt_path( db: &dyn ExpandDatabase, path: &ModPath, f: &mut fmt::Formatter<'_>, - escaped: Escape, + edition: Option, ) -> fmt::Result { let mut first_segment = true; let mut add_segment = |s| -> fmt::Result { @@ -221,10 +198,10 @@ fn display_fmt_path( f.write_str("::")?; } first_segment = false; - match escaped { - Escape::IfNeeded(edition) => segment.display(db, edition).fmt(f)?, - Escape::No => segment.unescaped().display(db).fmt(f)?, - } + match edition { + Some(edition) => segment.display(db, edition).fmt(f)?, + None => fmt::Display::fmt(segment.as_str(), f)?, + }; } Ok(()) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs index cc53d2e34aacb..848870c3a3844 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs @@ -4,8 +4,8 @@ use std::fmt; use intern::{sym, Symbol}; use span::{Edition, SyntaxContextId}; -use syntax::ast; use syntax::utils::is_raw_identifier; +use syntax::{ast, format_smolstr}; /// `Name` is a wrapper around string, which is used in hir for both references /// and declarations. In theory, names should also carry hygiene info, but we are @@ -51,33 +51,26 @@ impl PartialEq for Name { } } +impl PartialEq<&Symbol> for Name { + fn eq(&self, &sym: &&Symbol) -> bool { + self.symbol == *sym + } +} + impl PartialEq for Symbol { fn eq(&self, name: &Name) -> bool { *self == name.symbol } } -/// Wrapper of `Name` to print the name without "r#" even when it is a raw identifier. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct UnescapedName<'a>(&'a Name); - -impl<'a> UnescapedName<'a> { - pub fn display(self, db: &dyn crate::db::ExpandDatabase) -> impl fmt::Display + 'a { - _ = db; - UnescapedDisplay { name: self } - } - #[doc(hidden)] - pub fn display_no_db(self) -> impl fmt::Display + 'a { - UnescapedDisplay { name: self } +impl PartialEq for &Symbol { + fn eq(&self, name: &Name) -> bool { + **self == name.symbol } } impl Name { - /// Note: this is private to make creating name from random string hard. - /// Hopefully, this should allow us to integrate hygiene cleaner in the - /// future, and to switch to interned representation of names. fn new_text(text: &str) -> Name { - debug_assert!(!text.starts_with("r#")); Name { symbol: Symbol::intern(text), ctx: () } } @@ -87,12 +80,15 @@ impl Name { // Can't do that for all `SyntaxContextId`s because it breaks Salsa. ctx.remove_root_edition(); _ = ctx; - Self::new_text(text) + match text.strip_prefix("r#") { + Some(text) => Self::new_text(text), + None => Self::new_text(text), + } } pub fn new_root(text: &str) -> Name { // The edition doesn't matter for hygiene. - Self::new(text.trim_start_matches("r#"), SyntaxContextId::root(Edition::Edition2015)) + Self::new(text, SyntaxContextId::root(Edition::Edition2015)) } pub fn new_tuple_field(idx: usize) -> Name { @@ -119,12 +115,22 @@ impl Name { } pub fn new_lifetime(lt: &ast::Lifetime) -> Name { - Self::new_text(lt.text().as_str().trim_start_matches("r#")) + let text = lt.text(); + match text.strip_prefix("'r#") { + Some(text) => Self::new_text(&format_smolstr!("'{text}")), + None => Self::new_text(text.as_str()), + } + } + + pub fn new_symbol(symbol: Symbol, ctx: SyntaxContextId) -> Self { + debug_assert!(!symbol.as_str().starts_with("r#")); + _ = ctx; + Self { symbol, ctx: () } } - /// Resolve a name from the text of token. - fn resolve(raw_text: &str) -> Name { - Name::new_text(raw_text.trim_start_matches("r#")) + // FIXME: This needs to go once we have hygiene + pub fn new_symbol_root(sym: Symbol) -> Self { + Self::new_symbol(sym, SyntaxContextId::root(Edition::Edition2015)) } /// A fake name for things missing in the source code. @@ -161,22 +167,19 @@ impl Name { self.symbol.as_str().parse().ok() } + /// Whether this name needs to be escaped in the given edition via `r#`. + pub fn needs_escape(&self, edition: Edition) -> bool { + is_raw_identifier(self.symbol.as_str(), edition) + } + /// Returns the text this name represents if it isn't a tuple field. /// /// Do not use this for user-facing text, use `display` instead to handle editions properly. + // FIXME: This should take a database argument to hide the interning pub fn as_str(&self) -> &str { self.symbol.as_str() } - // FIXME: Remove this - pub fn unescaped(&self) -> UnescapedName<'_> { - UnescapedName(self) - } - - pub fn needs_escape(&self, edition: Edition) -> bool { - is_raw_identifier(self.symbol.as_str(), edition) - } - pub fn display<'a>( &'a self, db: &dyn crate::db::ExpandDatabase, @@ -186,7 +189,7 @@ impl Name { self.display_no_db(edition) } - // FIXME: Remove this + // FIXME: Remove this in favor of `display`, see fixme on `as_str` #[doc(hidden)] pub fn display_no_db(&self, edition: Edition) -> impl fmt::Display + '_ { Display { name: self, needs_escaping: is_raw_identifier(self.symbol.as_str(), edition) } @@ -195,24 +198,6 @@ impl Name { pub fn symbol(&self) -> &Symbol { &self.symbol } - - pub fn new_symbol(symbol: Symbol, ctx: SyntaxContextId) -> Self { - debug_assert!(!symbol.as_str().starts_with("r#")); - _ = ctx; - Self { symbol, ctx: () } - } - - // FIXME: This needs to go once we have hygiene - pub fn new_symbol_root(sym: Symbol) -> Self { - debug_assert!(!sym.as_str().starts_with("r#")); - Self { symbol: sym, ctx: () } - } - - // FIXME: Remove this - #[inline] - pub fn eq_ident(&self, ident: &str) -> bool { - self.as_str() == ident.trim_start_matches("r#") - } } struct Display<'a> { @@ -229,17 +214,6 @@ impl fmt::Display for Display<'_> { } } -struct UnescapedDisplay<'a> { - name: UnescapedName<'a>, -} - -impl fmt::Display for UnescapedDisplay<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let symbol = self.name.0.symbol.as_str(); - fmt::Display::fmt(symbol, f) - } -} - pub trait AsName { fn as_name(&self) -> Name; } @@ -248,14 +222,14 @@ impl AsName for ast::NameRef { fn as_name(&self) -> Name { match self.as_tuple_field() { Some(idx) => Name::new_tuple_field(idx), - None => Name::resolve(&self.text()), + None => Name::new_root(&self.text()), } } } impl AsName for ast::Name { fn as_name(&self) -> Name { - Name::resolve(&self.text()) + Name::new_root(&self.text()) } } @@ -270,7 +244,7 @@ impl AsName for ast::NameOrNameRef { impl AsName for tt::Ident { fn as_name(&self) -> Name { - Name::resolve(self.sym.as_str()) + Name::new_root(self.sym.as_str()) } } @@ -288,6 +262,6 @@ impl AsName for ast::FieldKind { impl AsName for base_db::Dependency { fn as_name(&self) -> Name { - Name::new_text(&self.name) + Name::new_root(&self.name) } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs index 2b5342314a65a..62feca5f8cbbf 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/autoderef.rs @@ -17,7 +17,7 @@ use crate::{ TraitEnvironment, Ty, TyBuilder, TyKind, }; -static AUTODEREF_RECURSION_LIMIT: Limit = Limit::new(10); +static AUTODEREF_RECURSION_LIMIT: Limit = Limit::new(20); #[derive(Debug)] pub(crate) enum AutoderefKind { @@ -39,7 +39,7 @@ pub fn autoderef( ) -> impl Iterator { let mut table = InferenceTable::new(db, env); let ty = table.instantiate_canonical(ty); - let mut autoderef = Autoderef::new_no_tracking(&mut table, ty, false); + let mut autoderef = Autoderef::new_no_tracking(&mut table, ty, false, false); let mut v = Vec::new(); while let Some((ty, _steps)) = autoderef.next() { // `ty` may contain unresolved inference variables. Since there's no chance they would be @@ -49,7 +49,7 @@ pub fn autoderef( // If the deref chain contains a cycle (e.g. `A` derefs to `B` and `B` derefs to `A`), we // would revisit some already visited types. Stop here to avoid duplication. // - // XXX: The recursion limit for `Autoderef` is currently 10, so `Vec::contains()` shouldn't + // XXX: The recursion limit for `Autoderef` is currently 20, so `Vec::contains()` shouldn't // be too expensive. Replace this duplicate check with `FxHashSet` if it proves to be more // performant. if v.contains(&resolved) { @@ -89,12 +89,18 @@ pub(crate) struct Autoderef<'table, 'db, T = Vec<(AutoderefKind, Ty)>> { at_start: bool, steps: T, explicit: bool, + use_receiver_trait: bool, } impl<'table, 'db> Autoderef<'table, 'db> { - pub(crate) fn new(table: &'table mut InferenceTable<'db>, ty: Ty, explicit: bool) -> Self { + pub(crate) fn new( + table: &'table mut InferenceTable<'db>, + ty: Ty, + explicit: bool, + use_receiver_trait: bool, + ) -> Self { let ty = table.resolve_ty_shallow(&ty); - Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit } + Autoderef { table, ty, at_start: true, steps: Vec::new(), explicit, use_receiver_trait } } pub(crate) fn steps(&self) -> &[(AutoderefKind, Ty)] { @@ -107,9 +113,10 @@ impl<'table, 'db> Autoderef<'table, 'db, usize> { table: &'table mut InferenceTable<'db>, ty: Ty, explicit: bool, + use_receiver_trait: bool, ) -> Self { let ty = table.resolve_ty_shallow(&ty); - Autoderef { table, ty, at_start: true, steps: 0, explicit } + Autoderef { table, ty, at_start: true, steps: 0, explicit, use_receiver_trait } } } @@ -137,7 +144,8 @@ impl Iterator for Autoderef<'_, '_, T> { return None; } - let (kind, new_ty) = autoderef_step(self.table, self.ty.clone(), self.explicit)?; + let (kind, new_ty) = + autoderef_step(self.table, self.ty.clone(), self.explicit, self.use_receiver_trait)?; self.steps.push(kind, &self.ty); self.ty = new_ty; @@ -150,11 +158,12 @@ pub(crate) fn autoderef_step( table: &mut InferenceTable<'_>, ty: Ty, explicit: bool, + use_receiver_trait: bool, ) -> Option<(AutoderefKind, Ty)> { if let Some(derefed) = builtin_deref(table.db, &ty, explicit) { Some((AutoderefKind::Builtin, table.resolve_ty_shallow(derefed))) } else { - Some((AutoderefKind::Overloaded, deref_by_trait(table, ty)?)) + Some((AutoderefKind::Overloaded, deref_by_trait(table, ty, use_receiver_trait)?)) } } @@ -176,6 +185,7 @@ pub(crate) fn builtin_deref<'ty>( pub(crate) fn deref_by_trait( table @ &mut InferenceTable { db, .. }: &mut InferenceTable<'_>, ty: Ty, + use_receiver_trait: bool, ) -> Option { let _p = tracing::info_span!("deref_by_trait").entered(); if table.resolve_ty_shallow(&ty).inference_var(Interner).is_some() { @@ -183,14 +193,25 @@ pub(crate) fn deref_by_trait( return None; } - let deref_trait = - db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait())?; + let trait_id = || { + if use_receiver_trait { + if let Some(receiver) = + db.lang_item(table.trait_env.krate, LangItem::Receiver).and_then(|l| l.as_trait()) + { + return Some(receiver); + } + } + // Old rustc versions might not have `Receiver` trait. + // Fallback to `Deref` if they don't + db.lang_item(table.trait_env.krate, LangItem::Deref).and_then(|l| l.as_trait()) + }; + let trait_id = trait_id()?; let target = db - .trait_data(deref_trait) + .trait_data(trait_id) .associated_type_by_name(&Name::new_symbol_root(sym::Target.clone()))?; let projection = { - let b = TyBuilder::subst_for_def(db, deref_trait, None); + let b = TyBuilder::subst_for_def(db, trait_id, None); if b.remaining() != 1 { // the Target type + Deref trait should only have one generic parameter, // namely Deref's Self type diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs index 4991d173b9c42..774991560e9ca 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs @@ -231,8 +231,7 @@ impl<'a> DeclValidator<'a> { .filter_map(|(pat_id, pat)| match pat { Pat::Bind { id, .. } => { let bind_name = &body.bindings[*id].name; - let mut suggested_text = - to_lower_snake_case(&bind_name.unescaped().display_no_db().to_smolstr())?; + let mut suggested_text = to_lower_snake_case(bind_name.as_str())?; if is_raw_identifier(&suggested_text, edition) { suggested_text.insert_str(0, "r#"); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs index 3545bf7677671..ae8fbe2ce6d76 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs @@ -34,6 +34,7 @@ use rustc_apfloat::{ ieee::{Half as f16, Quad as f128}, Float, }; +use rustc_hash::FxHashSet; use smallvec::SmallVec; use span::Edition; use stdx::never; @@ -87,6 +88,35 @@ pub struct HirFormatter<'a> { omit_verbose_types: bool, closure_style: ClosureStyle, display_target: DisplayTarget, + bounds_formatting_ctx: BoundsFormattingCtx, +} + +#[derive(Default)] +enum BoundsFormattingCtx { + Entered { + /// We can have recursive bounds like the following case: + /// ```rust + /// where + /// T: Foo, + /// T::FooAssoc: Baz<::BarAssoc> + Bar + /// ``` + /// So, record the projection types met while formatting bounds and + //. prevent recursing into their bounds to avoid infinite loops. + projection_tys_met: FxHashSet, + }, + #[default] + Exited, +} + +impl BoundsFormattingCtx { + fn contains(&mut self, proj: &ProjectionTy) -> bool { + match self { + BoundsFormattingCtx::Entered { projection_tys_met } => { + projection_tys_met.contains(proj) + } + BoundsFormattingCtx::Exited => false, + } + } } impl HirFormatter<'_> { @@ -97,6 +127,30 @@ impl HirFormatter<'_> { fn end_location_link(&mut self) { self.fmt.end_location_link(); } + + fn format_bounds_with T>( + &mut self, + target: ProjectionTy, + format_bounds: F, + ) -> T { + match self.bounds_formatting_ctx { + BoundsFormattingCtx::Entered { ref mut projection_tys_met } => { + projection_tys_met.insert(target); + format_bounds(self) + } + BoundsFormattingCtx::Exited => { + let mut projection_tys_met = FxHashSet::default(); + projection_tys_met.insert(target); + self.bounds_formatting_ctx = BoundsFormattingCtx::Entered { projection_tys_met }; + let res = format_bounds(self); + // Since we want to prevent only the infinite recursions in bounds formatting + // and do not want to skip formatting of other separate bounds, clear context + // when exiting the formatting of outermost bounds + self.bounds_formatting_ctx = BoundsFormattingCtx::Exited; + res + } + } + } } pub trait HirDisplay { @@ -220,6 +274,7 @@ pub trait HirDisplay { closure_style: ClosureStyle::ImplFn, display_target: DisplayTarget::SourceCode { module_id, allow_opaque }, show_container_bounds: false, + bounds_formatting_ctx: Default::default(), }) { Ok(()) => {} Err(HirDisplayError::FmtError) => panic!("Writing to String can't fail!"), @@ -427,6 +482,7 @@ impl HirDisplayWrapper<'_, T> { display_target: self.display_target, closure_style: self.closure_style, show_container_bounds: self.show_container_bounds, + bounds_formatting_ctx: Default::default(), }) } @@ -479,42 +535,46 @@ impl HirDisplay for ProjectionTy { // `::Assoc` if !f.display_target.is_source_code() { if let TyKind::Placeholder(idx) = self_ty.kind(Interner) { - let db = f.db; - let id = from_placeholder_idx(db, *idx); - let generics = generics(db.upcast(), id.parent); - - let substs = generics.placeholder_subst(db); - let bounds = db - .generic_predicates(id.parent) - .iter() - .map(|pred| pred.clone().substitute(Interner, &substs)) - .filter(|wc| match wc.skip_binders() { - WhereClause::Implemented(tr) => { - match tr.self_type_parameter(Interner).kind(Interner) { - TyKind::Alias(AliasTy::Projection(proj)) => proj == self, - _ => false, + if !f.bounds_formatting_ctx.contains(self) { + let db = f.db; + let id = from_placeholder_idx(db, *idx); + let generics = generics(db.upcast(), id.parent); + + let substs = generics.placeholder_subst(db); + let bounds = db + .generic_predicates(id.parent) + .iter() + .map(|pred| pred.clone().substitute(Interner, &substs)) + .filter(|wc| match wc.skip_binders() { + WhereClause::Implemented(tr) => { + matches!( + tr.self_type_parameter(Interner).kind(Interner), + TyKind::Alias(_) + ) } - } - WhereClause::TypeOutlives(t) => match t.ty.kind(Interner) { - TyKind::Alias(AliasTy::Projection(proj)) => proj == self, - _ => false, - }, - // We shouldn't be here if these exist - WhereClause::AliasEq(_) => false, - WhereClause::LifetimeOutlives(_) => false, - }) - .collect::>(); - if !bounds.is_empty() { - return write_bounds_like_dyn_trait_with_prefix( - f, - "impl", - Either::Left( - &TyKind::Alias(AliasTy::Projection(self.clone())).intern(Interner), - ), - &bounds, - SizedByDefault::NotSized, - ); - }; + WhereClause::TypeOutlives(t) => { + matches!(t.ty.kind(Interner), TyKind::Alias(_)) + } + // We shouldn't be here if these exist + WhereClause::AliasEq(_) => false, + WhereClause::LifetimeOutlives(_) => false, + }) + .collect::>(); + if !bounds.is_empty() { + return f.format_bounds_with(self.clone(), |f| { + write_bounds_like_dyn_trait_with_prefix( + f, + "impl", + Either::Left( + &TyKind::Alias(AliasTy::Projection(self.clone())) + .intern(Interner), + ), + &bounds, + SizedByDefault::NotSized, + ) + }); + } + } } } @@ -1159,6 +1219,7 @@ impl HirDisplay for Ty { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }, ) { write!(f, "{}", path.display(f.db.upcast(), f.edition()))?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 2523aba538334..9283c46d0f611 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -277,7 +277,7 @@ impl CapturedItem { /// Converts the place to a name that can be inserted into source code. pub fn place_to_name(&self, owner: DefWithBodyId, db: &dyn HirDatabase) -> String { let body = db.body(owner); - let mut result = body[self.place.local].name.unescaped().display(db.upcast()).to_string(); + let mut result = body[self.place.local].name.as_str().to_owned(); for proj in &self.place.projections { match proj { ProjectionElem::Deref => {} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs index 2fe90a8a92432..d40816ba8ced2 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/coerce.rs @@ -420,7 +420,7 @@ impl InferenceTable<'_> { let snapshot = self.snapshot(); - let mut autoderef = Autoderef::new(self, from_ty.clone(), false); + let mut autoderef = Autoderef::new(self, from_ty.clone(), false, false); let mut first_error = None; let mut found = None; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 6b6c0348dcb4f..b951443897cb0 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -487,7 +487,7 @@ impl InferenceContext<'_> { } Expr::Call { callee, args, .. } => { let callee_ty = self.infer_expr(*callee, &Expectation::none(), ExprIsRead::Yes); - let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false); + let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone(), false, true); let (res, derefed_callee) = loop { let Some((callee_deref_ty, _)) = derefs.next() else { break (None, callee_ty.clone()); @@ -854,7 +854,7 @@ impl InferenceContext<'_> { if let Some(derefed) = builtin_deref(self.table.db, &inner_ty, true) { self.resolve_ty_shallow(derefed) } else { - deref_by_trait(&mut self.table, inner_ty) + deref_by_trait(&mut self.table, inner_ty, false) .unwrap_or_else(|| self.err_ty()) } } @@ -1718,7 +1718,7 @@ impl InferenceContext<'_> { receiver_ty: &Ty, name: &Name, ) -> Option<(Ty, Either, Vec, bool)> { - let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone(), false); + let mut autoderef = Autoderef::new(&mut self.table, receiver_ty.clone(), false, false); let mut private_field = None; let res = autoderef.by_ref().find_map(|(derefed_ty, _)| { let (field_id, parameters) = match derefed_ty.kind(Interner) { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index 182032f04812d..1cea67ee96419 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -528,7 +528,7 @@ impl ReceiverAdjustments { let mut ty = table.resolve_ty_shallow(&ty); let mut adjust = Vec::new(); for _ in 0..self.autoderefs { - match autoderef::autoderef_step(table, ty.clone(), true) { + match autoderef::autoderef_step(table, ty.clone(), true, false) { None => { never!("autoderef not possible for {:?}", ty); ty = TyKind::Error.intern(Interner); @@ -1106,7 +1106,8 @@ fn iterate_method_candidates_by_receiver( // be found in any of the derefs of receiver_ty, so we have to go through // that, including raw derefs. table.run_in_snapshot(|table| { - let mut autoderef = autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true); + let mut autoderef = + autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true, true); while let Some((self_ty, _)) = autoderef.next() { iterate_inherent_methods( &self_ty, @@ -1123,7 +1124,8 @@ fn iterate_method_candidates_by_receiver( ControlFlow::Continue(()) })?; table.run_in_snapshot(|table| { - let mut autoderef = autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true); + let mut autoderef = + autoderef::Autoderef::new_no_tracking(table, receiver_ty.clone(), true, true); while let Some((self_ty, _)) = autoderef.next() { if matches!(self_ty.kind(Interner), TyKind::InferenceVar(_, TyVariableKind::General)) { // don't try to resolve methods on unknown types @@ -1709,7 +1711,7 @@ fn autoderef_method_receiver( ty: Ty, ) -> Vec<(Canonical, ReceiverAdjustments)> { let mut deref_chain: Vec<_> = Vec::new(); - let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ty, false); + let mut autoderef = autoderef::Autoderef::new_no_tracking(table, ty, false, true); while let Some((ty, derefs)) = autoderef.next() { deref_chain.push(( autoderef.table.canonicalize(ty), diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index 74acf23b75ab6..8866de22dfb99 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -1343,7 +1343,7 @@ fn foo(a: &T) { fn autoderef_visibility_field() { check( r#" -//- minicore: deref +//- minicore: receiver mod a { pub struct Foo(pub char); pub struct Bar(i32); @@ -1375,7 +1375,7 @@ fn autoderef_visibility_method() { cov_mark::check!(autoderef_candidate_not_visible); check( r#" -//- minicore: deref +//- minicore: receiver mod a { pub struct Foo(pub char); impl Foo { @@ -1741,7 +1741,7 @@ fn main() { fn deref_fun_1() { check_types( r#" -//- minicore: deref +//- minicore: receiver struct A(T, U); struct B(T); @@ -1782,7 +1782,7 @@ fn test() { fn deref_fun_2() { check_types( r#" -//- minicore: deref +//- minicore: receiver struct A(T, U); struct B(T); @@ -1903,7 +1903,7 @@ pub fn test(generic_args: impl Into) { fn bad_inferred_reference_2() { check_no_mismatches( r#" -//- minicore: deref +//- minicore: receiver trait ExactSizeIterator { fn len(&self) -> usize; } @@ -2054,7 +2054,7 @@ fn foo() { fn box_deref_is_builtin() { check( r#" -//- minicore: deref +//- minicore: receiver use core::ops::Deref; #[lang = "owned_box"] @@ -2087,7 +2087,7 @@ fn test() { fn manually_drop_deref_is_not_builtin() { check( r#" -//- minicore: manually_drop, deref +//- minicore: manually_drop, receiver struct Foo; impl Foo { fn foo(&self) {} @@ -2105,7 +2105,7 @@ fn test() { fn mismatched_args_due_to_supertraits_with_deref() { check_no_mismatches( r#" -//- minicore: deref +//- minicore: receiver use core::ops::Deref; trait Trait1 { @@ -2139,3 +2139,34 @@ fn problem_method() { "#, ); } + +#[test] +fn receiver_without_deref_impl() { + check( + r#" +//- minicore: receiver +use core::ops::Receiver; + +struct Foo; + +impl Foo { + fn foo1(self: &Bar) -> i32 { 42 } + fn foo2(self: Bar) -> bool { true } +} + +struct Bar; + +impl Receiver for Bar { + type Target = Foo; +} + +fn main() { + let bar = Bar; + let _v1 = bar.foo1(); + //^^^ type: i32 + let _v2 = bar.foo2(); + //^^^ type: bool +} +"#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index db3121d3cd35f..0cbc75726bf39 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -45,7 +45,7 @@ use hir_def::{ body::BodyDiagnostic, data::{adt::VariantData, TraitFlags}, generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance}, - hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, Pat}, + hir::{BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, LabelId, Pat}, item_tree::{AttrOwner, FieldParent, ItemTreeFieldId, ItemTreeNode}, lang_item::LangItemTarget, layout::{self, ReprOptions, TargetDataLayout}, @@ -2470,20 +2470,31 @@ impl Param { } pub fn as_local(&self, db: &dyn HirDatabase) -> Option { - let parent = match self.func { - Callee::Def(CallableDefId::FunctionId(it)) => DefWithBodyId::FunctionId(it), - Callee::Closure(closure, _) => db.lookup_intern_closure(closure.into()).0, - _ => return None, - }; - let body = db.body(parent); - if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) { - Some(Local { parent, binding_id: self_param }) - } else if let Pat::Bind { id, .. } = - &body[body.params[self.idx - body.self_param.is_some() as usize]] - { - Some(Local { parent, binding_id: *id }) - } else { - None + match self.func { + Callee::Def(CallableDefId::FunctionId(it)) => { + let parent = DefWithBodyId::FunctionId(it); + let body = db.body(parent); + if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) { + Some(Local { parent, binding_id: self_param }) + } else if let Pat::Bind { id, .. } = + &body[body.params[self.idx - body.self_param.is_some() as usize]] + { + Some(Local { parent, binding_id: *id }) + } else { + None + } + } + Callee::Closure(closure, _) => { + let c = db.lookup_intern_closure(closure.into()); + let body = db.body(c.0); + if let Expr::Closure { args, .. } = &body[c.1] { + if let Pat::Bind { id, .. } = &body[args[self.idx]] { + return Some(Local { parent: c.0, binding_id: *id }); + } + } + None + } + _ => None, } } @@ -2756,6 +2767,15 @@ impl Trait { traits.iter().map(|tr| Trait::from(*tr)).collect() } + pub fn function(self, db: &dyn HirDatabase, name: impl PartialEq) -> Option { + db.trait_data(self.id).items.iter().find(|(n, _)| name == *n).and_then( + |&(_, it)| match it { + AssocItemId::FunctionId(id) => Some(Function { id }), + _ => None, + }, + ) + } + pub fn items(self, db: &dyn HirDatabase) -> Vec { db.trait_data(self.id).items.iter().map(|(_name, it)| (*it).into()).collect() } @@ -4673,6 +4693,10 @@ impl Type { matches!(self.ty.kind(Interner), TyKind::Scalar(Scalar::Bool)) } + pub fn is_str(&self) -> bool { + matches!(self.ty.kind(Interner), TyKind::Str) + } + pub fn is_never(&self) -> bool { matches!(self.ty.kind(Interner), TyKind::Never) } diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 523bc6f10aab6..09470bed9cfb4 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -1439,8 +1439,20 @@ impl<'db> SemanticsImpl<'db> { self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call) } - pub fn resolve_known_blanket_dual_impls(&self, call: &ast::MethodCallExpr) -> Option { - self.analyze(call.syntax())?.resolve_known_blanket_dual_impls(self.db, call) + /// Env is used to derive the trait environment + // FIXME: better api for the trait environment + pub fn resolve_trait_impl_method( + &self, + env: Type, + trait_: Trait, + func: Function, + subst: impl IntoIterator, + ) -> Option { + let mut substs = hir_ty::TyBuilder::subst_for_def(self.db, TraitId::from(trait_), None); + for s in subst { + substs = substs.push(s.ty); + } + Some(self.db.lookup_impl_method(env.env, func.into(), substs.build()).0.into()) } fn resolve_range_pat(&self, range_pat: &ast::RangePat) -> Option { @@ -1471,6 +1483,8 @@ impl<'db> SemanticsImpl<'db> { self.analyze(try_expr.syntax())?.resolve_try_expr(self.db, try_expr) } + // This does not resolve the method call to the correct trait impl! + // We should probably fix that. pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option { self.analyze(call.syntax())?.resolve_method_call_as_callable(self.db, call) } diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index 6b78d7a3631fa..b699ccde4128e 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -322,68 +322,6 @@ impl SourceAnalyzer { } } - // If the method is into(), try_into(), parse(), resolve it to from, try_from, from_str. - pub(crate) fn resolve_known_blanket_dual_impls( - &self, - db: &dyn HirDatabase, - call: &ast::MethodCallExpr, - ) -> Option { - // e.g. if the method call is let b = a.into(), - // - receiver_type is A (type of a) - // - return_type is B (type of b) - // We will find the definition of B::from(a: A). - let callable = self.resolve_method_call_as_callable(db, call)?; - let (_, receiver_type) = callable.receiver_param(db)?; - let return_type = callable.return_type(); - let (search_method, substs) = match call.name_ref()?.text().as_str() { - "into" => { - let trait_ = - self.resolver.resolve_known_trait(db.upcast(), &path![core::convert::From])?; - ( - self.trait_fn(db, trait_, "from")?, - hir_ty::TyBuilder::subst_for_def(db, trait_, None) - .push(return_type.ty) - .push(receiver_type.ty) - .build(), - ) - } - "try_into" => { - let trait_ = self - .resolver - .resolve_known_trait(db.upcast(), &path![core::convert::TryFrom])?; - ( - self.trait_fn(db, trait_, "try_from")?, - hir_ty::TyBuilder::subst_for_def(db, trait_, None) - // If the method is try_into() or parse(), return_type is Result. - // Get T from type arguments of Result. - .push(return_type.type_arguments().next()?.ty) - .push(receiver_type.ty) - .build(), - ) - } - "parse" => { - let trait_ = - self.resolver.resolve_known_trait(db.upcast(), &path![core::str::FromStr])?; - ( - self.trait_fn(db, trait_, "from_str")?, - hir_ty::TyBuilder::subst_for_def(db, trait_, None) - .push(return_type.type_arguments().next()?.ty) - .build(), - ) - } - _ => return None, - }; - - let found_method = self.resolve_impl_method_or_trait_def(db, search_method, substs); - // If found_method == search_method, the method in trait itself is resolved. - // It means the blanket dual impl is not found. - if found_method == search_method { - None - } else { - Some(found_method.into()) - } - } - pub(crate) fn resolve_expr_as_callable( &self, db: &dyn HirDatabase, @@ -1309,18 +1247,6 @@ impl SourceAnalyzer { Some((trait_id, fn_id)) } - fn trait_fn( - &self, - db: &dyn HirDatabase, - trait_id: TraitId, - method_name: &str, - ) -> Option { - db.trait_data(trait_id).items.iter().find_map(|(item_name, item)| match item { - AssocItemId::FunctionId(t) if item_name.as_str() == method_name => Some(*t), - _ => None, - }) - } - fn ty_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<&Ty> { self.infer.as_ref()?.type_of_expr_or_pat(self.expr_id(db, expr)?) } diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index a6b8ed70c363a..2ebd88edae2d7 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -3,7 +3,7 @@ use either::Either; use hir_def::{ db::DefDatabase, - item_scope::{ImportId, ImportOrExternCrate}, + item_scope::{ImportId, ImportOrExternCrate, ImportOrGlob}, per_ns::Item, src::{HasChildSource, HasSource}, visibility::{Visibility, VisibilityExplicitness}, @@ -55,9 +55,10 @@ impl DeclarationLocation { } /// Represents an outstanding module that the symbol collector must collect symbols from. +#[derive(Debug)] struct SymbolCollectorWork { module_id: ModuleId, - parent: Option, + parent: Option, } pub struct SymbolCollector<'a> { @@ -81,7 +82,15 @@ impl<'a> SymbolCollector<'a> { } } + pub fn new_module(db: &dyn HirDatabase, module: Module) -> Box<[FileSymbol]> { + let mut symbol_collector = SymbolCollector::new(db); + symbol_collector.collect(module); + symbol_collector.finish() + } + pub fn collect(&mut self, module: Module) { + let _p = tracing::info_span!("SymbolCollector::collect", ?module).entered(); + tracing::info!(?module, "SymbolCollector::collect",); self.edition = module.krate().edition(self.db); // The initial work is the root module we're collecting, additional work will @@ -97,16 +106,12 @@ impl<'a> SymbolCollector<'a> { self.symbols.into_iter().collect() } - pub fn collect_module(db: &dyn HirDatabase, module: Module) -> Box<[FileSymbol]> { - let mut symbol_collector = SymbolCollector::new(db); - symbol_collector.collect(module); - symbol_collector.finish() - } - fn do_work(&mut self, work: SymbolCollectorWork) { + let _p = tracing::info_span!("SymbolCollector::do_work", ?work).entered(); + tracing::info!(?work, "SymbolCollector::do_work"); self.db.unwind_if_cancelled(); - let parent_name = work.parent.and_then(|id| self.def_with_body_id_name(id)); + let parent_name = work.parent.map(|name| name.as_str().to_smolstr()); self.with_container_name(parent_name, |s| s.collect_from_module(work.module_id)); } @@ -116,18 +121,18 @@ impl<'a> SymbolCollector<'a> { ModuleDefId::ModuleId(id) => this.push_module(id, name), ModuleDefId::FunctionId(id) => { this.push_decl(id, name, false); - this.collect_from_body(id); + this.collect_from_body(id, Some(name.clone())); } ModuleDefId::AdtId(AdtId::StructId(id)) => this.push_decl(id, name, false), ModuleDefId::AdtId(AdtId::EnumId(id)) => this.push_decl(id, name, false), ModuleDefId::AdtId(AdtId::UnionId(id)) => this.push_decl(id, name, false), ModuleDefId::ConstId(id) => { this.push_decl(id, name, false); - this.collect_from_body(id); + this.collect_from_body(id, Some(name.clone())); } ModuleDefId::StaticId(id) => { this.push_decl(id, name, false); - this.collect_from_body(id); + this.collect_from_body(id, Some(name.clone())); } ModuleDefId::TraitId(id) => { this.push_decl(id, name, false); @@ -153,24 +158,32 @@ impl<'a> SymbolCollector<'a> { // Nested trees are very common, so a cache here will hit a lot. let import_child_source_cache = &mut FxHashMap::default(); - let mut push_import = |this: &mut Self, i: ImportId, name: &Name, def: ModuleDefId| { + let is_explicit_import = |vis| match vis { + Visibility::Public => true, + Visibility::Module(_, VisibilityExplicitness::Explicit) => true, + Visibility::Module(_, VisibilityExplicitness::Implicit) => false, + }; + + let mut push_import = |this: &mut Self, i: ImportId, name: &Name, def: ModuleDefId, vis| { let source = import_child_source_cache - .entry(i.import) - .or_insert_with(|| i.import.child_source(this.db.upcast())); + .entry(i.use_) + .or_insert_with(|| i.use_.child_source(this.db.upcast())); let Some(use_tree_src) = source.value.get(i.idx) else { return }; - let Some(name_ptr) = use_tree_src - .rename() - .and_then(|rename| rename.name()) - .map(Either::Left) - .or_else(|| use_tree_src.path()?.segment()?.name_ref().map(Either::Right)) - .map(|it| AstPtr::new(&it)) - else { + let rename = use_tree_src.rename().and_then(|rename| rename.name()); + let name_syntax = match rename { + Some(name) => Some(Either::Left(name)), + None if is_explicit_import(vis) => { + (|| use_tree_src.path()?.segment()?.name_ref().map(Either::Right))() + } + None => None, + }; + let Some(name_syntax) = name_syntax else { return; }; let dec_loc = DeclarationLocation { hir_file_id: source.file_id, ptr: SyntaxNodePtr::new(use_tree_src.syntax()), - name_ptr, + name_ptr: AstPtr::new(&name_syntax), }; this.symbols.insert(FileSymbol { name: name.symbol().clone(), @@ -183,23 +196,23 @@ impl<'a> SymbolCollector<'a> { }; let push_extern_crate = - |this: &mut Self, i: ExternCrateId, name: &Name, def: ModuleDefId| { + |this: &mut Self, i: ExternCrateId, name: &Name, def: ModuleDefId, vis| { let loc = i.lookup(this.db.upcast()); let source = loc.source(this.db.upcast()); - let Some(name_ptr) = source - .value - .rename() - .and_then(|rename| rename.name()) - .map(Either::Left) - .or_else(|| source.value.name_ref().map(Either::Right)) - .map(|it| AstPtr::new(&it)) - else { + let rename = source.value.rename().and_then(|rename| rename.name()); + + let name_syntax = match rename { + Some(name) => Some(Either::Left(name)), + None if is_explicit_import(vis) => None, + None => source.value.name_ref().map(Either::Right), + }; + let Some(name_syntax) = name_syntax else { return; }; let dec_loc = DeclarationLocation { hir_file_id: source.file_id, ptr: SyntaxNodePtr::new(source.value.syntax()), - name_ptr, + name_ptr: AstPtr::new(&name_syntax), }; this.symbols.insert(FileSymbol { name: name.symbol().clone(), @@ -211,18 +224,6 @@ impl<'a> SymbolCollector<'a> { }); }; - let is_explicit_import = |vis| { - match vis { - Visibility::Module(_, VisibilityExplicitness::Explicit) => true, - Visibility::Module(_, VisibilityExplicitness::Implicit) => { - // consider imports in the crate root explicit, as these are visibly - // crate-wide anyways - module_id.is_crate_root() - } - Visibility::Public => true, - } - }; - let def_map = module_id.def_map(self.db.upcast()); let scope = &def_map[module_id.local_id].scope; @@ -232,14 +233,14 @@ impl<'a> SymbolCollector<'a> { for (name, Item { def, vis, import }) in scope.types() { if let Some(i) = import { - if is_explicit_import(vis) { - match i { - ImportOrExternCrate::Import(i) => push_import(self, i, name, def), - ImportOrExternCrate::ExternCrate(i) => { - push_extern_crate(self, i, name, def) - } + match i { + ImportOrExternCrate::Import(i) => push_import(self, i, name, def, vis), + ImportOrExternCrate::Glob(_) => (), + ImportOrExternCrate::ExternCrate(i) => { + push_extern_crate(self, i, name, def, vis) } } + continue; } // self is a declaration @@ -248,8 +249,9 @@ impl<'a> SymbolCollector<'a> { for (name, Item { def, vis, import }) in scope.macros() { if let Some(i) = import { - if is_explicit_import(vis) { - push_import(self, i, name, def.into()); + match i { + ImportOrGlob::Import(i) => push_import(self, i, name, def.into(), vis), + ImportOrGlob::Glob(_) => (), } continue; } @@ -259,8 +261,9 @@ impl<'a> SymbolCollector<'a> { for (name, Item { def, vis, import }) in scope.values() { if let Some(i) = import { - if is_explicit_import(vis) { - push_import(self, i, name, def); + match i { + ImportOrGlob::Import(i) => push_import(self, i, name, def, vis), + ImportOrGlob::Glob(_) => (), } continue; } @@ -269,7 +272,7 @@ impl<'a> SymbolCollector<'a> { } for const_id in scope.unnamed_consts() { - self.collect_from_body(const_id); + self.collect_from_body(const_id, None); } for (name, id) in scope.legacy_macros() { @@ -285,7 +288,7 @@ impl<'a> SymbolCollector<'a> { } } - fn collect_from_body(&mut self, body_id: impl Into) { + fn collect_from_body(&mut self, body_id: impl Into, name: Option) { let body_id = body_id.into(); let body = self.db.body(body_id); @@ -294,7 +297,7 @@ impl<'a> SymbolCollector<'a> { for (id, _) in def_map.modules() { self.work.push(SymbolCollectorWork { module_id: def_map.module_id(id), - parent: Some(body_id), + parent: name.clone(), }); } } @@ -333,24 +336,6 @@ impl<'a> SymbolCollector<'a> { } } - fn def_with_body_id_name(&self, body_id: DefWithBodyId) -> Option { - match body_id { - DefWithBodyId::FunctionId(id) => { - Some(self.db.function_data(id).name.display_no_db(self.edition).to_smolstr()) - } - DefWithBodyId::StaticId(id) => { - Some(self.db.static_data(id).name.display_no_db(self.edition).to_smolstr()) - } - DefWithBodyId::ConstId(id) => { - Some(self.db.const_data(id).name.as_ref()?.display_no_db(self.edition).to_smolstr()) - } - DefWithBodyId::VariantId(id) => { - Some(self.db.enum_variant_data(id).name.display_no_db(self.edition).to_smolstr()) - } - DefWithBodyId::InTypeConstId(_) => Some("in type const".into()), - } - } - fn push_assoc_item(&mut self, assoc_item_id: AssocItemId, name: &Name) { match assoc_item_id { AssocItemId::FunctionId(id) => self.push_decl(id, name, true), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs index 82d8db4258924..fb533077d9626 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/assist_config.rs @@ -28,6 +28,7 @@ impl AssistConfig { prefer_no_std: self.prefer_no_std, prefer_prelude: self.prefer_prelude, prefer_absolute: self.prefer_absolute, + allow_unstable: true, } } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs index a5c5b08d5b0c7..eb784cd1226fd 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_bool_then.rs @@ -159,7 +159,7 @@ pub(crate) fn convert_bool_then_to_if(acc: &mut Assists, ctx: &AssistContext<'_> }; // Verify this is `bool::then` that is being called. let func = ctx.sema.resolve_method_call(&mcall)?; - if !func.name(ctx.sema.db).eq_ident("then") { + if func.name(ctx.sema.db) != sym::then { return None; } let assoc = func.as_assoc_item(ctx.sema.db)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs index bb04a43cf9615..d34cf895cd90a 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_closure_to_fn.rs @@ -343,11 +343,9 @@ fn compute_closure_type_params( let mut mentioned_names = mentioned_generic_params .iter() .filter_map(|param| match param { - hir::GenericParam::TypeParam(param) => { - Some(param.name(ctx.db()).unescaped().display(ctx.db()).to_smolstr()) - } + hir::GenericParam::TypeParam(param) => Some(param.name(ctx.db()).as_str().to_smolstr()), hir::GenericParam::ConstParam(param) => { - Some(param.name(ctx.db()).unescaped().display(ctx.db()).to_smolstr()) + Some(param.name(ctx.db()).as_str().to_smolstr()) } hir::GenericParam::LifetimeParam(_) => None, }) @@ -390,7 +388,7 @@ fn compute_closure_type_params( let has_name = syntax .descendants() .filter_map(ast::NameOrNameRef::cast) - .any(|name| mentioned_names.contains(&*name.text())); + .any(|name| mentioned_names.contains(name.text().trim_start_matches("r#"))); let mut has_new_params = false; if has_name { syntax diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 615b5d3f98b55..d4f2ea3bd941b 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -170,7 +170,7 @@ fn existing_definition(db: &RootDatabase, variant_name: &ast::Name, variant: &Va ), _ => false, }) - .any(|(name, _)| name.eq_ident(variant_name.text().as_str())) + .any(|(name, _)| name.as_str() == variant_name.text().trim_start_matches("r#")) } fn extract_generic_params( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index 97321f4ec1ef0..7b6f76d00452e 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -1672,8 +1672,8 @@ macro_rules! vec { () => {Vec} } fn main() { - let $0vec = vec![]; - let _ = vec; + let $0items = vec![]; + let _ = items; } "#, "Extract into variable", @@ -1696,8 +1696,8 @@ macro_rules! vec { () => {Vec} } fn main() { - const $0VEC: Vec = vec![]; - let _ = VEC; + const $0ITEMS: Vec = vec![]; + let _ = ITEMS; } "#, "Extract into constant", @@ -1720,8 +1720,8 @@ macro_rules! vec { () => {Vec} } fn main() { - static $0VEC: Vec = vec![]; - let _ = VEC; + static $0ITEMS: Vec = vec![]; + let _ = ITEMS; } "#, "Extract into static", @@ -2019,8 +2019,8 @@ impl Vec { } fn foo(s: &mut S) { - let $0vec = &mut s.vec; - vec.push(0); + let $0items = &mut s.vec; + items.push(0); }"#, "Extract into variable", ); @@ -2106,8 +2106,8 @@ impl Vec { } fn foo(f: &mut Y) { - let $0vec = &mut f.field.field.vec; - vec.push(0); + let $0items = &mut f.field.field.vec; + items.push(0); }"#, "Extract into variable", ); diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs index 7a92d8911bf85..47e4a68293f0c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/fix_visibility.rs @@ -48,7 +48,7 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) let (_, def) = module .scope(ctx.db(), None) .into_iter() - .find(|(name, _)| name.eq_ident(name_ref.text().as_str()))?; + .find(|(name, _)| name.as_str() == name_ref.text().trim_start_matches("r#"))?; let ScopeDef::ModuleDef(def) = def else { return None; }; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs index 8a7a06b380f51..10915f8aafb8d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_from_mod_rs.rs @@ -2,7 +2,7 @@ use ide_db::{ assists::{AssistId, AssistKind}, base_db::AnchoredPathBuf, }; -use syntax::{ast, AstNode}; +use syntax::{ast, AstNode, ToSmolStr}; use crate::{ assist_context::{AssistContext, Assists}, @@ -39,7 +39,7 @@ pub(crate) fn move_from_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op } let target = source_file.syntax().text_range(); - let module_name = module.name(ctx.db())?.unescaped().display(ctx.db()).to_string(); + let module_name = module.name(ctx.db())?.as_str().to_smolstr(); let path = format!("../{module_name}.rs"); let dst = AnchoredPathBuf { anchor: ctx.file_id().into(), path }; acc.add( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs index 9692b70592912..bbf18e21948eb 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_module_to_file.rs @@ -61,7 +61,7 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) -> .string_value_unescape() .is_none() => { - format_to!(buf, "{}/", name.unescaped().display(db)) + format_to!(buf, "{}/", name.as_str()) } _ => (), } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs index 2925e2334b44d..7b38c795dc80f 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_to_mod_rs.rs @@ -2,7 +2,7 @@ use ide_db::{ assists::{AssistId, AssistKind}, base_db::AnchoredPathBuf, }; -use syntax::{ast, AstNode}; +use syntax::{ast, AstNode, ToSmolStr}; use crate::{ assist_context::{AssistContext, Assists}, @@ -39,7 +39,7 @@ pub(crate) fn move_to_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti } let target = source_file.syntax().text_range(); - let module_name = module.name(ctx.db())?.unescaped().display(ctx.db()).to_string(); + let module_name = module.name(ctx.db())?.as_str().to_smolstr(); let path = format!("./{module_name}/mod.rs"); let dst = AnchoredPathBuf { anchor: ctx.file_id().into(), path }; acc.add( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs index 849b8a42c6949..2a8465f634cfb 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/qualify_path.rs @@ -208,7 +208,7 @@ fn find_trait_method( if let Some(hir::AssocItem::Function(method)) = trait_.items(db).into_iter().find(|item: &hir::AssocItem| { item.name(db) - .map(|name| name.eq_ident(trait_method_name.text().as_str())) + .map(|name| name.as_str() == trait_method_name.text().trim_start_matches("r#")) .unwrap_or(false) }) { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs index 972303c2a0416..a79a82be45079 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_fields.rs @@ -110,7 +110,7 @@ fn compute_fields_ranks( .fields(ctx.db()) .into_iter() .enumerate() - .map(|(idx, field)| (field.name(ctx.db()).unescaped().display(ctx.db()).to_string(), idx)) + .map(|(idx, field)| (field.name(ctx.db()).as_str().to_owned(), idx)) .collect(); Some(res) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs index eb1d538f8743a..c3404173eafe6 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/reorder_impl_items.rs @@ -122,7 +122,7 @@ fn compute_item_ranks( .iter() .flat_map(|i| i.name(ctx.db())) .enumerate() - .map(|(idx, name)| (name.unescaped().display(ctx.db()).to_string(), idx)) + .map(|(idx, name)| (name.as_str().to_owned(), idx)) .collect(), ) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index 40669c65c5766..a22e7b272ea05 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -188,9 +188,6 @@ impl Completions { resolution: hir::ScopeDef, doc_aliases: Vec, ) { - if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) { - return; - } let is_private_editable = match ctx.def_is_visible(&resolution) { Visible::Yes => false, Visible::Editable => true, @@ -216,9 +213,6 @@ impl Completions { local_name: hir::Name, resolution: hir::ScopeDef, ) { - if !ctx.check_stability(resolution.attrs(ctx.db).as_deref()) { - return; - } let is_private_editable = match ctx.def_is_visible(&resolution) { Visible::Yes => false, Visible::Editable => true, @@ -241,7 +235,7 @@ impl Completions { path_ctx: &PathCompletionCtx, e: hir::Enum, ) { - if !ctx.check_stability(Some(&e.attrs(ctx.db))) { + if !ctx.check_stability_and_hidden(e) { return; } e.variants(ctx.db) @@ -257,9 +251,6 @@ impl Completions { local_name: hir::Name, doc_aliases: Vec, ) { - if !ctx.check_stability(Some(&module.attrs(ctx.db))) { - return; - } self.add_path_resolution( ctx, path_ctx, @@ -276,9 +267,6 @@ impl Completions { mac: hir::Macro, local_name: hir::Name, ) { - if !ctx.check_stability(Some(&mac.attrs(ctx.db))) { - return; - } let is_private_editable = match ctx.is_visible(&mac) { Visible::Yes => false, Visible::Editable => true, @@ -302,9 +290,6 @@ impl Completions { func: hir::Function, local_name: Option, ) { - if !ctx.check_stability(Some(&func.attrs(ctx.db))) { - return; - } let is_private_editable = match ctx.is_visible(&func) { Visible::Yes => false, Visible::Editable => true, @@ -332,9 +317,6 @@ impl Completions { receiver: Option, local_name: Option, ) { - if !ctx.check_stability(Some(&func.attrs(ctx.db))) { - return; - } let is_private_editable = match ctx.is_visible(&func) { Visible::Yes => false, Visible::Editable => true, @@ -362,9 +344,6 @@ impl Completions { func: hir::Function, import: LocatedImport, ) { - if !ctx.check_stability(Some(&func.attrs(ctx.db))) { - return; - } let is_private_editable = match ctx.is_visible(&func) { Visible::Yes => false, Visible::Editable => true, @@ -387,9 +366,6 @@ impl Completions { } pub(crate) fn add_const(&mut self, ctx: &CompletionContext<'_>, konst: hir::Const) { - if !ctx.check_stability(Some(&konst.attrs(ctx.db))) { - return; - } let is_private_editable = match ctx.is_visible(&konst) { Visible::Yes => false, Visible::Editable => true, @@ -406,9 +382,6 @@ impl Completions { ctx: &CompletionContext<'_>, type_alias: hir::TypeAlias, ) { - if !ctx.check_stability(Some(&type_alias.attrs(ctx.db))) { - return; - } let is_private_editable = match ctx.is_visible(&type_alias) { Visible::Yes => false, Visible::Editable => true, @@ -438,7 +411,7 @@ impl Completions { variant: hir::Variant, path: hir::ModPath, ) { - if !ctx.check_stability(Some(&variant.attrs(ctx.db))) { + if !ctx.check_stability_and_hidden(variant) { return; } if let Some(builder) = @@ -455,7 +428,7 @@ impl Completions { variant: hir::Variant, local_name: Option, ) { - if !ctx.check_stability(Some(&variant.attrs(ctx.db))) { + if !ctx.check_stability_and_hidden(variant) { return; } if let PathCompletionCtx { kind: PathKind::Pat { pat_ctx }, .. } = path_ctx { @@ -479,9 +452,6 @@ impl Completions { field: hir::Field, ty: &hir::Type, ) { - if !ctx.check_stability(Some(&field.attrs(ctx.db))) { - return; - } let is_private_editable = match ctx.is_visible(&field) { Visible::Yes => false, Visible::Editable => true, @@ -506,12 +476,18 @@ impl Completions { path: Option, local_name: Option, ) { - if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) { - return; - } - if let Some(builder) = - render_struct_literal(RenderContext::new(ctx), path_ctx, strukt, path, local_name) - { + let is_private_editable = match ctx.is_visible(&strukt) { + Visible::Yes => false, + Visible::Editable => true, + Visible::No => return, + }; + if let Some(builder) = render_struct_literal( + RenderContext::new(ctx).private_editable(is_private_editable), + path_ctx, + strukt, + path, + local_name, + ) { self.add(builder.build(ctx.db)); } } @@ -523,10 +499,17 @@ impl Completions { path: Option, local_name: Option, ) { - if !ctx.check_stability(Some(&un.attrs(ctx.db))) { - return; - } - let item = render_union_literal(RenderContext::new(ctx), un, path, local_name); + let is_private_editable = match ctx.is_visible(&un) { + Visible::Yes => false, + Visible::Editable => true, + Visible::No => return, + }; + let item = render_union_literal( + RenderContext::new(ctx).private_editable(is_private_editable), + un, + path, + local_name, + ); self.add_opt(item); } @@ -571,7 +554,7 @@ impl Completions { variant: hir::Variant, local_name: Option, ) { - if !ctx.check_stability(Some(&variant.attrs(ctx.db))) { + if !ctx.check_stability_and_hidden(variant) { return; } self.add_opt(render_variant_pat( @@ -591,7 +574,7 @@ impl Completions { variant: hir::Variant, path: hir::ModPath, ) { - if !ctx.check_stability(Some(&variant.attrs(ctx.db))) { + if !ctx.check_stability_and_hidden(variant) { return; } let path = Some(&path); @@ -612,10 +595,17 @@ impl Completions { strukt: hir::Struct, local_name: Option, ) { - if !ctx.check_stability(Some(&strukt.attrs(ctx.db))) { - return; - } - self.add_opt(render_struct_pat(RenderContext::new(ctx), pattern_ctx, strukt, local_name)); + let is_private_editable = match ctx.is_visible(&strukt) { + Visible::Yes => false, + Visible::Editable => true, + Visible::No => return, + }; + self.add_opt(render_struct_pat( + RenderContext::new(ctx).private_editable(is_private_editable), + pattern_ctx, + strukt, + local_name, + )); } pub(crate) fn suggest_name(&mut self, ctx: &CompletionContext<'_>, name: &str) { @@ -660,7 +650,7 @@ fn enum_variants_with_paths( if let Some(path) = ctx.module.find_path( ctx.db, hir::ModuleDef::from(variant), - ctx.config.import_path_config(), + ctx.config.import_path_config(ctx.is_nightly), ) { // Variants with trivial paths are already added by the existing completion logic, // so we should avoid adding these twice diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index 7679d9076ded2..d12654665ce95 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -42,31 +42,38 @@ pub(crate) fn complete_dot( item.detail("expr.await"); item.add_to(acc, ctx.db); - // Completions that skip `.await`, e.g. `.await.foo()`. - let dot_access_kind = match &dot_access.kind { - DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => { - DotAccessKind::Field { receiver_is_ambiguous_float_literal: false } - } - it @ DotAccessKind::Method { .. } => *it, - }; - let dot_access = DotAccess { - receiver: dot_access.receiver.clone(), - receiver_ty: Some(hir::TypeInfo { original: future_output.clone(), adjusted: None }), - kind: dot_access_kind, - ctx: dot_access.ctx, - }; - complete_fields( - acc, - ctx, - &future_output, - |acc, field, ty| acc.add_field(ctx, &dot_access, Some(await_str.clone()), field, &ty), - |acc, field, ty| acc.add_tuple_field(ctx, Some(await_str.clone()), field, &ty), - is_field_access, - is_method_access_with_parens, - ); - complete_methods(ctx, &future_output, &traits_in_scope, |func| { - acc.add_method(ctx, &dot_access, func, Some(await_str.clone()), None) - }); + if ctx.config.enable_auto_await { + // Completions that skip `.await`, e.g. `.await.foo()`. + let dot_access_kind = match &dot_access.kind { + DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => { + DotAccessKind::Field { receiver_is_ambiguous_float_literal: false } + } + it @ DotAccessKind::Method { .. } => *it, + }; + let dot_access = DotAccess { + receiver: dot_access.receiver.clone(), + receiver_ty: Some(hir::TypeInfo { + original: future_output.clone(), + adjusted: None, + }), + kind: dot_access_kind, + ctx: dot_access.ctx, + }; + complete_fields( + acc, + ctx, + &future_output, + |acc, field, ty| { + acc.add_field(ctx, &dot_access, Some(await_str.clone()), field, &ty) + }, + |acc, field, ty| acc.add_tuple_field(ctx, Some(await_str.clone()), field, &ty), + is_field_access, + is_method_access_with_parens, + ); + complete_methods(ctx, &future_output, &traits_in_scope, |func| { + acc.add_method(ctx, &dot_access, func, Some(await_str.clone()), None) + }); + } } complete_fields( @@ -82,39 +89,41 @@ pub(crate) fn complete_dot( acc.add_method(ctx, dot_access, func, None, None) }); - // FIXME: - // Checking for the existence of `iter()` is complicated in our setup, because we need to substitute - // its return type, so we instead check for `<&Self as IntoIterator>::IntoIter`. - // Does <&receiver_ty as IntoIterator>::IntoIter` exist? Assume `iter` is valid - let iter = receiver_ty - .strip_references() - .add_reference(hir::Mutability::Shared) - .into_iterator_iter(ctx.db) - .map(|ty| (ty, SmolStr::new_static("iter()"))); - // Does ::IntoIter` exist? - let into_iter = || { - receiver_ty - .clone() + if ctx.config.enable_auto_iter { + // FIXME: + // Checking for the existence of `iter()` is complicated in our setup, because we need to substitute + // its return type, so we instead check for `<&Self as IntoIterator>::IntoIter`. + // Does <&receiver_ty as IntoIterator>::IntoIter` exist? Assume `iter` is valid + let iter = receiver_ty + .strip_references() + .add_reference(hir::Mutability::Shared) .into_iterator_iter(ctx.db) - .map(|ty| (ty, SmolStr::new_static("into_iter()"))) - }; - if let Some((iter, iter_sym)) = iter.or_else(into_iter) { - // Skip iterators, e.g. complete `.iter().filter_map()`. - let dot_access_kind = match &dot_access.kind { - DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => { - DotAccessKind::Field { receiver_is_ambiguous_float_literal: false } - } - it @ DotAccessKind::Method { .. } => *it, + .map(|ty| (ty, SmolStr::new_static("iter()"))); + // Does ::IntoIter` exist? + let into_iter = || { + receiver_ty + .clone() + .into_iterator_iter(ctx.db) + .map(|ty| (ty, SmolStr::new_static("into_iter()"))) }; - let dot_access = DotAccess { - receiver: dot_access.receiver.clone(), - receiver_ty: Some(hir::TypeInfo { original: iter.clone(), adjusted: None }), - kind: dot_access_kind, - ctx: dot_access.ctx, - }; - complete_methods(ctx, &iter, &traits_in_scope, |func| { - acc.add_method(ctx, &dot_access, func, Some(iter_sym.clone()), None) - }); + if let Some((iter, iter_sym)) = iter.or_else(into_iter) { + // Skip iterators, e.g. complete `.iter().filter_map()`. + let dot_access_kind = match &dot_access.kind { + DotAccessKind::Field { receiver_is_ambiguous_float_literal: _ } => { + DotAccessKind::Field { receiver_is_ambiguous_float_literal: false } + } + it @ DotAccessKind::Method { .. } => *it, + }; + let dot_access = DotAccess { + receiver: dot_access.receiver.clone(), + receiver_ty: Some(hir::TypeInfo { original: iter.clone(), adjusted: None }), + kind: dot_access_kind, + ctx: dot_access.ctx, + }; + complete_methods(ctx, &iter, &traits_in_scope, |func| { + acc.add_method(ctx, &dot_access, func, Some(iter_sym.clone()), None) + }); + } } } @@ -1466,4 +1475,34 @@ async fn bar() { "#, ); } + + #[test] + fn receiver_without_deref_impl_completion() { + check_no_kw( + r#" +//- minicore: receiver +use core::ops::Receiver; + +struct Foo; + +impl Foo { + fn foo(self: Bar) {} +} + +struct Bar; + +impl Receiver for Bar { + type Target = Foo; +} + +fn main() { + let bar = Bar; + bar.$0 +} +"#, + expect![[r#" + me foo() fn(self: Bar) +"#]], + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs index db18b531d7c3c..e710175170199 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs @@ -247,7 +247,7 @@ pub(crate) fn complete_expr_path( .find_path( ctx.db, hir::ModuleDef::from(strukt), - ctx.config.import_path_config(), + ctx.config.import_path_config(ctx.is_nightly), ) .filter(|it| it.len() > 1); @@ -269,7 +269,7 @@ pub(crate) fn complete_expr_path( .find_path( ctx.db, hir::ModuleDef::from(un), - ctx.config.import_path_config(), + ctx.config.import_path_config(ctx.is_nightly), ) .filter(|it| it.len() > 1); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs index 73313eeaa6b79..24243f57b46a0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/flyimport.rs @@ -5,7 +5,7 @@ use ide_db::imports::{ insert_use::ImportScope, }; use itertools::Itertools; -use syntax::{ast, AstNode, SyntaxNode, ToSmolStr}; +use syntax::{ast, AstNode, SyntaxNode}; use crate::{ config::AutoImportExclusionType, @@ -257,7 +257,7 @@ fn import_on_the_fly( }; let user_input_lowercased = potential_import_name.to_lowercase(); - let import_cfg = ctx.config.import_path_config(); + let import_cfg = ctx.config.import_path_config(ctx.is_nightly); import_assets .search_for_imports(&ctx.sema, import_cfg, ctx.config.insert_use.prefix_kind) @@ -316,7 +316,7 @@ fn import_on_the_fly_pat_( ItemInNs::Values(def) => matches!(def, hir::ModuleDef::Const(_)), }; let user_input_lowercased = potential_import_name.to_lowercase(); - let cfg = ctx.config.import_path_config(); + let cfg = ctx.config.import_path_config(ctx.is_nightly); import_assets .search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind) @@ -358,7 +358,7 @@ fn import_on_the_fly_method( let user_input_lowercased = potential_import_name.to_lowercase(); - let cfg = ctx.config.import_path_config(); + let cfg = ctx.config.import_path_config(ctx.is_nightly); import_assets .search_for_imports(&ctx.sema, cfg, ctx.config.insert_use.prefix_kind) @@ -444,7 +444,7 @@ fn compute_fuzzy_completion_order_key( cov_mark::hit!(certain_fuzzy_order_test); let import_name = match proposed_mod_path.segments().last() { // FIXME: nasty alloc, this is a hot path! - Some(name) => name.unescaped().display_no_db().to_smolstr().to_ascii_lowercase(), + Some(name) => name.as_str().to_ascii_lowercase(), None => return usize::MAX, }; match import_name.match_indices(user_input_lowercased).next() { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index 6d1945c45341d..831f5665f4aa0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -31,7 +31,7 @@ //! } //! ``` -use hir::{db::ExpandDatabase, HasAttrs, MacroFileId, Name}; +use hir::{db::ExpandDatabase, MacroFileId, Name}; use ide_db::text_edit::TextEdit; use ide_db::{ documentation::HasDocs, path_transform::PathTransform, @@ -85,7 +85,7 @@ fn complete_trait_impl_name( name: &Option, kind: ImplCompletionKind, ) -> Option<()> { - let item = match name { + let macro_file_item = match name { Some(name) => name.syntax().parent(), None => { let token = &ctx.token; @@ -96,12 +96,12 @@ fn complete_trait_impl_name( .parent() } }?; - let item = ctx.sema.original_syntax_node_rooted(&item)?; + let real_file_item = ctx.sema.original_syntax_node_rooted(¯o_file_item)?; // item -> ASSOC_ITEM_LIST -> IMPL - let impl_def = ast::Impl::cast(item.parent()?.parent()?)?; + let impl_def = ast::Impl::cast(macro_file_item.parent()?.parent()?)?; let replacement_range = { // ctx.sema.original_ast_node(item)?; - let first_child = item + let first_child = real_file_item .children_with_tokens() .find(|child| { !matches!( @@ -109,7 +109,7 @@ fn complete_trait_impl_name( SyntaxKind::COMMENT | SyntaxKind::WHITESPACE | SyntaxKind::ATTR ) }) - .unwrap_or_else(|| SyntaxElement::Node(item.clone())); + .unwrap_or_else(|| SyntaxElement::Node(real_file_item.clone())); TextRange::new(first_child.text_range().start(), ctx.source_range().end()) }; @@ -133,8 +133,11 @@ pub(crate) fn complete_trait_impl_item_by_name( acc, ctx, ImplCompletionKind::All, - match name_ref { - Some(name) => name.syntax().text_range(), + match name_ref + .as_ref() + .and_then(|name| ctx.sema.original_syntax_node_rooted(name.syntax())) + { + Some(name) => name.text_range(), None => ctx.source_range(), }, impl_, @@ -152,7 +155,7 @@ fn complete_trait_impl( if let Some(hir_impl) = ctx.sema.to_def(impl_def) { get_missing_assoc_items(&ctx.sema, impl_def) .into_iter() - .filter(|item| ctx.check_stability(Some(&item.attrs(ctx.db)))) + .filter(|item| ctx.check_stability_and_hidden(*item)) .for_each(|item| { use self::ImplCompletionKind::*; match (item, kind) { @@ -359,7 +362,7 @@ fn add_type_alias_impl( type_alias: hir::TypeAlias, impl_def: hir::Impl, ) { - let alias_name = type_alias.name(ctx.db).unescaped().display(ctx.db).to_smolstr(); + let alias_name = type_alias.name(ctx.db).as_str().to_smolstr(); let label = format_smolstr!("type {alias_name} ="); @@ -516,7 +519,7 @@ fn function_declaration( mod tests { use expect_test::expect; - use crate::tests::{check_edit, check_no_kw}; + use crate::tests::{check, check_edit, check_no_kw}; #[test] fn no_completion_inside_fn() { @@ -1639,4 +1642,51 @@ impl DesugaredAsyncTrait for () { "#, ); } + + #[test] + fn within_attr_macro() { + check( + r#" +//- proc_macros: identity +trait Trait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +#[proc_macros::identity] +impl Trait for () { + f$0 +} + "#, + expect![[r#" + me fn bar(..) + me fn baz(..) + me fn foo(..) + md proc_macros + kw crate:: + kw self:: + "#]], + ); + check( + r#" +//- proc_macros: identity +trait Trait { + fn foo(&self) {} + fn bar(&self) {} + fn baz(&self) {} +} + +#[proc_macros::identity] +impl Trait for () { + fn $0 +} + "#, + expect![[r#" + me fn bar(..) + me fn baz(..) + me fn foo(..) + "#]], + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs index bafe32942098c..cca6a22f290d2 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/mod_.rs @@ -7,7 +7,7 @@ use ide_db::{ base_db::{SourceRootDatabase, VfsPath}, FxHashSet, RootDatabase, SymbolKind, }; -use syntax::{ast, AstNode, SyntaxKind, ToSmolStr}; +use syntax::{ast, AstNode, SyntaxKind}; use crate::{context::CompletionContext, CompletionItem, Completions}; @@ -140,9 +140,7 @@ fn directory_to_look_for_submodules( module_chain_to_containing_module_file(module, db) .into_iter() .filter_map(|module| module.name(db)) - .try_fold(base_directory, |path, name| { - path.join(&name.unescaped().display_no_db().to_smolstr()) - }) + .try_fold(base_directory, |path, name| path.join(name.as_str())) } fn module_chain_to_containing_module_file( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index 67ea05e002b73..2c39a8fdfed73 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs @@ -60,7 +60,7 @@ pub(crate) fn complete_postfix( None => return, }; - let cfg = ctx.config.import_path_config(); + let cfg = ctx.config.import_path_config(ctx.is_nightly); if let Some(drop_trait) = ctx.famous_defs().core_ops_Drop() { if receiver_ty.impls_trait(ctx.db, drop_trait, &[]) { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs index 9d62622add206..b384987c51ce1 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/use_.rs @@ -52,8 +52,14 @@ pub(crate) fn complete_use_path( ) }; for (name, def) in module_scope { - if !ctx.check_stability(def.attrs(ctx.db).as_deref()) { - continue; + if let (Some(attrs), Some(defining_crate)) = + (def.attrs(ctx.db), def.krate(ctx.db)) + { + if !ctx.check_stability(Some(&attrs)) + || ctx.is_doc_hidden(&attrs, defining_crate) + { + continue; + } } let is_name_already_imported = already_imported_names.contains(name.as_str()); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs index 8b1ce11e8a45f..45aab38e8ea09 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs @@ -14,6 +14,8 @@ pub struct CompletionConfig<'a> { pub enable_postfix_completions: bool, pub enable_imports_on_the_fly: bool, pub enable_self_on_the_fly: bool, + pub enable_auto_iter: bool, + pub enable_auto_await: bool, pub enable_private_editable: bool, pub enable_term_search: bool, pub term_search_fuel: u64, @@ -57,11 +59,12 @@ impl CompletionConfig<'_> { .flat_map(|snip| snip.prefix_triggers.iter().map(move |trigger| (&**trigger, snip))) } - pub fn import_path_config(&self) -> ImportPathConfig { + pub fn import_path_config(&self, allow_unstable: bool) -> ImportPathConfig { ImportPathConfig { prefer_no_std: self.prefer_no_std, prefer_prelude: self.prefer_prelude, prefer_absolute: self.prefer_absolute, + allow_unstable, } } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index 3a2a4a23a1987..2f1860cbb59af 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -443,7 +443,9 @@ pub(crate) struct CompletionContext<'a> { /// The module of the `scope`. pub(crate) module: hir::Module, /// Whether nightly toolchain is used. Cached since this is looked up a lot. - is_nightly: bool, + pub(crate) is_nightly: bool, + /// The edition of the current crate + // FIXME: This should probably be the crate of the current token? pub(crate) edition: Edition, /// The expected name of what we are completing. @@ -532,7 +534,7 @@ impl CompletionContext<'_> { } } - /// Checks if an item is visible and not `doc(hidden)` at the completion site. + /// Checks if an item is visible, not `doc(hidden)` and stable at the completion site. pub(crate) fn is_visible(&self, item: &I) -> Visible where I: hir::HasVisibility + hir::HasAttrs + hir::HasCrate + Copy, @@ -568,6 +570,15 @@ impl CompletionContext<'_> { !attrs.is_unstable() || self.is_nightly } + pub(crate) fn check_stability_and_hidden(&self, item: I) -> bool + where + I: hir::HasAttrs + hir::HasCrate, + { + let defining_crate = item.krate(self.db); + let attrs = item.attrs(self.db); + self.check_stability(Some(&attrs)) && !self.is_doc_hidden(&attrs, defining_crate) + } + /// Whether the given trait is an operator trait or not. pub(crate) fn is_ops_trait(&self, trait_: hir::Trait) -> bool { match trait_.attrs(self.db).lang() { @@ -645,6 +656,10 @@ impl CompletionContext<'_> { attrs: &hir::Attrs, defining_crate: hir::Crate, ) -> Visible { + if !self.check_stability(Some(attrs)) { + return Visible::No; + } + if !vis.is_visible_from(self.db, self.module.into()) { if !self.config.enable_private_editable { return Visible::No; @@ -664,7 +679,7 @@ impl CompletionContext<'_> { } } - fn is_doc_hidden(&self, attrs: &hir::Attrs, defining_crate: hir::Crate) -> bool { + pub(crate) fn is_doc_hidden(&self, attrs: &hir::Attrs, defining_crate: hir::Crate) -> bool { // `doc(hidden)` items are only completed within the defining crate. self.krate != defining_crate && attrs.has_doc_hidden() } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index 3c4d489c0ff88..f5a50ae81907f 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -5,7 +5,7 @@ use hir::{ExpandResult, Semantics, Type, TypeInfo, Variant}; use ide_db::{active_parameter::ActiveParameter, RootDatabase}; use itertools::Either; use syntax::{ - algo::{ancestors_at_offset, find_node_at_offset, non_trivia_sibling}, + algo::{self, ancestors_at_offset, find_node_at_offset, non_trivia_sibling}, ast::{ self, AttrKind, HasArgList, HasGenericArgs, HasGenericParams, HasLoopBody, HasName, NameOrNameRef, @@ -85,6 +85,11 @@ pub(super) fn expand_and_analyze( }) } +fn token_at_offset_ignore_whitespace(file: &SyntaxNode, offset: TextSize) -> Option { + let token = file.token_at_offset(offset).left_biased()?; + algo::skip_whitespace_token(token, Direction::Prev) +} + /// Expand attributes and macro calls at the current cursor position for both the original file /// and fake file repeatedly. As soon as one of the two expansions fail we stop so the original /// and speculative states stay in sync. @@ -125,9 +130,7 @@ fn expand( // Left biased since there may already be an identifier token there, and we appended to it. if !sema.might_be_inside_macro_call(&fake_ident_token) - && original_file - .token_at_offset(original_offset + relative_offset) - .left_biased() + && token_at_offset_ignore_whitespace(&original_file, original_offset + relative_offset) .is_some_and(|original_token| !sema.might_be_inside_macro_call(&original_token)) { // Recursion base case. @@ -143,9 +146,11 @@ fn expand( let parent_item = |item: &ast::Item| item.syntax().ancestors().skip(1).find_map(ast::Item::cast); + let original_node = token_at_offset_ignore_whitespace(&original_file, original_offset) + .and_then(|token| token.parent_ancestors().find_map(ast::Item::cast)); let ancestor_items = iter::successors( Option::zip( - find_node_at_offset::(&original_file, original_offset), + original_node, find_node_at_offset::( &speculative_file, fake_ident_token.text_range().start(), @@ -1590,11 +1595,11 @@ fn pattern_context_for( }).map(|enum_| enum_.variants(sema.db)) }) }).map(|variants| variants.iter().filter_map(|variant| { - let variant_name = variant.name(sema.db).unescaped().display(sema.db).to_string(); + let variant_name = variant.name(sema.db); let variant_already_present = match_arm_list.arms().any(|arm| { arm.pat().and_then(|pat| { - let pat_already_present = pat.syntax().to_string().contains(&variant_name); + let pat_already_present = pat.syntax().to_string().contains(variant_name.as_str()); pat_already_present.then_some(pat_already_present) }).is_some() }); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index dc2f9a7680293..41a82409597bd 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -82,8 +82,7 @@ pub struct CompletionItem { pub ref_match: Option<(CompletionItemRefMode, TextSize)>, /// The import data to add to completion's edits. - /// (ImportPath, LastSegment) - pub import_to_add: SmallVec<[(String, String); 1]>, + pub import_to_add: SmallVec<[String; 1]>, } #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)] @@ -181,6 +180,8 @@ pub struct CompletionRelevance { pub postfix_match: Option, /// This is set for items that are function (associated or method) pub function: Option, + /// true when there is an `await.method()` or `iter().method()` completion. + pub is_skipping_completion: bool, } #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct CompletionRelevanceTraitInfo { @@ -269,6 +270,7 @@ impl CompletionRelevance { postfix_match, trait_, function, + is_skipping_completion, } = self; // only applicable for completions within use items @@ -296,6 +298,12 @@ impl CompletionRelevance { score -= 5; } } + + // Lower rank for completions that skip `await` and `iter()`. + if is_skipping_completion { + score -= 7; + } + // lower rank for items that need an import if requires_import { score -= 1; @@ -561,12 +569,7 @@ impl Builder { let import_to_add = self .imports_to_add .into_iter() - .filter_map(|import| { - Some(( - import.import_path.display(db, self.edition).to_string(), - import.import_path.segments().last()?.display(db, self.edition).to_string(), - )) - }) + .map(|import| import.import_path.display(db, self.edition).to_string()) .collect(); CompletionItem { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs index 56d7eeaf8ea03..8051d48ca5fe0 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs @@ -10,17 +10,13 @@ mod snippet; #[cfg(test)] mod tests; -use ide_db::text_edit::TextEdit; use ide_db::{ - helpers::mod_path_to_ast, - imports::{ - import_assets::NameToImport, - insert_use::{self, ImportScope}, - }, - items_locator, + imports::insert_use::{self, ImportScope}, syntax_helpers::tree_diff::diff, + text_edit::TextEdit, FilePosition, FxHashSet, RootDatabase, }; +use syntax::ast::make; use crate::{ completions::Completions, @@ -272,7 +268,7 @@ pub fn resolve_completion_edits( db: &RootDatabase, config: &CompletionConfig<'_>, FilePosition { file_id, offset }: FilePosition, - imports: impl IntoIterator, + imports: impl IntoIterator, ) -> Option> { let _p = tracing::info_span!("resolve_completion_edits").entered(); let sema = hir::Semantics::new(db); @@ -289,27 +285,12 @@ pub fn resolve_completion_edits( let new_ast = scope.clone_for_update(); let mut import_insert = TextEdit::builder(); - let cfg = config.import_path_config(); - - imports.into_iter().for_each(|(full_import_path, imported_name)| { - let items_with_name = items_locator::items_with_name( - &sema, - current_crate, - NameToImport::exact_case_sensitive(imported_name), - items_locator::AssocSearchMode::Include, + imports.into_iter().for_each(|full_import_path| { + insert_use::insert_use( + &new_ast, + make::path_from_text_with_edition(&full_import_path, current_edition), + &config.insert_use, ); - let import = items_with_name - .filter_map(|candidate| { - current_module.find_use_path(db, candidate, config.insert_use.prefix_kind, cfg) - }) - .find(|mod_path| mod_path.display(db, current_edition).to_string() == full_import_path); - if let Some(import_path) = import { - insert_use::insert_use( - &new_ast, - mod_path_to_ast(&import_path, current_edition), - &config.insert_use, - ); - } }); diff(scope.as_syntax_node(), new_ast.as_syntax_node()).into_text_edit(&mut import_insert); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index 61e8114d381aa..dc7eacbfbafdb 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -130,10 +130,8 @@ pub(crate) fn render_field( let db = ctx.db(); let is_deprecated = ctx.is_deprecated(field); let name = field.name(db); - let (name, escaped_name) = ( - name.unescaped().display(db).to_smolstr(), - name.display_no_db(ctx.completion.edition).to_smolstr(), - ); + let (name, escaped_name) = + (name.as_str().to_smolstr(), name.display_no_db(ctx.completion.edition).to_smolstr()); let mut item = CompletionItem::new( SymbolKind::Field, ctx.source_range(), @@ -142,7 +140,8 @@ pub(crate) fn render_field( ); item.set_relevance(CompletionRelevance { type_match: compute_type_match(ctx.completion, ty), - exact_name_match: compute_exact_name_match(ctx.completion, name.as_str()), + exact_name_match: compute_exact_name_match(ctx.completion, &name), + is_skipping_completion: receiver.is_some(), ..CompletionRelevance::default() }); item.detail(ty.display(db, ctx.completion.edition).to_string()) @@ -215,6 +214,10 @@ pub(crate) fn render_tuple_field( ); item.detail(ty.display(ctx.db(), ctx.completion.edition).to_string()) .lookup_by(field.to_string()); + item.set_relevance(CompletionRelevance { + is_skipping_completion: receiver.is_some(), + ..ctx.completion_relevance() + }); item.build(ctx.db()) } @@ -298,7 +301,7 @@ pub(crate) fn render_expr( .unwrap_or_else(|| String::from("...")) }; - let cfg = ctx.config.import_path_config(); + let cfg = ctx.config.import_path_config(ctx.is_nightly); let label = expr.gen_source_code(&ctx.scope, &mut label_formatter, cfg, ctx.edition).ok()?; @@ -512,7 +515,7 @@ fn render_resolution_simple_( let mut item = CompletionItem::new( kind, ctx.source_range(), - local_name.unescaped().display(db).to_smolstr(), + local_name.as_str().to_smolstr(), ctx.completion.edition, ); item.set_relevance(ctx.completion_relevance()) @@ -1335,6 +1338,7 @@ fn main() { let _: m::Spam = S$0 } is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, trigger_call_info: true, }, @@ -1364,6 +1368,7 @@ fn main() { let _: m::Spam = S$0 } is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, trigger_call_info: true, }, @@ -1453,6 +1458,7 @@ fn foo() { A { the$0 } } is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, }, ] @@ -1511,6 +1517,7 @@ impl S { return_type: Other, }, ), + is_skipping_completion: false, }, }, CompletionItem { @@ -1653,6 +1660,7 @@ fn foo(s: S) { s.$0 } return_type: Other, }, ), + is_skipping_completion: false, }, }, ] @@ -1864,6 +1872,7 @@ fn f() -> i32 { is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, }, ] @@ -2624,6 +2633,7 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 } return_type: Other, }, ), + is_skipping_completion: false, }, ref_match: "&@107", }, @@ -2709,6 +2719,7 @@ fn foo() { is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, }, ] @@ -2766,6 +2777,7 @@ fn main() { return_type: Other, }, ), + is_skipping_completion: false, }, ref_match: "&@92", }, @@ -3140,6 +3152,7 @@ fn main() { is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, }, CompletionItem { @@ -3173,6 +3186,7 @@ fn main() { is_private_editable: false, postfix_match: None, function: None, + is_skipping_completion: false, }, }, ] diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs index 415d87c6239b4..e357ab24d22df 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/const_.rs @@ -14,10 +14,8 @@ pub(crate) fn render_const(ctx: RenderContext<'_>, const_: hir::Const) -> Option fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option { let db = ctx.db(); let name = const_.name(db)?; - let (name, escaped_name) = ( - name.unescaped().display(db).to_smolstr(), - name.display(db, ctx.completion.edition).to_smolstr(), - ); + let (name, escaped_name) = + (name.as_str().to_smolstr(), name.display(db, ctx.completion.edition).to_smolstr()); let detail = const_.display(db, ctx.completion.edition).to_string(); let mut item = diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index 3b97d67169eca..c3354902c3b78 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -59,13 +59,10 @@ fn render( let (call, escaped_call) = match &func_kind { FuncKind::Method(_, Some(receiver)) => ( - format_smolstr!("{}.{}", receiver, name.unescaped().display(ctx.db())), + format_smolstr!("{}.{}", receiver, name.as_str()), format_smolstr!("{}.{}", receiver, name.display(ctx.db(), completion.edition)), ), - _ => ( - name.unescaped().display(db).to_smolstr(), - name.display(db, completion.edition).to_smolstr(), - ), + _ => (name.as_str().to_smolstr(), name.display(db, completion.edition).to_smolstr()), }; let has_self_param = func.self_param(db).is_some(); let mut item = CompletionItem::new( @@ -126,6 +123,7 @@ fn render( exact_name_match: compute_exact_name_match(completion, &call), function, trait_: trait_info, + is_skipping_completion: matches!(func_kind, FuncKind::Method(_, Some(_))), ..ctx.completion_relevance() }); @@ -151,7 +149,7 @@ fn render( item.set_documentation(ctx.docs(func)) .set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func)) .detail(detail) - .lookup_by(name.unescaped().display(db).to_smolstr()); + .lookup_by(name.as_str().to_smolstr()); if let Some((cap, (self_param, params))) = complete_call_parens { add_call_parens( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs index c71356e5300fc..aab54ca5e0146 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/literal.rs @@ -75,7 +75,7 @@ fn render( None => (name.clone().into(), name.into(), false), }; let (qualified_name, escaped_qualified_name) = ( - qualified_name.unescaped().display(ctx.db()).to_string(), + qualified_name.display_verbatim(ctx.db()).to_string(), qualified_name.display(ctx.db(), completion.edition).to_string(), ); let snippet_cap = ctx.snippet_cap(); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs index 6490171fbb48b..e265e92f9794b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/macro_.rs @@ -46,21 +46,19 @@ fn render( ctx.source_range() }; - let (name, escaped_name) = ( - name.unescaped().display(ctx.db()).to_smolstr(), - name.display(ctx.db(), completion.edition).to_smolstr(), - ); + let (name, escaped_name) = + (name.as_str(), name.display(ctx.db(), completion.edition).to_smolstr()); let docs = ctx.docs(macro_); let docs_str = docs.as_ref().map(Documentation::as_str).unwrap_or_default(); let is_fn_like = macro_.is_fn_like(completion.db); - let (bra, ket) = if is_fn_like { guess_macro_braces(&name, docs_str) } else { ("", "") }; + let (bra, ket) = if is_fn_like { guess_macro_braces(name, docs_str) } else { ("", "") }; let needs_bang = is_fn_like && !is_use_path && !has_macro_bang; let mut item = CompletionItem::new( SymbolKind::from(macro_.kind(completion.db)), source_range, - label(&ctx, needs_bang, bra, ket, &name), + label(&ctx, needs_bang, bra, ket, &name.to_smolstr()), completion.edition, ); item.set_deprecated(ctx.is_deprecated(macro_)) @@ -71,11 +69,11 @@ fn render( match ctx.snippet_cap() { Some(cap) if needs_bang && !has_call_parens => { let snippet = format!("{escaped_name}!{bra}$0{ket}"); - let lookup = banged_name(&name); + let lookup = banged_name(name); item.insert_snippet(cap, snippet).lookup_by(lookup); } _ if needs_bang => { - item.insert_text(banged_name(&escaped_name)).lookup_by(banged_name(&name)); + item.insert_text(banged_name(&escaped_name)).lookup_by(banged_name(name)); } _ => { cov_mark::hit!(dont_insert_macro_call_parens_unnecessary); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs index 5675dfb5c6ff2..124abb17b6a1c 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/pattern.rs @@ -31,13 +31,11 @@ pub(crate) fn render_struct_pat( } let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())); - let (name, escaped_name) = ( - name.unescaped().display(ctx.db()).to_smolstr(), - name.display(ctx.db(), ctx.completion.edition).to_smolstr(), - ); + let (name, escaped_name) = + (name.as_str(), name.display(ctx.db(), ctx.completion.edition).to_smolstr()); let kind = strukt.kind(ctx.db()); - let label = format_literal_label(name.as_str(), kind, ctx.snippet_cap()); - let lookup = format_literal_lookup(name.as_str(), kind); + let label = format_literal_label(name, kind, ctx.snippet_cap()); + let lookup = format_literal_lookup(name, kind); let pat = render_pat(&ctx, pattern_ctx, &escaped_name, kind, &visible_fields, fields_omitted)?; let db = ctx.db(); @@ -61,13 +59,13 @@ pub(crate) fn render_variant_pat( let (name, escaped_name) = match path { Some(path) => ( - path.unescaped().display(ctx.db()).to_string().into(), - path.display(ctx.db(), ctx.completion.edition).to_string().into(), + path.display_verbatim(ctx.db()).to_smolstr(), + path.display(ctx.db(), ctx.completion.edition).to_smolstr(), ), None => { let name = local_name.unwrap_or_else(|| variant.name(ctx.db())); let it = ( - name.unescaped().display(ctx.db()).to_smolstr(), + name.as_str().to_smolstr(), name.display(ctx.db(), ctx.completion.edition).to_smolstr(), ); it diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs index 09eb19201c5b0..1b952f31360c2 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/type_alias.rs @@ -32,14 +32,11 @@ fn render( let name = type_alias.name(db); let (name, escaped_name) = if with_eq { ( - SmolStr::from_iter([&name.unescaped().display(db).to_smolstr(), " = "]), + SmolStr::from_iter([&name.as_str().to_smolstr(), " = "]), SmolStr::from_iter([&name.display_no_db(ctx.completion.edition).to_smolstr(), " = "]), ) } else { - ( - name.unescaped().display(db).to_smolstr(), - name.display_no_db(ctx.completion.edition).to_smolstr(), - ) + (name.as_str().to_smolstr(), name.display_no_db(ctx.completion.edition).to_smolstr()) }; let detail = type_alias.display(db, ctx.completion.edition).to_string(); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs index e053e299d903a..742036265211e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/union_literal.rs @@ -23,12 +23,12 @@ pub(crate) fn render_union_literal( let (qualified_name, escaped_qualified_name) = match path { Some(p) => ( - p.unescaped().display(ctx.db()).to_string(), - p.display(ctx.db(), ctx.completion.edition).to_string(), + p.display_verbatim(ctx.db()).to_smolstr(), + p.display(ctx.db(), ctx.completion.edition).to_smolstr(), ), None => ( - name.unescaped().display(ctx.db()).to_string(), - name.display(ctx.db(), ctx.completion.edition).to_string(), + name.as_str().to_smolstr(), + name.display(ctx.db(), ctx.completion.edition).to_smolstr(), ), }; let label = format_literal_label( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs index 04bb178c658f3..866b83a614603 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/snippet.rs @@ -164,7 +164,7 @@ impl Snippet { } fn import_edits(ctx: &CompletionContext<'_>, requires: &[ModPath]) -> Option> { - let import_cfg = ctx.config.import_path_config(); + let import_cfg = ctx.config.import_path_config(ctx.is_nightly); let resolve = |import| { let item = ctx.scope.resolve_mod_path(import).next()?; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index b7dbf0a6306c6..9d91f95eb65b8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -87,6 +87,8 @@ pub(crate) const TEST_CONFIG: CompletionConfig<'_> = CompletionConfig { fields_to_resolve: CompletionFieldsToResolve::empty(), exclude_flyimport: vec![], exclude_traits: &[], + enable_auto_await: true, + enable_auto_iter: true, }; pub(crate) fn completion_list(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> String { diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs index e117dbf4bdf0b..663a038580d55 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs @@ -1965,3 +1965,24 @@ fn bar() { "#]], ); } + +#[test] +fn doc_hidden_enum_variant() { + check( + r#" +//- /foo.rs crate:foo +pub enum Enum { + #[doc(hidden)] Hidden, + Visible, +} + +//- /lib.rs crate:lib deps:foo +fn foo() { + let _ = foo::Enum::$0; +} + "#, + expect![[r#" + ev Visible Visible + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index d491e438feffc..2e7c53def7fc5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -1390,6 +1390,41 @@ pub struct FooStruct {} ); } +#[test] +fn flyimport_pattern_unstable_path() { + check( + r#" +//- /main.rs crate:main deps:std +fn function() { + let foo$0 +} +//- /std.rs crate:std +#[unstable] +pub mod unstable { + pub struct FooStruct {} +} +"#, + expect![""], + ); + check( + r#" +//- toolchain:nightly +//- /main.rs crate:main deps:std +fn function() { + let foo$0 +} +//- /std.rs crate:std +#[unstable] +pub mod unstable { + pub struct FooStruct {} +} +"#, + expect![[r#" + st FooStruct (use std::unstable::FooStruct) + "#]], + ); +} + #[test] fn flyimport_pattern_unstable_item_on_nightly() { check( diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs index 04b3a47a64dab..593b1edde5cc5 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/use_tree.rs @@ -451,3 +451,20 @@ marco_rules! m { () => {} } "#]], ); } + +#[test] +fn use_tree_doc_hidden() { + check( + r#" +//- /foo.rs crate:foo +#[doc(hidden)] pub struct Hidden; +pub struct Visible; + +//- /lib.rs crate:lib deps:foo +use foo::$0; + "#, + expect![[r#" + st Visible Visible + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index 49d26dfe25c12..d12bda0816fd3 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -794,7 +794,7 @@ impl NameRefClass { hir::AssocItem::TypeAlias(it) => Some(it), _ => None, }) - .find(|alias| alias.name(sema.db).eq_ident(name_ref.text().as_str())) + .find(|alias| alias.name(sema.db).as_str() == name_ref.text().trim_start_matches("r#")) { // No substitution, this can only occur in type position. return Some(NameRefClass::Definition(Definition::TypeAlias(ty), None)); diff --git a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs index 9e3506d6f53b7..2f4d07446f2c1 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs @@ -46,6 +46,10 @@ impl FamousDefs<'_, '_> { self.find_trait("core:cmp:Ord") } + pub fn core_convert_FromStr(&self) -> Option { + self.find_trait("core:str:FromStr") + } + pub fn core_convert_From(&self) -> Option { self.find_trait("core:convert:From") } @@ -54,6 +58,14 @@ impl FamousDefs<'_, '_> { self.find_trait("core:convert:Into") } + pub fn core_convert_TryFrom(&self) -> Option { + self.find_trait("core:convert:TryFrom") + } + + pub fn core_convert_TryInto(&self) -> Option { + self.find_trait("core:convert:TryInto") + } + pub fn core_convert_Index(&self) -> Option { self.find_trait("core:ops:Index") } @@ -130,6 +142,13 @@ impl FamousDefs<'_, '_> { self.find_macro("core:unimplemented") } + pub fn core_fmt_Display(&self) -> Option { + self.find_trait("core:fmt:Display") + } + + pub fn alloc_string_ToString(&self) -> Option { + self.find_trait("alloc:string:ToString") + } pub fn builtin_crates(&self) -> impl Iterator { IntoIterator::into_iter([ self.std(), @@ -202,14 +221,15 @@ impl FamousDefs<'_, '_> { for segment in path { module = module.children(db).find_map(|child| { let name = child.name(db)?; - if name.eq_ident(segment) { + if name.as_str() == segment { Some(child) } else { None } })?; } - let def = module.scope(db, None).into_iter().find(|(name, _def)| name.eq_ident(trait_))?.1; + let def = + module.scope(db, None).into_iter().find(|(name, _def)| name.as_str() == trait_)?.1; Some(def) } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs index a045c22c2dff1..f045e44dd318b 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs @@ -319,6 +319,7 @@ impl Ctx<'_> { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }; let found_path = self.target_module.find_path( self.source_scope.db.upcast(), @@ -378,6 +379,7 @@ impl Ctx<'_> { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }; let found_path = self.target_module.find_path(self.source_scope.db.upcast(), def, cfg)?; @@ -417,6 +419,7 @@ impl Ctx<'_> { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }; let found_path = self.target_module.find_path( self.source_scope.db.upcast(), diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs index 42efbd68e33d3..59914bedde434 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs @@ -263,13 +263,12 @@ fn rename_mod( // - Module has submodules defined in separate files let dir_paths = match (is_mod_rs, has_detached_child, module.name(sema.db)) { // Go up one level since the anchor is inside the dir we're trying to rename - (true, _, Some(mod_name)) => Some(( - format!("../{}", mod_name.unescaped().display(sema.db)), - format!("../{new_name}"), - )), + (true, _, Some(mod_name)) => { + Some((format!("../{}", mod_name.as_str()), format!("../{new_name}"))) + } // The anchor is on the same level as target dir (false, true, Some(mod_name)) => { - Some((mod_name.unescaped().display(sema.db).to_string(), new_name.to_owned())) + Some((mod_name.as_str().to_owned(), new_name.to_owned())) } _ => None, }; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index a75aba137be6c..7fc563a424104 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -625,7 +625,7 @@ impl<'a> FindUsages<'a> { let _p = tracing::info_span!("collect_possible_aliases").entered(); let db = sema.db; - let container_name = container.name(db).unescaped().display(db).to_smolstr(); + let container_name = container.name(db).as_str().to_smolstr(); let search_scope = Definition::from(container).search_scope(db); let mut seen = FxHashSet::default(); let mut completed = FxHashSet::default(); @@ -925,12 +925,8 @@ impl<'a> FindUsages<'a> { .or_else(|| ty.as_builtin().map(|builtin| builtin.name())) }) }; - // We need to unescape the name in case it is written without "r#" in earlier - // editions of Rust where it isn't a keyword. - self.def - .name(sema.db) - .or_else(self_kw_refs) - .map(|it| it.unescaped().display(sema.db).to_smolstr()) + // We need to search without the `r#`, hence `as_str` access. + self.def.name(sema.db).or_else(self_kw_refs).map(|it| it.as_str().to_smolstr()) } }; let name = match &name { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index c94644eeb89be..e5ce10a771ef9 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -143,7 +143,7 @@ fn library_symbols(db: &dyn SymbolsDatabase, source_root_id: SourceRootId) -> Ar fn module_symbols(db: &dyn SymbolsDatabase, module: Module) -> Arc { let _p = tracing::info_span!("module_symbols").entered(); - Arc::new(SymbolIndex::new(SymbolCollector::collect_module(db.upcast(), module))) + Arc::new(SymbolIndex::new(SymbolCollector::new_module(db.upcast(), module))) } pub fn crate_symbols(db: &dyn SymbolsDatabase, krate: Crate) -> Box<[Arc]> { @@ -284,13 +284,15 @@ impl SymbolIndex { builder.insert(key, value).unwrap(); } - // FIXME: fst::Map should ideally have a way to shrink the backing buffer without the unwrap dance - let map = fst::Map::new({ - let mut buf = builder.into_inner().unwrap(); - buf.shrink_to_fit(); - buf - }) - .unwrap(); + let map = builder + .into_inner() + .and_then(|mut buf| { + fst::Map::new({ + buf.shrink_to_fit(); + buf + }) + }) + .unwrap(); SymbolIndex { symbols, map } } @@ -491,7 +493,7 @@ pub(self) use crate::Trait as IsThisJustATrait; .modules(&db) .into_iter() .map(|module_id| { - let mut symbols = SymbolCollector::collect_module(&db, module_id); + let mut symbols = SymbolCollector::new_module(&db, module_id); symbols.sort_by_key(|it| it.name.as_str().to_owned()); (module_id, symbols) }) @@ -518,7 +520,7 @@ struct Duplicate; .modules(&db) .into_iter() .map(|module_id| { - let mut symbols = SymbolCollector::collect_module(&db, module_id); + let mut symbols = SymbolCollector::new_module(&db, module_id); symbols.sort_by_key(|it| it.name.as_str().to_owned()); (module_id, symbols) }) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs index 557c95f704b98..0a7141c19b6b5 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs @@ -31,6 +31,12 @@ const USELESS_NAME_PREFIXES: &[&str] = &["from_", "with_", "into_"]; /// `Result` -> `User` const WRAPPER_TYPES: &[&str] = &["Box", "Arc", "Rc", "Option", "Result"]; +/// Generic types replaced by a plural of their first argument. +/// +/// # Examples +/// `Vec` -> "names" +const SEQUENCE_TYPES: &[&str] = &["Vec", "VecDeque", "LinkedList"]; + /// Prefixes to strip from methods names /// /// # Examples @@ -378,6 +384,11 @@ fn name_of_type(ty: &hir::Type, db: &RootDatabase, edition: Edition) -> Option Option, db: &RootDatabase, edition: Edition) -> SmolStr { + let items_str = SmolStr::new_static("items"); + let Some(inner_ty) = inner_ty else { + return items_str; + }; + let Some(name) = name_of_type(inner_ty, db, edition) else { + return items_str; + }; + + if name.ends_with(['s', 'x', 'y']) { + // Given a type called e.g. "Boss", "Fox" or "Story", don't try to + // create a plural. + items_str + } else { + SmolStr::new(format!("{name}s")) + } +} + fn trait_name(trait_: &hir::Trait, db: &RootDatabase, edition: Edition) -> Option { let name = trait_.name(db).display(db, edition).to_string(); if USELESS_TRAITS.contains(&name.as_str()) { @@ -897,6 +928,58 @@ fn foo() { $0(bar())$0; } ); } + #[test] + fn vec_value() { + check( + r#" +struct Vec {}; +struct Seed; +fn bar() -> Vec {} +fn foo() { $0(bar())$0; } +"#, + "seeds", + ); + } + + #[test] + fn vec_value_ends_with_s() { + check( + r#" +struct Vec {}; +struct Boss; +fn bar() -> Vec {} +fn foo() { $0(bar())$0; } +"#, + "items", + ); + } + + #[test] + fn vecdeque_value() { + check( + r#" +struct VecDeque {}; +struct Seed; +fn bar() -> VecDeque {} +fn foo() { $0(bar())$0; } +"#, + "seeds", + ); + } + + #[test] + fn slice_value() { + check( + r#" +struct Vec {}; +struct Seed; +fn bar() -> &[Seed] {} +fn foo() { $0(bar())$0; } +"#, + "seeds", + ); + } + #[test] fn ref_call() { check( diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt index 535777dfcbea6..7dce95592b819 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt @@ -1007,6 +1007,39 @@ is_alias: false, is_assoc: false, }, + FileSymbol { + name: "ThisStruct", + def: Adt( + Struct( + Struct { + id: StructId( + 4, + ), + }, + ), + ), + loc: DeclarationLocation { + hir_file_id: EditionedFileId( + FileId( + 1, + ), + Edition2021, + ), + ptr: SyntaxNodePtr { + kind: USE_TREE, + range: 85..125, + }, + name_ptr: AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 115..125, + }, + ), + }, + container_name: None, + is_alias: false, + is_assoc: false, + }, ], ), ] diff --git a/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs b/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs index 515bc418cb467..2fdd8358637df 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/ty_filter.rs @@ -26,7 +26,7 @@ impl TryEnum { _ => return None, }; TryEnum::ALL.iter().find_map(|&var| { - if enum_.name(sema.db).eq_ident(var.type_name()) { + if enum_.name(sema.db).as_str() == var.type_name() { return Some(var); } None diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs index dca889d1a8efe..f22041ebe233b 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs @@ -147,6 +147,7 @@ pub(crate) fn json_in_items( prefer_no_std: config.prefer_no_std, prefer_prelude: config.prefer_prelude, prefer_absolute: config.prefer_absolute, + allow_unstable: true, }; if !scope_has("Serialize") { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs index fd1044e51bc23..938b7182bc946 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -128,6 +128,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option, d: &hir::TypedHole) -> Option prefer_no_std: ctx.config.prefer_no_std, prefer_prelude: ctx.config.prefer_prelude, prefer_absolute: ctx.config.prefer_absolute, + allow_unstable: ctx.is_nightly, }, ctx.edition, ) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs index 13591dfb2eebd..f3109b9bb73a2 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unlinked_file.rs @@ -112,7 +112,7 @@ fn fixes( // shouldn't occur _ => continue 'crates, }; - match current.children.iter().find(|(name, _)| name.eq_ident(seg)) { + match current.children.iter().find(|(name, _)| name.as_str() == seg) { Some((_, &child)) => current = &crate_def_map[child], None => continue 'crates, } @@ -161,7 +161,7 @@ fn fixes( // try finding a parent that has an inline tree from here on let mut current = module; for s in stack.iter().rev() { - match module.children.iter().find(|(name, _)| name.eq_ident(s)) { + match module.children.iter().find(|(name, _)| name.as_str() == s) { Some((_, child)) => { current = &crate_def_map[*child]; } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 1e99d7ad6e68a..50c91a69602c1 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -83,7 +83,7 @@ use either::Either; use hir::{db::ExpandDatabase, diagnostics::AnyDiagnostic, Crate, HirFileId, InFile, Semantics}; use ide_db::{ assists::{Assist, AssistId, AssistKind, AssistResolveStrategy}, - base_db::SourceDatabase, + base_db::{ReleaseChannel, SourceDatabase}, generated::lints::{Lint, LintGroup, CLIPPY_LINT_GROUPS, DEFAULT_LINTS, DEFAULT_LINT_GROUPS}, imports::insert_use::InsertUseConfig, label::Label, @@ -276,6 +276,7 @@ struct DiagnosticsContext<'a> { sema: Semantics<'a, RootDatabase>, resolve: &'a AssistResolveStrategy, edition: Edition, + is_nightly: bool, } impl DiagnosticsContext<'_> { @@ -368,7 +369,11 @@ pub fn semantic_diagnostics( let module = sema.file_to_module_def(file_id); - let ctx = DiagnosticsContext { config, sema, resolve, edition: file_id.edition() }; + let is_nightly = matches!( + module.and_then(|m| db.toolchain_channel(m.krate().into())), + Some(ReleaseChannel::Nightly) | None + ); + let ctx = DiagnosticsContext { config, sema, resolve, edition: file_id.edition(), is_nightly }; let mut diags = Vec::new(); match module { diff --git a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs index 4edc3633fbe65..4bead14e31d4d 100644 --- a/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs +++ b/src/tools/rust-analyzer/crates/ide-ssr/src/matching.rs @@ -673,6 +673,7 @@ impl Match { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }; let mod_path = module.find_path(sema.db, module_def, cfg).ok_or_else(|| { match_error!("Failed to render template path `{}` at match location") diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index bc9843f3f35a2..cfd8919730ad2 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -413,8 +413,7 @@ fn rewrite_url_link(db: &RootDatabase, def: Definition, target: &str) -> Option< fn mod_path_of_def(db: &RootDatabase, def: Definition) -> Option { def.canonical_module_path(db).map(|it| { let mut path = String::new(); - it.flat_map(|it| it.name(db)) - .for_each(|name| format_to!(path, "{}/", name.unescaped().display(db))); + it.flat_map(|it| it.name(db)).for_each(|name| format_to!(path, "{}/", name.as_str())); path }) } @@ -590,10 +589,10 @@ fn filename_and_frag_for_def( let res = match def { Definition::Adt(adt) => match adt { Adt::Struct(s) => { - format!("struct.{}.html", s.name(db).unescaped().display(db.upcast())) + format!("struct.{}.html", s.name(db).as_str()) } - Adt::Enum(e) => format!("enum.{}.html", e.name(db).unescaped().display(db.upcast())), - Adt::Union(u) => format!("union.{}.html", u.name(db).unescaped().display(db.upcast())), + Adt::Enum(e) => format!("enum.{}.html", e.name(db).as_str()), + Adt::Union(u) => format!("union.{}.html", u.name(db).as_str()), }, Definition::Crate(_) => String::from("index.html"), Definition::Module(m) => match m.name(db) { @@ -603,48 +602,48 @@ fn filename_and_frag_for_def( Some(kw) => { format!("keyword.{}.html", kw) } - None => format!("{}/index.html", name.unescaped().display(db.upcast())), + None => format!("{}/index.html", name.as_str()), } } None => String::from("index.html"), }, Definition::Trait(t) => { - format!("trait.{}.html", t.name(db).unescaped().display(db.upcast())) + format!("trait.{}.html", t.name(db).as_str()) } Definition::TraitAlias(t) => { - format!("traitalias.{}.html", t.name(db).unescaped().display(db.upcast())) + format!("traitalias.{}.html", t.name(db).as_str()) } Definition::TypeAlias(t) => { - format!("type.{}.html", t.name(db).unescaped().display(db.upcast())) + format!("type.{}.html", t.name(db).as_str()) } Definition::BuiltinType(t) => { - format!("primitive.{}.html", t.name().unescaped().display(db.upcast())) + format!("primitive.{}.html", t.name().as_str()) } Definition::Function(f) => { - format!("fn.{}.html", f.name(db).unescaped().display(db.upcast())) + format!("fn.{}.html", f.name(db).as_str()) } Definition::Variant(ev) => { format!( "enum.{}.html#variant.{}", - ev.parent_enum(db).name(db).unescaped().display(db.upcast()), - ev.name(db).unescaped().display(db.upcast()) + ev.parent_enum(db).name(db).as_str(), + ev.name(db).as_str() ) } Definition::Const(c) => { - format!("const.{}.html", c.name(db)?.unescaped().display(db.upcast())) + format!("const.{}.html", c.name(db)?.as_str()) } Definition::Static(s) => { - format!("static.{}.html", s.name(db).unescaped().display(db.upcast())) + format!("static.{}.html", s.name(db).as_str()) } Definition::Macro(mac) => match mac.kind(db) { hir::MacroKind::Declarative | hir::MacroKind::BuiltIn | hir::MacroKind::Attr | hir::MacroKind::ProcMacro => { - format!("macro.{}.html", mac.name(db).unescaped().display(db.upcast())) + format!("macro.{}.html", mac.name(db).as_str()) } hir::MacroKind::Derive => { - format!("derive.{}.html", mac.name(db).unescaped().display(db.upcast())) + format!("derive.{}.html", mac.name(db).as_str()) } }, Definition::Field(field) => { @@ -654,11 +653,7 @@ fn filename_and_frag_for_def( hir::VariantDef::Variant(it) => Definition::Variant(it), }; let (_, file, _) = filename_and_frag_for_def(db, def)?; - return Some(( - def, - file, - Some(format!("structfield.{}", field.name(db).unescaped().display(db.upcast()))), - )); + return Some((def, file, Some(format!("structfield.{}", field.name(db).as_str())))); } Definition::SelfType(impl_) => { let adt = impl_.self_ty(db).as_adt()?.into(); @@ -667,7 +662,7 @@ fn filename_and_frag_for_def( return Some((adt, file, Some(String::from("impl")))); } Definition::ExternCrateDecl(it) => { - format!("{}/index.html", it.name(db).unescaped().display(db.upcast())) + format!("{}/index.html", it.name(db).as_str()) } Definition::Local(_) | Definition::GenericParam(_) @@ -699,16 +694,16 @@ fn get_assoc_item_fragment(db: &dyn HirDatabase, assoc_item: hir::AssocItem) -> // Rustdoc makes this decision based on whether a method 'has defaultness'. // Currently this is only the case for provided trait methods. if is_trait_method && !function.has_body(db) { - format!("tymethod.{}", function.name(db).unescaped().display(db.upcast())) + format!("tymethod.{}", function.name(db).as_str()) } else { - format!("method.{}", function.name(db).unescaped().display(db.upcast())) + format!("method.{}", function.name(db).as_str()) } } AssocItem::Const(constant) => { - format!("associatedconstant.{}", constant.name(db)?.unescaped().display(db.upcast())) + format!("associatedconstant.{}", constant.name(db)?.as_str()) } AssocItem::TypeAlias(ty) => { - format!("associatedtype.{}", ty.name(db).unescaped().display(db.upcast())) + format!("associatedtype.{}", ty.name(db).as_str()) } }) } diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index f804cc3677274..d18732a6b846b 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -5,10 +5,14 @@ use crate::{ navigation_target::{self, ToNav}, FilePosition, NavigationTarget, RangeInfo, TryToNav, UpmappingResult, }; -use hir::{AsAssocItem, AssocItem, FileRange, InFile, MacroFileIdExt, ModuleDef, Semantics}; +use hir::{ + sym, AsAssocItem, AssocItem, CallableKind, FileRange, HasCrate, InFile, MacroFileIdExt, + ModuleDef, Semantics, +}; use ide_db::{ base_db::{AnchoredPath, FileLoader, SourceDatabase}, defs::{Definition, IdentClass}, + famous_defs::FamousDefs, helpers::pick_best_token, RootDatabase, SymbolKind, }; @@ -129,15 +133,74 @@ pub(crate) fn goto_definition( Some(RangeInfo::new(original_token.text_range(), navs)) } -// If the token is into(), try_into(), parse(), search the definition of From, TryFrom, FromStr. +// If the token is into(), try_into(), search the definition of From, TryFrom. fn find_definition_for_known_blanket_dual_impls( sema: &Semantics<'_, RootDatabase>, original_token: &SyntaxToken, ) -> Option> { let method_call = ast::MethodCallExpr::cast(original_token.parent()?.parent()?)?; - let target_method = sema.resolve_known_blanket_dual_impls(&method_call)?; + let callable = sema.resolve_method_call_as_callable(&method_call)?; + let CallableKind::Function(f) = callable.kind() else { return None }; + let assoc = f.as_assoc_item(sema.db)?; + + let return_type = callable.return_type(); + let fd = FamousDefs(sema, return_type.krate(sema.db)); + + let t = match assoc.container(sema.db) { + hir::AssocItemContainer::Trait(t) => t, + hir::AssocItemContainer::Impl(impl_) + if impl_.self_ty(sema.db).is_str() && f.name(sema.db) == sym::parse => + { + let t = fd.core_convert_FromStr()?; + let t_f = t.function(sema.db, &sym::from_str)?; + return sema + .resolve_trait_impl_method( + return_type.clone(), + t, + t_f, + [return_type.type_arguments().next()?], + ) + .map(|f| def_to_nav(sema.db, f.into())); + } + hir::AssocItemContainer::Impl(_) => return None, + }; - let def = Definition::from(target_method); + let fn_name = f.name(sema.db); + let f = if fn_name == sym::into && fd.core_convert_Into() == Some(t) { + let dual = fd.core_convert_From()?; + let dual_f = dual.function(sema.db, &sym::from)?; + sema.resolve_trait_impl_method( + return_type.clone(), + dual, + dual_f, + [return_type, callable.receiver_param(sema.db)?.1], + )? + } else if fn_name == sym::try_into && fd.core_convert_TryInto() == Some(t) { + let dual = fd.core_convert_TryFrom()?; + let dual_f = dual.function(sema.db, &sym::try_from)?; + sema.resolve_trait_impl_method( + return_type.clone(), + dual, + dual_f, + // Extract the `T` from `Result` + [return_type.type_arguments().next()?, callable.receiver_param(sema.db)?.1], + )? + } else if fn_name == sym::to_string && fd.alloc_string_ToString() == Some(t) { + let dual = fd.core_fmt_Display()?; + let dual_f = dual.function(sema.db, &sym::fmt)?; + sema.resolve_trait_impl_method( + return_type.clone(), + dual, + dual_f, + [callable.receiver_param(sema.db)?.1.strip_reference()], + )? + } else { + return None; + }; + // Assert that we got a trait impl function, if we are back in a trait definition we didn't + // succeed + let _t = f.as_assoc_item(sema.db)?.implemented_trait(sema.db)?; + let def = Definition::from(f); Some(def_to_nav(sema.db, def)) } @@ -3168,18 +3231,45 @@ fn f() { r#" //- minicore: from, str struct A; - impl FromStr for A { type Error = String; - fn from_str(value: &str) -> Result { //^^^^^^^^ Ok(A) } } - fn f() { let a: Result = "aaaaaa".parse$0(); +} + "#, + ); + } + + #[test] + fn to_string_call_to_display_definition() { + check( + r#" +//- minicore:fmt +//- /alloc.rs crate:alloc +pub mod string { + pub struct String; + pub trait ToString { + fn to_string(&self) -> String; + } + + impl ToString for T { + fn to_string(&self) -> String { String } + } +} +//- /lib.rs crate:lib deps:alloc +use alloc::string::ToString; +struct A; +impl core::fmt::Display for A { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {} + // ^^^ +} +fn f() { + A.to_string$0(); } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide/src/hover.rs b/src/tools/rust-analyzer/crates/ide/src/hover.rs index 18a3fed07ece1..9d4c103fc2e01 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover.rs @@ -346,7 +346,7 @@ fn hover_offset( .unique() .reduce(|mut acc: HoverResult, HoverResult { markup, actions }| { acc.actions.extend(actions); - acc.markup = Markup::from(format!("{}\n---\n{markup}", acc.markup)); + acc.markup = Markup::from(format!("{}\n\n---\n{markup}", acc.markup)); acc }) .map(|mut res: HoverResult| { diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 46242b75dd0b9..40f3406b72d37 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -1082,7 +1082,19 @@ fn render_memory_layout( if config.niches { if let Some(niches) = layout.niches() { - format_to!(label, "niches = {niches}, "); + if niches > 1024 { + if niches.is_power_of_two() { + format_to!(label, "niches = 2{}, ", pwr2_to_exponent(niches)); + } else if is_pwr2plus1(niches) { + format_to!(label, "niches = 2{} + 1, ", pwr2_to_exponent(niches - 1)); + } else if is_pwr2minus1(niches) { + format_to!(label, "niches = 2{} - 1, ", pwr2_to_exponent(niches + 1)); + } else { + format_to!(label, "niches = a lot, "); + } + } else { + format_to!(label, "niches = {niches}, "); + } } } label.pop(); // ' ' @@ -1210,3 +1222,74 @@ fn render_dyn_compatibility( } } } + +fn is_pwr2minus1(val: u128) -> bool { + val == u128::MAX || (val + 1).is_power_of_two() +} + +fn is_pwr2plus1(val: u128) -> bool { + val != 0 && (val - 1).is_power_of_two() +} + +/// Formats a power of two as an exponent of two, i.e. 16 => ⁴. Note that `num` MUST be a power +/// of 2, or this function will panic. +fn pwr2_to_exponent(num: u128) -> String { + const DIGITS: [char; 10] = ['⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹']; + assert_eq!(num.count_ones(), 1); + num.trailing_zeros() + .to_string() + .chars() + .map(|c| c.to_digit(10).unwrap() as usize) + .map(|idx| DIGITS[idx]) + .collect::() +} + +#[cfg(test)] +mod tests { + use super::*; + + const TESTERS: [u128; 10] = [0, 1, 2, 3, 4, 255, 256, 257, u128::MAX - 1, u128::MAX]; + + #[test] + fn test_is_pwr2minus1() { + const OUTCOMES: [bool; 10] = + [true, true, false, true, false, true, false, false, false, true]; + for (test, expected) in TESTERS.iter().zip(OUTCOMES) { + let actual = is_pwr2minus1(*test); + assert_eq!(actual, expected, "is_pwr2minu1({test}) gave {actual}, expected {expected}"); + } + } + + #[test] + fn test_is_pwr2plus1() { + const OUTCOMES: [bool; 10] = + [false, false, true, true, false, false, false, true, false, false]; + for (test, expected) in TESTERS.iter().zip(OUTCOMES) { + let actual = is_pwr2plus1(*test); + assert_eq!(actual, expected, "is_pwr2plus1({test}) gave {actual}, expected {expected}"); + } + } + + #[test] + fn test_pwr2_to_exponent() { + const TESTERS: [u128; 9] = [ + 1, + 2, + 4, + 8, + 16, + 9223372036854775808, + 18446744073709551616, + 36893488147419103232, + 170141183460469231731687303715884105728, + ]; + const OUTCOMES: [&str; 9] = ["⁰", "¹", "²", "³", "⁴", "⁶³", "⁶⁴", "⁶⁵", "¹²⁷"]; + for (test, expected) in TESTERS.iter().zip(OUTCOMES) { + let actual = pwr2_to_exponent(*test); + assert_eq!( + actual, expected, + "pwr2_to_exponent({test}) returned {actual}, expected {expected}", + ); + } + } +} diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 014b751f95b0c..8c32cc9720af4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -303,6 +303,7 @@ m!(ab$0c); --- Outer + --- ```rust @@ -1357,7 +1358,7 @@ fn hover_enum_limit() { --- - size = 12 (0xC), align = 4, niches = 4294967288 + size = 12 (0xC), align = 4, niches = a lot "#]], ); } @@ -4401,6 +4402,7 @@ fn main() { --- size = 8, align = 8, niches = 1 + --- ```rust @@ -10094,6 +10096,7 @@ fn bar() { ```rust let field: i32 ``` + --- ```rust @@ -10128,6 +10131,7 @@ fn bar() { --- size = 4, align = 4 + --- ```rust diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 6d83a747d7669..1f723c85df7aa 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -209,7 +209,7 @@ fn hints( ) { closing_brace::hints(hints, sema, config, file_id, node.clone()); if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) { - generic_param::hints(hints, sema, config, any_has_generic_args); + generic_param::hints(hints, famous_defs, config, any_has_generic_args); } match_ast! { @@ -300,22 +300,23 @@ pub struct InlayHintsConfig { pub closing_brace_hints_min_lines: Option, pub fields_to_resolve: InlayFieldsToResolve, } + impl InlayHintsConfig { - fn lazy_text_edit(&self, finish: impl FnOnce() -> TextEdit) -> Lazy { + fn lazy_text_edit(&self, finish: impl FnOnce() -> TextEdit) -> LazyProperty { if self.fields_to_resolve.resolve_text_edits { - Lazy::Lazy + LazyProperty::Lazy } else { let edit = finish(); never!(edit.is_empty(), "inlay hint produced an empty text edit"); - Lazy::Computed(edit) + LazyProperty::Computed(edit) } } - fn lazy_tooltip(&self, finish: impl FnOnce() -> InlayTooltip) -> Lazy { + fn lazy_tooltip(&self, finish: impl FnOnce() -> InlayTooltip) -> LazyProperty { if self.fields_to_resolve.resolve_hint_tooltip && self.fields_to_resolve.resolve_label_tooltip { - Lazy::Lazy + LazyProperty::Lazy } else { let tooltip = finish(); never!( @@ -326,7 +327,20 @@ impl InlayHintsConfig { .is_empty(), "inlay hint produced an empty tooltip" ); - Lazy::Computed(tooltip) + LazyProperty::Computed(tooltip) + } + } + + /// This always reports a resolvable location, so only use this when it is very likely for a + /// location link to actually resolve but where computing `finish` would be costly. + fn lazy_location_opt( + &self, + finish: impl FnOnce() -> Option, + ) -> Option> { + if self.fields_to_resolve.resolve_label_location { + Some(LazyProperty::Lazy) + } else { + finish().map(LazyProperty::Computed) } } } @@ -441,7 +455,7 @@ pub struct InlayHint { /// The actual label to show in the inlay hint. pub label: InlayHintLabel, /// Text edit to apply when "accepting" this inlay hint. - pub text_edit: Option>, + pub text_edit: Option>, /// Range to recompute inlay hints when trying to resolve for this hint. If this is none, the /// hint does not support resolving. pub resolve_parent: Option, @@ -449,15 +463,15 @@ pub struct InlayHint { /// A type signaling that a value is either computed, or is available for computation. #[derive(Clone, Debug)] -pub enum Lazy { +pub enum LazyProperty { Computed(T), Lazy, } -impl Lazy { +impl LazyProperty { pub fn computed(self) -> Option { match self { - Lazy::Computed(it) => Some(it), + LazyProperty::Computed(it) => Some(it), _ => None, } } @@ -508,8 +522,8 @@ pub struct InlayHintLabel { impl InlayHintLabel { pub fn simple( s: impl Into, - tooltip: Option>, - linked_location: Option, + tooltip: Option>, + linked_location: Option>, ) -> InlayHintLabel { InlayHintLabel { parts: smallvec![InlayHintLabelPart { text: s.into(), linked_location, tooltip }], @@ -593,16 +607,16 @@ pub struct InlayHintLabelPart { /// refers to (not necessarily the location itself). /// When setting this, no tooltip must be set on the containing hint, or VS Code will display /// them both. - pub linked_location: Option, + pub linked_location: Option>, /// The tooltip to show when hovering over the inlay hint, this may invoke other actions like /// hover requests to show. - pub tooltip: Option>, + pub tooltip: Option>, } impl std::hash::Hash for InlayHintLabelPart { fn hash(&self, state: &mut H) { self.text.hash(state); - self.linked_location.hash(state); + self.linked_location.is_some().hash(state); self.tooltip.is_some().hash(state); } } @@ -610,7 +624,9 @@ impl std::hash::Hash for InlayHintLabelPart { impl fmt::Debug for InlayHintLabelPart { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self { text, linked_location: None, tooltip: None | Some(Lazy::Lazy) } => text.fmt(f), + Self { text, linked_location: None, tooltip: None | Some(LazyProperty::Lazy) } => { + text.fmt(f) + } Self { text, linked_location, tooltip } => f .debug_struct("InlayHintLabelPart") .field("text", text) @@ -618,8 +634,10 @@ impl fmt::Debug for InlayHintLabelPart { .field( "tooltip", &tooltip.as_ref().map_or("", |it| match it { - Lazy::Computed(InlayTooltip::String(it) | InlayTooltip::Markdown(it)) => it, - Lazy::Lazy => "", + LazyProperty::Computed( + InlayTooltip::String(it) | InlayTooltip::Markdown(it), + ) => it, + LazyProperty::Lazy => "", }), ) .finish(), @@ -632,7 +650,8 @@ struct InlayHintLabelBuilder<'a> { db: &'a RootDatabase, result: InlayHintLabel, last_part: String, - location: Option, + resolve: bool, + location: Option>, } impl fmt::Write for InlayHintLabelBuilder<'_> { @@ -645,11 +664,16 @@ impl HirWrite for InlayHintLabelBuilder<'_> { fn start_location_link(&mut self, def: ModuleDefId) { never!(self.location.is_some(), "location link is already started"); self.make_new_part(); - let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return }; - let location = location.call_site(); - let location = - FileRange { file_id: location.file_id, range: location.focus_or_full_range() }; - self.location = Some(location); + + self.location = Some(if self.resolve { + LazyProperty::Lazy + } else { + LazyProperty::Computed({ + let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return }; + let location = location.call_site(); + FileRange { file_id: location.file_id, range: location.focus_or_full_range() } + }) + }); } fn end_location_link(&mut self) { @@ -735,6 +759,7 @@ fn label_of_ty( last_part: String::new(), location: None, result: InlayHintLabel::default(), + resolve: config.fields_to_resolve.resolve_label_location, }; let _ = rec(sema, famous_defs, config.max_length, ty, &mut label_builder, config, edition); let r = label_builder.finish(); @@ -783,7 +808,7 @@ fn ty_to_text_edit( ty: &hir::Type, offset_to_insert: TextSize, prefix: impl Into, -) -> Option> { +) -> Option> { // FIXME: Limit the length and bail out on excess somehow? let rendered = sema .scope(node_for_hint) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index ab5464156f0a7..01a1a4545c478 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -1203,6 +1203,40 @@ fn f5>(it: G) { let l = it.f(); //^ () } +"#, + ); + } + + #[test] + fn regression_19007() { + check_types( + r#" +trait Foo { + type Assoc; + + fn foo(&self) -> Self::Assoc; +} + +trait Bar { + type Target; +} + +trait Baz {} + +struct Struct { + field: T, +} + +impl Struct +where + T: Foo, + T::Assoc: Baz<::Target> + Bar, +{ + fn f(&self) { + let x = self.field.foo(); + //^ impl Baz<<::Assoc as Bar>::Target> + Bar + } +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs index 429ddd31cbd0a..e9b728bcaa75d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs @@ -22,11 +22,7 @@ pub(super) fn hints( return None; } - let linked_location = - famous_defs.core_marker_Sized().and_then(|it| it.try_to_nav(sema.db)).map(|it| { - let n = it.call_site(); - FileRange { file_id: n.file_id, range: n.focus_or_full_range() } - }); + let sized_trait = famous_defs.core_marker_Sized(); for param in params.type_or_const_params() { match param { @@ -48,7 +44,17 @@ pub(super) fn hints( } hint.parts.push(InlayHintLabelPart { text: "Sized".to_owned(), - linked_location, + linked_location: sized_trait.and_then(|it| { + config.lazy_location_opt(|| { + it.try_to_nav(sema.db).map(|it| { + let n = it.call_site(); + FileRange { + file_id: n.file_id, + range: n.focus_or_full_range(), + } + }) + }) + }), tooltip: None, }); if has_bounds { @@ -134,12 +140,14 @@ fn foo() {} InlayHintLabelPart { text: "Sized", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 135..140, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 135..140, + }, + ), ), tooltip: "", }, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs index 7fa7ab1a94d63..8471547727fed 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs @@ -81,7 +81,10 @@ mod tests { use crate::{ fixture, - inlay_hints::tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG}, + inlay_hints::{ + tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG}, + LazyProperty, + }, InlayHintsConfig, }; @@ -99,7 +102,7 @@ mod tests { let (analysis, file_id) = fixture::file(ra_fixture); let mut inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap(); inlay_hints.iter_mut().flat_map(|hint| &mut hint.label.parts).for_each(|hint| { - if let Some(loc) = &mut hint.linked_location { + if let Some(LazyProperty::Computed(loc)) = &mut hint.linked_location { loc.range = TextRange::empty(TextSize::from(0)); } }); @@ -134,12 +137,14 @@ fn main() { InlayHintLabelPart { text: "B", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 63..64, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 63..64, + }, + ), ), tooltip: "", }, @@ -151,12 +156,14 @@ fn main() { InlayHintLabelPart { text: "A", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 7..8, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..8, + }, + ), ), tooltip: "", }, @@ -213,12 +220,14 @@ fn main() { InlayHintLabelPart { text: "C", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 51..52, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 51..52, + }, + ), ), tooltip: "", }, @@ -230,12 +239,14 @@ fn main() { InlayHintLabelPart { text: "B", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 29..30, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 29..30, + }, + ), ), tooltip: "", }, @@ -276,12 +287,14 @@ fn main() { InlayHintLabelPart { text: "C", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 51..52, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 51..52, + }, + ), ), tooltip: "", }, @@ -293,12 +306,14 @@ fn main() { InlayHintLabelPart { text: "B", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 29..30, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 29..30, + }, + ), ), tooltip: "", }, @@ -340,12 +355,14 @@ fn main() { InlayHintLabelPart { text: "B", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 23..24, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 23..24, + }, + ), ), tooltip: "", }, @@ -353,12 +370,14 @@ fn main() { InlayHintLabelPart { text: "X", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 55..56, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 55..56, + }, + ), ), tooltip: "", }, @@ -371,12 +390,14 @@ fn main() { InlayHintLabelPart { text: "A", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 7..8, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..8, + }, + ), ), tooltip: "", }, @@ -384,12 +405,14 @@ fn main() { InlayHintLabelPart { text: "X", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 55..56, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 55..56, + }, + ), ), tooltip: "", }, @@ -435,12 +458,14 @@ fn main() { InlayHintLabelPart { text: "Iterator", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -448,12 +473,14 @@ fn main() { InlayHintLabelPart { text: "Item", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -467,12 +494,14 @@ fn main() { InlayHintLabelPart { text: "Iterator", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -480,12 +509,14 @@ fn main() { InlayHintLabelPart { text: "Item", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -499,12 +530,14 @@ fn main() { InlayHintLabelPart { text: "Iterator", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -512,12 +545,14 @@ fn main() { InlayHintLabelPart { text: "Item", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 1, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 1, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -531,12 +566,14 @@ fn main() { InlayHintLabelPart { text: "MyIter", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 0..0, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 0..0, + }, + ), ), tooltip: "", }, @@ -577,12 +614,14 @@ fn main() { InlayHintLabelPart { text: "Struct", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 7..13, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..13, + }, + ), ), tooltip: "", }, @@ -594,12 +633,14 @@ fn main() { InlayHintLabelPart { text: "Struct", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 7..13, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..13, + }, + ), ), tooltip: "", }, @@ -611,12 +652,14 @@ fn main() { InlayHintLabelPart { text: "Struct", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 7..13, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 7..13, + }, + ), ), tooltip: "", }, @@ -628,12 +671,14 @@ fn main() { InlayHintLabelPart { text: "self", linked_location: Some( - FileRangeWrapper { - file_id: FileId( - 0, - ), - range: 42..46, - }, + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 42..46, + }, + ), ), tooltip: "", }, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs index 90b8be64a46d2..3767d34e2c7a9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs @@ -11,7 +11,10 @@ use syntax::{ match_ast, SyntaxKind, SyntaxNode, T, }; -use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind}; +use crate::{ + inlay_hints::LazyProperty, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, + InlayKind, +}; pub(super) fn hints( acc: &mut Vec, @@ -141,7 +144,7 @@ pub(super) fn hints( acc.push(InlayHint { range: closing_token.text_range(), kind: InlayKind::ClosingBrace, - label: InlayHintLabel::simple(label, None, linked_location), + label: InlayHintLabel::simple(label, None, linked_location.map(LazyProperty::Computed)), text_edit: None, position: InlayHintPosition::After, pad_left: true, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs index 906f2acf0c445..3e91618d08e6f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs @@ -53,10 +53,6 @@ pub(super) fn hints( let last = captures.len() - 1; for (idx, capture) in captures.into_iter().enumerate() { let local = capture.local(); - let source = local.primary_source(sema.db); - - // force cache the source file, otherwise sema lookup will potentially panic - _ = sema.parse_or_expand(source.file()); let label = format!( "{}{}", @@ -73,8 +69,17 @@ pub(super) fn hints( } hint.label.append_part(InlayHintLabelPart { text: label, - linked_location: source.name().and_then(|name| { - name.syntax().original_file_range_opt(sema.db).map(TupleExt::head).map(Into::into) + linked_location: config.lazy_location_opt(|| { + let source = local.primary_source(sema.db); + + // force cache the source file, otherwise sema lookup will potentially panic + _ = sema.parse_or_expand(source.file()); + source.name().and_then(|name| { + name.syntax() + .original_file_range_opt(sema.db) + .map(TupleExt::head) + .map(Into::into) + }) }), tooltip: None, }); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs index 037b328d971fc..762a4c2655181 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs @@ -1,17 +1,19 @@ //! Implementation of inlay hints for generic parameters. -use ide_db::{active_parameter::generic_def_for_node, RootDatabase}; +use ide_db::{active_parameter::generic_def_for_node, famous_defs::FamousDefs}; use syntax::{ ast::{self, AnyHasGenericArgs, HasGenericArgs, HasName}, AstNode, }; -use crate::{inlay_hints::GenericParameterHints, InlayHint, InlayHintsConfig, InlayKind}; +use crate::{ + inlay_hints::GenericParameterHints, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind, +}; -use super::param_name::{is_argument_similar_to_param_name, render_label}; +use super::param_name::is_argument_similar_to_param_name; pub(crate) fn hints( acc: &mut Vec, - sema: &hir::Semantics<'_, RootDatabase>, + FamousDefs(sema, krate): &FamousDefs<'_, '_>, config: &InlayHintsConfig, node: AnyHasGenericArgs, ) -> Option<()> { @@ -45,12 +47,23 @@ pub(crate) fn hints( return None; } - let name = param.name(sema.db); - let param_name = name.as_str(); + let allowed = match (param, &arg) { + (hir::GenericParam::TypeParam(_), ast::GenericArg::TypeArg(_)) => type_hints, + (hir::GenericParam::ConstParam(_), ast::GenericArg::ConstArg(_)) => const_hints, + (hir::GenericParam::LifetimeParam(_), ast::GenericArg::LifetimeArg(_)) => { + lifetime_hints + } + _ => false, + }; + if !allowed { + return None; + } + + let param_name = param.name(sema.db); let should_hide = { let argument = get_string_representation(&arg)?; - is_argument_similar_to_param_name(&argument, param_name) + is_argument_similar_to_param_name(&argument, param_name.as_str()) }; if should_hide { @@ -59,30 +72,28 @@ pub(crate) fn hints( let range = sema.original_range_opt(arg.syntax())?.range; - let source_syntax = match param { - hir::GenericParam::TypeParam(it) => { - if !type_hints || !matches!(arg, ast::GenericArg::TypeArg(_)) { - return None; - } - sema.source(it.merge())?.value.syntax().clone() - } - hir::GenericParam::ConstParam(it) => { - if !const_hints || !matches!(arg, ast::GenericArg::ConstArg(_)) { - return None; - } - let syntax = sema.source(it.merge())?.value.syntax().clone(); - let const_param = ast::ConstParam::cast(syntax)?; - const_param.name()?.syntax().clone() - } - hir::GenericParam::LifetimeParam(it) => { - if !lifetime_hints || !matches!(arg, ast::GenericArg::LifetimeArg(_)) { - return None; - } - sema.source(it)?.value.syntax().clone() - } - }; - let linked_location = sema.original_range_opt(&source_syntax); - let label = render_label(param_name, config, linked_location); + let colon = if config.render_colons { ":" } else { "" }; + let label = InlayHintLabel::simple( + format!("{}{colon}", param_name.display(sema.db, krate.edition(sema.db))), + None, + config.lazy_location_opt(|| { + let source_syntax = match param { + hir::GenericParam::TypeParam(it) => { + sema.source(it.merge()).map(|it| it.value.syntax().clone()) + } + hir::GenericParam::ConstParam(it) => { + let syntax = sema.source(it.merge())?.value.syntax().clone(); + let const_param = ast::ConstParam::cast(syntax)?; + const_param.name().map(|it| it.syntax().clone()) + } + hir::GenericParam::LifetimeParam(it) => { + sema.source(it).map(|it| it.value.syntax().clone()) + } + }; + let linked_location = source_syntax.and_then(|it| sema.original_range_opt(&it)); + linked_location.map(Into::into) + }), + ); Some(InlayHint { range, diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs index 1358d3722f897..27c7c3d498187 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs @@ -49,7 +49,7 @@ pub(super) fn hints( if mir.locals[place.local].ty.adt_id(ChalkTyInterner).is_none() { continue; // Arguably only ADTs have significant drop impls } - let Some(binding) = local_to_binding.get(place.local) else { + let Some(&binding_idx) = local_to_binding.get(place.local) else { continue; // Ignore temporary values }; let range = match terminator.span { @@ -91,25 +91,26 @@ pub(super) fn hints( }, MirSpan::Unknown => continue, }; - let binding_source = source_map - .patterns_for_binding(*binding) - .first() - .and_then(|d| source_map.pat_syntax(*d).ok()) - .and_then(|d| { - Some(FileRange { - file_id: d.file_id.file_id()?.into(), - range: d.value.text_range(), - }) - }); - let binding = &hir.bindings[*binding]; + let binding = &hir.bindings[binding_idx]; let name = binding.name.display_no_db(file_id.edition()).to_smolstr(); if name.starts_with(" i32 { x + y } //! _ = max(/*x*/4, /*y*/4); //! ``` -use std::fmt::Display; use either::Either; use hir::{Callable, Semantics}; @@ -20,7 +19,7 @@ use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, Inla pub(super) fn hints( acc: &mut Vec, - FamousDefs(sema, _): &FamousDefs<'_, '_>, + FamousDefs(sema, krate): &FamousDefs<'_, '_>, config: &InlayHintsConfig, _file_id: EditionedFileId, expr: ast::Expr, @@ -37,23 +36,29 @@ pub(super) fn hints( .filter_map(|(p, arg)| { // Only annotate hints for expressions that exist in the original file let range = sema.original_range_opt(arg.syntax())?; - let source = sema.source(p)?; - let (param_name, name_syntax) = match source.value.as_ref() { - Either::Left(pat) => (pat.name()?, pat.name()), - Either::Right(param) => match param.pat()? { - ast::Pat::IdentPat(it) => (it.name()?, it.name()), - _ => return None, - }, - }; - Some((name_syntax, param_name, arg, range)) + let param_name = p.name(sema.db)?; + Some((p, param_name, arg, range)) }) .filter(|(_, param_name, arg, _)| { - !should_hide_param_name_hint(sema, &callable, ¶m_name.text(), arg) + !should_hide_param_name_hint(sema, &callable, param_name.as_str(), arg) }) .map(|(param, param_name, _, hir::FileRange { range, .. })| { - let linked_location = param.and_then(|name| sema.original_range_opt(name.syntax())); - - let label = render_label(¶m_name, config, linked_location); + let colon = if config.render_colons { ":" } else { "" }; + let label = InlayHintLabel::simple( + format!("{}{colon}", param_name.display(sema.db, krate.edition(sema.db))), + None, + config.lazy_location_opt(|| { + let source = sema.source(param)?; + let name_syntax = match source.value.as_ref() { + Either::Left(pat) => pat.name(), + Either::Right(param) => match param.pat()? { + ast::Pat::IdentPat(it) => it.name(), + _ => None, + }, + }?; + sema.original_range_opt(name_syntax.syntax()).map(Into::into) + }), + ); InlayHint { range, kind: InlayKind::Parameter, @@ -70,16 +75,6 @@ pub(super) fn hints( Some(()) } -pub(super) fn render_label( - param_name: impl Display, - config: &InlayHintsConfig, - linked_location: Option, -) -> InlayHintLabel { - let colon = if config.render_colons { ":" } else { "" }; - - InlayHintLabel::simple(format!("{param_name}{colon}"), None, linked_location.map(Into::into)) -} - fn get_callable( sema: &Semantics<'_, RootDatabase>, expr: &ast::Expr, @@ -124,9 +119,7 @@ fn should_hide_param_name_hint( } let fn_name = match callable.kind() { - hir::CallableKind::Function(it) => { - Some(it.name(sema.db).unescaped().display_no_db().to_smolstr()) - } + hir::CallableKind::Function(it) => Some(it.name(sema.db).as_str().to_smolstr()), _ => None, }; let fn_name = fn_name.as_deref(); diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index 346e2862b0fdd..e942f5a6aac78 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -91,7 +91,8 @@ pub use crate::{ inlay_hints::{ AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, GenericParameterHints, InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, - InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints, + InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip, LazyProperty, + LifetimeElisionHints, }, join_lines::JoinLinesConfig, markup::Markup, @@ -671,7 +672,7 @@ impl Analysis { &self, config: &CompletionConfig<'_>, position: FilePosition, - imports: impl IntoIterator + std::panic::UnwindSafe, + imports: impl IntoIterator + std::panic::UnwindSafe, ) -> Cancellable> { Ok(self .with_db(|db| ide_completion::resolve_completion_edits(db, config, position, imports))? diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index ba739df3092b3..07dfd83c4eb7f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -263,7 +263,7 @@ fn find_definitions( .and_then(|def| { // if the name differs from the definitions name it has to be an alias if def - .name(sema.db).is_some_and(|it| !it.eq_ident(name_ref.text().as_str())) + .name(sema.db).is_some_and(|it| it.as_str() != name_ref.text().trim_start_matches("r#")) { Err(format_err!("Renaming aliases is currently unsupported")) } else { diff --git a/src/tools/rust-analyzer/crates/intern/Cargo.toml b/src/tools/rust-analyzer/crates/intern/Cargo.toml index 5e7ee54c6af77..c0358ef929b4d 100644 --- a/src/tools/rust-analyzer/crates/intern/Cargo.toml +++ b/src/tools/rust-analyzer/crates/intern/Cargo.toml @@ -19,7 +19,6 @@ dashmap.workspace = true hashbrown.workspace = true rustc-hash.workspace = true triomphe.workspace = true -sptr = "0.3.2" [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol.rs b/src/tools/rust-analyzer/crates/intern/src/symbol.rs index 200b14027f804..b3bf285edfb11 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol.rs @@ -13,7 +13,6 @@ use std::{ use dashmap::{DashMap, SharedValue}; use hashbrown::{hash_map::RawEntryMut, HashMap}; use rustc_hash::FxHasher; -use sptr::Strict; use triomphe::Arc; pub mod symbols; @@ -84,7 +83,7 @@ impl TaggedArcPtr { #[inline] pub(crate) unsafe fn try_as_arc_owned(self) -> Option>>> { // Unpack the tag from the alignment niche - let tag = Strict::addr(self.packed.as_ptr()) & Self::BOOL_BITS; + let tag = self.packed.as_ptr().addr() & Self::BOOL_BITS; if tag != 0 { // Safety: We checked that the tag is non-zero -> true, so we are pointing to the data offset of an `Arc` Some(ManuallyDrop::new(unsafe { @@ -99,40 +98,18 @@ impl TaggedArcPtr { fn pack_arc(ptr: NonNull<*const str>) -> NonNull<*const str> { let packed_tag = true as usize; - // can't use this strict provenance stuff here due to trait methods not being const - // unsafe { - // // Safety: The pointer is derived from a non-null - // NonNull::new_unchecked(Strict::map_addr(ptr.as_ptr(), |addr| { - // // Safety: - // // - The pointer is `NonNull` => it's address is `NonZero` - // // - `P::BITS` least significant bits are always zero (`Pointer` contract) - // // - `T::BITS <= P::BITS` (from `Self::ASSERTION`) - // // - // // Thus `addr >> T::BITS` is guaranteed to be non-zero. - // // - // // `{non_zero} | packed_tag` can't make the value zero. - - // (addr >> Self::BOOL_BITS) | packed_tag - // })) - // } - // so what follows is roughly what the above looks like but inlined - - let self_addr = ptr.as_ptr() as *const *const str as usize; - let addr = self_addr | packed_tag; - let dest_addr = addr as isize; - let offset = dest_addr.wrapping_sub(self_addr as isize); - - // SAFETY: The resulting pointer is guaranteed to be NonNull as we only modify the niche bytes - unsafe { NonNull::new_unchecked(ptr.as_ptr().cast::().wrapping_offset(offset).cast()) } + unsafe { + // Safety: The pointer is derived from a non-null and bit-oring it with true (1) will + // not make it null. + NonNull::new_unchecked(ptr.as_ptr().map_addr(|addr| addr | packed_tag)) + } } #[inline] pub(crate) fn pointer(self) -> NonNull<*const str> { // SAFETY: The resulting pointer is guaranteed to be NonNull as we only modify the niche bytes unsafe { - NonNull::new_unchecked(Strict::map_addr(self.packed.as_ptr(), |addr| { - addr & !Self::BOOL_BITS - })) + NonNull::new_unchecked(self.packed.as_ptr().map_addr(|addr| addr & !Self::BOOL_BITS)) } } diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index b3b46421b50fb..9bc78ff87b8aa 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -240,8 +240,10 @@ define_symbols! { format_unsafe_arg, format, freeze, + from, From, FromStr, + from_str, from_output, from_residual, from_usize, @@ -273,6 +275,8 @@ define_symbols! { index_mut, index, Index, + into, + Into, into_future, into_iter, IntoFuture, @@ -361,6 +365,7 @@ define_symbols! { panic_nounwind, panic, Param, + parse, partial_ord, PartialEq, PartialOrd, @@ -389,6 +394,7 @@ define_symbols! { RangeToInclusive, Ready, receiver, + receiver_target, recursion_limit, register_attr, register_tool, @@ -454,13 +460,17 @@ define_symbols! { termination, test_case, test, + then, thiscall, + to_string, trace_macros, transmute_opts, transmute_trait, transparent, + try_into, Try, TryFrom, + try_from, tuple_trait, u128, u16, diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index 00446b27cf2f3..5654c04a59287 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -256,6 +256,24 @@ impl ProjectFolders { fsc.add_file_set(file_set_roots) } + for ws in workspaces.iter() { + let mut file_set_roots: Vec = vec![]; + let mut entries = vec![]; + + for buildfile in ws.buildfiles() { + file_set_roots.push(VfsPath::from(buildfile.to_owned())); + entries.push(buildfile.to_owned()); + } + + if !file_set_roots.is_empty() { + let entry = vfs::loader::Entry::Files(entries); + res.watch.push(res.load.len()); + res.load.push(entry); + local_filesets.push(fsc.len() as u64); + fsc.add_file_set(file_set_roots) + } + } + if let Some(user_config_path) = user_config_dir_path { let ratoml_path = { let mut p = user_config_path.to_path_buf(); diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index c7614849e015b..59293ee3f9659 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -392,12 +392,12 @@ impl server::Span for RaSpanServer { fn line(&mut self, _span: Self::Span) -> usize { // FIXME requires db to resolve line index, THIS IS NOT INCREMENTAL - 0 + 1 } fn column(&mut self, _span: Self::Span) -> usize { // FIXME requires db to resolve line index, THIS IS NOT INCREMENTAL - 0 + 1 } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs index 466eb14b55ea5..409cf3cc78134 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/token_id.rs @@ -291,11 +291,11 @@ impl server::Span for TokenIdServer { } fn line(&mut self, _span: Self::Span) -> usize { - 0 + 1 } fn column(&mut self, _span: Self::Span) -> usize { - 0 + 1 } } diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs index 6a88cf022dfb0..a396396761041 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs @@ -63,7 +63,7 @@ use crate::{ManifestPath, TargetKind}; pub struct ProjectJson { /// e.g. `path/to/sysroot` pub(crate) sysroot: Option, - /// e.g. `path/to/sysroot/lib/rustlib/src/rust` + /// e.g. `path/to/sysroot/lib/rustlib/src/rust/library` pub(crate) sysroot_src: Option, project_root: AbsPathBuf, /// The path to the rust-project.json file. May be None if this diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs index 4bf9b59e7d038..e472da0c89b0d 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs @@ -19,7 +19,7 @@ pub fn get( let rustc_cfgs = match rustc_cfgs { Ok(cfgs) => cfgs, Err(e) => { - tracing::error!(?e, "failed to get rustc cfgs"); + tracing::warn!(?e, "failed to get rustc cfgs"); return vec![]; } }; diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index f98d983ac0608..dcd62753cb2f9 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -524,6 +524,17 @@ impl ProjectWorkspace { } } + pub fn buildfiles(&self) -> Vec { + match &self.kind { + ProjectWorkspaceKind::Json(project) => project + .crates() + .filter_map(|(_, krate)| krate.build.as_ref().map(|build| build.build_file.clone())) + .map(|build_file| self.workspace_root().join(build_file)) + .collect(), + _ => vec![], + } + } + pub fn find_sysroot_proc_macro_srv(&self) -> anyhow::Result { self.sysroot.discover_proc_macro_srv() } @@ -568,27 +579,15 @@ impl ProjectWorkspace { match &self.kind { ProjectWorkspaceKind::Json(project) => project .crates() - .map(|(_, krate)| { - // FIXME: PackageRoots dont allow specifying files, only directories - let build_file = krate - .build - .as_ref() - .map(|build| self.workspace_root().join(&build.build_file)) - .as_deref() - .and_then(AbsPath::parent) - .map(ToOwned::to_owned); - - PackageRoot { - is_local: krate.is_workspace_member, - include: krate - .include - .iter() - .cloned() - .chain(build_file) - .chain(self.extra_includes.iter().cloned()) - .collect(), - exclude: krate.exclude.clone(), - } + .map(|(_, krate)| PackageRoot { + is_local: krate.is_workspace_member, + include: krate + .include + .iter() + .cloned() + .chain(self.extra_includes.iter().cloned()) + .collect(), + exclude: krate.exclude.clone(), }) .collect::>() .into_iter() diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index bcaec52019591..18c27c844964b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -465,6 +465,7 @@ impl flags::AnalysisStats { prefer_no_std: false, prefer_prelude: true, prefer_absolute: false, + allow_unstable: true, }, Edition::LATEST, ) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 3dc4379258fa1..44325fa1a29e6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -453,6 +453,10 @@ config_data! { /// /// In `match` arms it completes a comma instead. completion_addSemicolonToUnit: bool = true, + /// Toggles the additional completions that automatically show method calls and field accesses with `await` prefixed to them when completing on a future. + completion_autoAwait_enable: bool = true, + /// Toggles the additional completions that automatically show method calls with `iter()` or `into_iter()` prefixed to them when completing on a type that has them. + completion_autoIter_enable: bool = true, /// Toggles the additional completions that automatically add imports when completed. /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. completion_autoimport_enable: bool = true, @@ -1484,6 +1488,8 @@ impl Config { enable_imports_on_the_fly: self.completion_autoimport_enable(source_root).to_owned() && self.caps.completion_item_edit_resolve(), enable_self_on_the_fly: self.completion_autoself_enable(source_root).to_owned(), + enable_auto_iter: *self.completion_autoIter_enable(source_root), + enable_auto_await: *self.completion_autoAwait_enable(source_root), enable_private_editable: self.completion_privateEditable_enable(source_root).to_owned(), full_function_signatures: self .completion_fullFunctionSignatures_enable(source_root) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs index 22f06d68d80d1..2309f94a7429b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -88,6 +88,17 @@ pub(crate) enum FlycheckConfig { }, } +impl FlycheckConfig { + pub(crate) fn invocation_strategy_once(&self) -> bool { + match self { + FlycheckConfig::CargoCommand { .. } => false, + FlycheckConfig::CustomCommand { invocation_strategy, .. } => { + *invocation_strategy == InvocationStrategy::Once + } + } + } +} + impl fmt::Display for FlycheckConfig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index 98efc637c2c81..84ba89d9f31f9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -291,9 +291,15 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { let file_id = state.vfs.read().0.file_id(&vfs_path); if let Some(file_id) = file_id { let world = state.snapshot(); + let invocation_strategy_once = state.config.flycheck(None).invocation_strategy_once(); let may_flycheck_workspace = state.config.flycheck_workspace(None); let mut updated = false; let task = move || -> std::result::Result<(), ide::Cancelled> { + if invocation_strategy_once { + let saved_file = vfs_path.as_path().map(|p| p.to_owned()); + world.flycheck[0].restart_workspace(saved_file.clone()); + } + let target = TargetSpec::for_file(&world, file_id)?.and_then(|it| { let tgt_kind = it.target_kind(); let (tgt_name, root, package) = match it { @@ -320,16 +326,15 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { // the user opted into package checks then let package_check_allowed = target.is_some() || !may_flycheck_workspace; if package_check_allowed { - let workspace = - world.workspaces.iter().enumerate().find(|(_, ws)| match &ws.kind { - project_model::ProjectWorkspaceKind::Cargo { cargo, .. } - | project_model::ProjectWorkspaceKind::DetachedFile { - cargo: Some((cargo, _, _)), - .. - } => *cargo.workspace_root() == root, - _ => false, - }); - if let Some((idx, _)) = workspace { + let workspace = world.workspaces.iter().position(|ws| match &ws.kind { + project_model::ProjectWorkspaceKind::Cargo { cargo, .. } + | project_model::ProjectWorkspaceKind::DetachedFile { + cargo: Some((cargo, _, _)), + .. + } => *cargo.workspace_root() == root, + _ => false, + }); + if let Some(idx) = workspace { world.flycheck[idx].restart_for_package(package, target); } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 190015d7faad8..39cbf53eaa21d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -1154,10 +1154,7 @@ pub(crate) fn handle_completion_resolve( .resolve_completion_edits( &forced_resolve_completions_config, position, - resolve_data - .imports - .into_iter() - .map(|import| (import.full_import_path, import.imported_name)), + resolve_data.imports.into_iter().map(|import| import.full_import_path), )? .into_iter() .flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel))) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs index fcfd06679bf2b..5cdc51a1c1995 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -176,6 +176,8 @@ fn integrated_completion_benchmark() { fields_to_resolve: CompletionFieldsToResolve::empty(), exclude_flyimport: vec![], exclude_traits: &[], + enable_auto_await: true, + enable_auto_iter: true, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -226,6 +228,8 @@ fn integrated_completion_benchmark() { fields_to_resolve: CompletionFieldsToResolve::empty(), exclude_flyimport: vec![], exclude_traits: &[], + enable_auto_await: true, + enable_auto_iter: true, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -274,6 +278,8 @@ fn integrated_completion_benchmark() { fields_to_resolve: CompletionFieldsToResolve::empty(), exclude_flyimport: vec![], exclude_traits: &[], + enable_auto_await: true, + enable_auto_iter: true, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs index 61ec576dd4f90..ccffa7a671e66 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs @@ -142,9 +142,8 @@ fn completion_item_hash(item: &CompletionItem, is_ref_completion: bool) -> [u8; hasher.update(prefix); hasher.update(u32::from(*text_size).to_le_bytes()); } - for (import_path, import_name) in &item.import_to_add { + for import_path in &item.import_to_add { hasher.update(import_path); - hasher.update(import_name); } hasher.finalize() } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs index 134de92feab3a..ca4372aa83f8d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs @@ -850,7 +850,6 @@ pub struct InlayHintResolveData { #[derive(Debug, Serialize, Deserialize)] pub struct CompletionImport { pub full_import_path: String, - pub imported_name: String, } #[derive(Debug, Deserialize, Default)] diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index a5516e7f9d4fe..bff53cf98b7b8 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -11,8 +11,8 @@ use ide::{ Annotation, AnnotationKind, Assist, AssistKind, Cancellable, CompletionFieldsToResolve, CompletionItem, CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel, - InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayKind, Markup, - NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp, + InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayKind, LazyProperty, + Markup, NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp, SnippetEdit, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize, }; use ide_db::{assists, rust_doc::format_docs, FxHasher}; @@ -394,10 +394,7 @@ fn completion_item( item.import_to_add .clone() .into_iter() - .map(|(import_path, import_name)| lsp_ext::CompletionImport { - full_import_path: import_path, - imported_name: import_name, - }) + .map(|import_path| lsp_ext::CompletionImport { full_import_path: import_path }) .collect() } else { Vec::new() @@ -549,12 +546,11 @@ pub(crate) fn inlay_hint( ) -> Cancellable { let hint_needs_resolve = |hint: &InlayHint| -> Option { hint.resolve_parent.filter(|_| { - hint.text_edit.is_some() - || hint - .label - .parts - .iter() - .any(|part| part.linked_location.is_some() || part.tooltip.is_some()) + hint.text_edit.as_ref().is_some_and(LazyProperty::is_lazy) + || hint.label.parts.iter().any(|part| { + part.linked_location.as_ref().is_some_and(LazyProperty::is_lazy) + || part.tooltip.as_ref().is_some_and(LazyProperty::is_lazy) + }) }) }; @@ -569,22 +565,21 @@ pub(crate) fn inlay_hint( }); let mut something_to_resolve = false; - let text_edits = if snap - .config - .visual_studio_code_version() - .is_none_or(|version| VersionReq::parse(">=1.86.0").unwrap().matches(version)) - && resolve_range_and_hash.is_some() - && fields_to_resolve.resolve_text_edits - { - something_to_resolve |= inlay_hint.text_edit.is_some(); - None - } else { - inlay_hint - .text_edit - .take() - .and_then(|it| it.computed()) - .map(|it| text_edit_vec(line_index, it)) - }; + let text_edits = inlay_hint + .text_edit + .take() + .and_then(|it| match it { + LazyProperty::Computed(it) => Some(it), + LazyProperty::Lazy => { + something_to_resolve |= + snap.config.visual_studio_code_version().is_none_or(|version| { + VersionReq::parse(">=1.86.0").unwrap().matches(version) + }) && resolve_range_and_hash.is_some() + && fields_to_resolve.resolve_text_edits; + None + } + }) + .map(|it| text_edit_vec(line_index, it)); let (label, tooltip) = inlay_hint_label( snap, fields_to_resolve, @@ -637,22 +632,23 @@ fn inlay_hint_label( let (label, tooltip) = match &*label.parts { [InlayHintLabelPart { linked_location: None, .. }] => { let InlayHintLabelPart { text, tooltip, .. } = label.parts.pop().unwrap(); - let hint_tooltip = if needs_resolve && fields_to_resolve.resolve_hint_tooltip { - *something_to_resolve |= tooltip.is_some(); - None - } else { - match tooltip.and_then(|it| it.computed()) { - Some(ide::InlayTooltip::String(s)) => { - Some(lsp_types::InlayHintTooltip::String(s)) - } - Some(ide::InlayTooltip::Markdown(s)) => { - Some(lsp_types::InlayHintTooltip::MarkupContent(lsp_types::MarkupContent { - kind: lsp_types::MarkupKind::Markdown, - value: s, - })) - } - None => None, + let tooltip = tooltip.and_then(|it| match it { + LazyProperty::Computed(it) => Some(it), + LazyProperty::Lazy => { + *something_to_resolve |= + needs_resolve && fields_to_resolve.resolve_hint_tooltip; + None } + }); + let hint_tooltip = match tooltip { + Some(ide::InlayTooltip::String(s)) => Some(lsp_types::InlayHintTooltip::String(s)), + Some(ide::InlayTooltip::Markdown(s)) => { + Some(lsp_types::InlayHintTooltip::MarkupContent(lsp_types::MarkupContent { + kind: lsp_types::MarkupKind::Markdown, + value: s, + })) + } + None => None, }; (lsp_types::InlayHintLabel::String(text), hint_tooltip) } @@ -661,31 +657,38 @@ fn inlay_hint_label( .parts .into_iter() .map(|part| { - let tooltip = if needs_resolve && fields_to_resolve.resolve_label_tooltip { - *something_to_resolve |= part.tooltip.is_some(); - None - } else { - match part.tooltip.and_then(|it| it.computed()) { - Some(ide::InlayTooltip::String(s)) => { - Some(lsp_types::InlayHintLabelPartTooltip::String(s)) - } - Some(ide::InlayTooltip::Markdown(s)) => { - Some(lsp_types::InlayHintLabelPartTooltip::MarkupContent( - lsp_types::MarkupContent { - kind: lsp_types::MarkupKind::Markdown, - value: s, - }, - )) - } - None => None, + let tooltip = part.tooltip.and_then(|it| match it { + LazyProperty::Computed(it) => Some(it), + LazyProperty::Lazy => { + *something_to_resolve |= fields_to_resolve.resolve_label_tooltip; + None } + }); + let tooltip = match tooltip { + Some(ide::InlayTooltip::String(s)) => { + Some(lsp_types::InlayHintLabelPartTooltip::String(s)) + } + Some(ide::InlayTooltip::Markdown(s)) => { + Some(lsp_types::InlayHintLabelPartTooltip::MarkupContent( + lsp_types::MarkupContent { + kind: lsp_types::MarkupKind::Markdown, + value: s, + }, + )) + } + None => None, }; - let location = if needs_resolve && fields_to_resolve.resolve_label_location { - *something_to_resolve |= part.linked_location.is_some(); - None - } else { - part.linked_location.map(|range| location(snap, range)).transpose()? - }; + let location = part + .linked_location + .and_then(|it| match it { + LazyProperty::Computed(it) => Some(it), + LazyProperty::Lazy => { + *something_to_resolve |= fields_to_resolve.resolve_label_location; + None + } + }) + .map(|range| location(snap, range)) + .transpose()?; Ok(lsp_types::InlayHintLabelPart { value: part.text, tooltip, diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index dca231604fa13..ff027ac5848b3 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -411,6 +411,11 @@ pub fn path_from_text(text: &str) -> ast::Path { ast_from_text(&format!("fn main() {{ let test: {text}; }}")) } +// FIXME: should not be pub +pub fn path_from_text_with_edition(text: &str, edition: Edition) -> ast::Path { + ast_from_text_with_edition(&format!("fn main() {{ let test: {text}; }}"), edition) +} + pub fn use_tree_glob() -> ast::UseTree { ast_from_text("use *;") } @@ -1230,7 +1235,12 @@ pub fn token_tree( #[track_caller] fn ast_from_text(text: &str) -> N { - let parse = SourceFile::parse(text, Edition::CURRENT); + ast_from_text_with_edition(text, Edition::CURRENT) +} + +#[track_caller] +fn ast_from_text_with_edition(text: &str, edition: Edition) -> N { + let parse = SourceFile::parse(text, edition); let node = match parse.tree().syntax().descendants().find_map(N::cast) { Some(it) => it, None => { diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index fd06736a25248..4ed68d18e8071 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -53,6 +53,7 @@ //! pin: //! pointee: copy, send, sync, ord, hash, unpin //! range: +//! receiver: deref //! result: //! send: sized //! size_of: sized @@ -513,10 +514,26 @@ pub mod ops { fn deref_mut(&mut self) -> &mut Self::Target; } // endregion:deref_mut + + // region:receiver + #[lang = "receiver"] + pub trait Receiver { + #[lang = "receiver_target"] + type Target: ?Sized; + } + + impl Receiver for P + where + P: Deref, + { + type Target = T; + } + // endregion:receiver } pub use self::deref::{ Deref, DerefMut, // :deref_mut + Receiver, // :receiver }; // endregion:deref diff --git a/src/tools/rust-analyzer/docs/dev/README.md b/src/tools/rust-analyzer/docs/dev/README.md index 3ba492e095974..c990212d585d2 100644 --- a/src/tools/rust-analyzer/docs/dev/README.md +++ b/src/tools/rust-analyzer/docs/dev/README.md @@ -269,19 +269,13 @@ Note: we tag releases by dates, releasing a patch release on the same day should ## Permissions -There are three sets of people with extra permissions: +There are two sets of people with extra permissions: -* rust-analyzer GitHub organization [**admins**](https://github.com/orgs/rust-analyzer/people?query=role:owner) (which include current t-compiler leads). - Admins have full access to the org. -* [**review**](https://github.com/orgs/rust-analyzer/teams/review) team in the organization. - Reviewers have `r+` access to all of organization's repositories and publish rights on crates.io. - They also have direct commit access, but all changes should via bors queue. +* The [rust-lang](https://github.com/rust-lang) team [t-rust-analyzer](https://github.com/rust-lang/team/blob/master/teams/rust-analyzer.toml). + This team has write access to the repository and merge queue permissions (note the repo itself is managed by infra admins). It's ok to self-approve if you think you know what you are doing! - bors should automatically sync the permissions. Feel free to request a review or assign any PR to a reviewer with the relevant expertise to bring the work to their attention. Don't feel pressured to review assigned PRs though. - If you don't feel like reviewing for whatever reason, someone else will pick the review up! -* [**triage**](https://github.com/orgs/rust-analyzer/teams/triage) team in the organization. - This team can label and close issues. - -Note that at the time being you need to be a member of the org yourself to view the links. + If you don't feel like reviewing for whatever reason, someone else will pick the review up (but please speak up if you don't feel like it)! +* The [rust-lang](https://github.com/rust-lang) team [t-rust-analyzer-contributors]([https://github.com/orgs/rust-analyzer/teams/triage](https://github.com/rust-lang/team/blob/master/teams/rust-analyzer-contributors.toml)). + This team has general triaging permissions allowing to label, close and re-open issues. diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md index a632fc6f5fb87..c7ee4e402363c 100644 --- a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md +++ b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@