Merge pull request #22994 from A4-Tacks/useless-ty-term-search-assist fix: term_search exclude useless target type
diff --git a/Cargo.lock b/Cargo.lock index 7a2e2d4..19bdd0c 100644 --- a/Cargo.lock +++ b/Cargo.lock
@@ -453,7 +453,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -464,7 +464,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -524,7 +524,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -698,9 +698,9 @@ [[package]] name = "gen-lsp-types" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd635c5206acd03ea024d6b5902539e5c903de3afa220fdb5c94b583af77f4f" +checksum = "b64887ac3a8083427ae935a7296db876871582cd57eac077564f8bc18fa49116" dependencies = [ "serde", "serde_json", @@ -1471,7 +1471,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] @@ -1857,7 +1857,7 @@ checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.117", ] [[package]] @@ -2104,7 +2104,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2183,7 +2183,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] @@ -2296,9 +2296,9 @@ [[package]] name = "rowan" -version = "0.15.19" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2441aaccb50f4267d4f0f58b21e0138e96a449f361ed57a2673a83c9bca0772" +checksum = "14b574c58582fa59fa43a2feb6608b8744184659f08a2e0117e4b8224d95ed61" dependencies = [ "countme", "hashbrown 0.14.5", @@ -2447,9 +2447,9 @@ [[package]] name = "salsa" -version = "0.27.2" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbaab832e2ea754afda4a738f987dd1e8bd30c9e5d8c981ee6a3934386095e2" +checksum = "cf0e374215cd2db2b5c75d7b3a99cb0cc052c0595335dfdefc03d4eb08f4aa81" dependencies = [ "boxcar", "crossbeam-queue", @@ -2474,20 +2474,19 @@ [[package]] name = "salsa-macro-rules" -version = "0.27.2" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de6872462ac73d39969a836273c24163e6a26a4e08f5114fcd80e25af30ea9c6" +checksum = "85f4b7d4405540bbd6d4ffa52d4322d983f3781954d3073067ac1bdb028459b3" [[package]] name = "salsa-macros" -version = "0.27.2" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc78ffaf65b1a9175818592c5130aa10b1bb245a905722fd4db87cea8a8457" +checksum = "445be2bfbb2f67cb663225ecd7bc5a25370c0250fca30f9d8cbad9a913650370" dependencies = [ "proc-macro2", "quote", - "syn", - "synstructure", + "syn 3.0.3", ] [[package]] @@ -2551,7 +2550,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2696,6 +2695,17 @@ ] [[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] name = "synstructure" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2703,7 +2713,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2714,6 +2724,7 @@ "expect-test", "itertools 0.15.0", "parser", + "ra-ap-rustc_lexer", "rayon", "rowan", "rustc-hash 2.1.2", @@ -2842,7 +2853,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2853,7 +2864,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -2999,6 +3010,7 @@ version = "0.0.0" dependencies = [ "camino", + "semver", ] [[package]] @@ -3020,7 +3032,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3282,7 +3294,7 @@ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wasm-bindgen-shared", ] @@ -3401,7 +3413,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3412,7 +3424,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3589,7 +3601,7 @@ "heck", "indexmap", "prettyplease", - "syn", + "syn 2.0.117", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -3605,7 +3617,7 @@ "prettyplease", "proc-macro2", "quote", - "syn", + "syn 2.0.117", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -3728,7 +3740,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] @@ -3749,7 +3761,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]] @@ -3769,7 +3781,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", "synstructure", ] @@ -3803,7 +3815,7 @@ dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.117", ] [[package]]
diff --git a/Cargo.toml b/Cargo.toml index 4ef92c5..e7f2b35 100644 --- a/Cargo.toml +++ b/Cargo.toml
@@ -123,17 +123,17 @@ pulldown-cmark-to-cmark = "10.0.4" pulldown-cmark = { version = "0.9.6", default-features = false } rayon = "1.10.0" -rowan = "=0.15.19" +rowan = "0.17.0" # Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work # on impls without it -salsa = { version = "0.27.0", default-features = false, features = [ +salsa = { version = "0.28.2", default-features = false, features = [ "rayon", "salsa_unstable", "macros", "inventory", "triomphe" ] } -salsa-macros = "0.27.0" +salsa-macros = "0.28.2" semver = "1.0.26" serde = { version = "1.0.219" } serde_derive = { version = "1.0.219" }
diff --git a/crates/base-db/src/editioned_file_id.rs b/crates/base-db/src/editioned_file_id.rs index f674c5d..6b32469 100644 --- a/crates/base-db/src/editioned_file_id.rs +++ b/crates/base-db/src/editioned_file_id.rs
@@ -10,15 +10,16 @@ use crate::SourceDatabase; -#[salsa::interned(debug, constructor = from_span_file_id, no_lifetime)] +#[salsa::interned(debug, constructor = from_span_file_id, unsafe(no_lifetime), revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct EditionedFileId { + #[returns(copy)] field: span::EditionedFileId, } #[salsa::tracked] impl EditionedFileId { - #[salsa::tracked(lru = 128)] + #[salsa::tracked(lru = 128, returns(clone))] pub fn parse(self, db: &dyn SourceDatabase) -> syntax::Parse<ast::SourceFile> { let _p = tracing::info_span!("parse", ?self).entered(); let (file_id, edition) = self.unpack(db);
diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs index 4beeae9..37f617b 100644 --- a/crates/base-db/src/lib.rs +++ b/crates/base-db/src/lib.rs
@@ -6,40 +6,7 @@ #[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; -pub mod salsa { - pub use salsa::*; - - // Adjusted from `salsa::update_fallback` to work with database owned T - /// "Fallback" for maybe-update that is suitable for database owned T - /// that implement `Eq`. In this version, we update only if the new value - /// is not `Eq` to the old one. Note that given `Eq` impls that are not just - /// structurally comparing fields, this may cause us not to update even if - /// the value has changed (presumably because this change is not semantically - /// significant). - /// - /// # Safety - /// - /// See `Update::maybe_update`, additionally, `'db` is required to be `'static` or the lifetime - /// of the database `T` belongs to. - pub unsafe fn update_fallback_db<'db, T>(old_pointer: *mut T, new_value: T) -> bool - where - T: 'db + PartialEq, - { - // SAFETY: Because everything is owned, this ref is simply a valid `&mut` - let old_ref: &mut T = unsafe { &mut *old_pointer }; - - if *old_ref != new_value { - *old_ref = new_value; - true - } else { - // Subtle but important: Eq impls can be buggy or define equality - // in surprising ways. If it says that the value has not changed, - // we do not modify the existing value, and thus do not have to - // update the revision, as downstream code will not see the new value. - false - } - } -} +pub use salsa; pub use salsa_macros; use span::TextSize; @@ -79,7 +46,7 @@ #[macro_export] macro_rules! impl_intern_key { ($id:ident, $loc:ident) => { - #[salsa::interned(no_lifetime, revisions = usize::MAX)] + #[salsa::interned(unsafe(no_lifetime), revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct $id { #[returns(ref)] @@ -255,11 +222,13 @@ #[salsa::input(debug)] pub struct FileSourceRootInput { + #[returns(copy)] pub source_root_id: SourceRootId, } #[salsa::input(debug)] pub struct SourceRootInput { + #[returns(clone)] pub source_root: Arc<SourceRoot>, } @@ -363,6 +332,7 @@ #[salsa::input(singleton, debug)] struct AllCrates { + #[returns(clone)] crates: std::sync::Arc<[Crate]>, } @@ -389,6 +359,7 @@ #[doc(hidden)] #[salsa::interned] pub struct InternedSourceRootId { + #[returns(copy)] pub id: SourceRootId, }
diff --git a/crates/hir-def/src/attrs.rs b/crates/hir-def/src/attrs.rs index d55509e..d71cad4 100644 --- a/crates/hir-def/src/attrs.rs +++ b/crates/hir-def/src/attrs.rs
@@ -201,7 +201,7 @@ let segment4 = segment4.and_then(|it| it.segment()?.name_ref()); segment1.text() == "test" && segment3.is_none_or(|it| it.text() == "prelude") - && segment4.is_none_or(|it| matches!(&*it.text(), "core" | "std")) + && segment4.is_none_or(|it| matches!(it.text(), "core" | "std")) }); if is_test { attr_flags.insert(AttrFlags::IS_TEST); @@ -593,7 +593,7 @@ #[salsa::tracked] impl AttrFlags { - #[salsa::tracked] + #[salsa::tracked(returns(copy))] pub fn query(db: &dyn SourceDatabase, owner: AttrDefId) -> AttrFlags { let mut attr_flags = AttrFlags::empty(); collect_attrs(db, owner, |attr| match_attr_flags(&mut attr_flags, attr)); @@ -740,7 +740,7 @@ return lang_item(db, owner); - #[salsa::tracked] + #[salsa::tracked(returns(clone))] fn lang_item(db: &dyn SourceDatabase, owner: AttrDefId) -> Option<Symbol> { collect_attrs(db, owner, |attr| { if let ast::Meta::KeyValueMeta(attr) = attr @@ -769,7 +769,7 @@ /// /// Prefer [`AttrFlags::repr()`] in non-perf-sensitive places as it also has a check that /// that the ADT has repr. - #[salsa::tracked] + #[salsa::tracked(returns(copy))] pub fn repr_assume_has(db: &dyn SourceDatabase, owner: AdtId) -> Option<ReprOptions> { let mut result = None; collect_attrs::<Infallible>(db, owner.into(), |attr| { @@ -922,7 +922,7 @@ return rustc_layout_scalar_valid_range(db, owner); - #[salsa::tracked] + #[salsa::tracked(returns(copy))] fn rustc_layout_scalar_valid_range( db: &dyn SourceDatabase, owner: AdtId, @@ -1049,7 +1049,7 @@ } return doc_keyword(db, owner); - #[salsa::tracked] + #[salsa::tracked(returns(clone))] fn doc_keyword(db: &dyn SourceDatabase, owner: ModuleId) -> Option<Symbol> { collect_attrs(db, AttrDefId::ModuleId(owner), |attr| { if let ast::Meta::TokenTreeMeta(attr) = attr @@ -1166,7 +1166,7 @@ return unstable_feature(db, owner); - #[salsa::tracked] + #[salsa::tracked(returns(clone))] fn unstable_feature(db: &dyn SourceDatabase, owner: AttrDefId) -> Option<Symbol> { collect_attrs(db, owner, |attr| { if let ast::Meta::TokenTreeMeta(attr) = attr
diff --git a/crates/hir-def/src/attrs/docs.rs b/crates/hir-def/src/attrs/docs.rs index 55cb6f8..0dd20c1 100644 --- a/crates/hir-def/src/attrs/docs.rs +++ b/crates/hir-def/src/attrs/docs.rs
@@ -325,8 +325,8 @@ struct DocMacroExpander<'db> { db: &'db dyn SourceDatabase, krate: Crate, - recursion_depth: usize, - recursion_limit: usize, + macro_depth: u32, + recursion_limit: u32, resolver: Resolver<'db>, file_id: HirFileId, ast_id_map: &'db AstIdMap, @@ -357,7 +357,7 @@ expander: &mut DocMacroExpander<'db>, macro_call: ast::MacroCall, ) -> Option<String> { - if expander.recursion_depth >= expander.recursion_limit { + if expander.macro_depth >= expander.recursion_limit { return None; } @@ -374,6 +374,7 @@ call_site.ctx, ExpandTo::Expr, expander.krate, + expander.macro_depth + 1, |path| { expander.resolver.resolve_path_as_macro_def(expander.db, path, Some(MacroSubNs::Bang)) }, @@ -394,14 +395,14 @@ std::mem::replace(&mut expander.span_map, SpanMap::ExpansionSpanMap(span_map)); let old_ast_id_map = std::mem::replace(&mut expander.ast_id_map, expansion_file_id.ast_id_map(expander.db)); - expander.recursion_depth += 1; + expander.macro_depth += 1; let expansion = expand_doc_expr_via_macro_pipeline(expander, expr); expander.file_id = old_file_id; expander.span_map = old_span_map; expander.ast_id_map = old_ast_id_map; - expander.recursion_depth -= 1; + expander.macro_depth -= 1; expansion } @@ -444,11 +445,11 @@ let exp = expander.get_or_insert_with(|| { let resolver = make_resolver(); let def_map = resolver.top_level_def_map(); - let recursion_limit = def_map.recursion_limit() as usize; + let recursion_limit = def_map.recursion_limit(); DocMacroExpander { db, krate, - recursion_depth: 0, + macro_depth: file_id.macro_expansion_depth(db), recursion_limit, resolver, file_id,
diff --git a/crates/hir-def/src/expr_store.rs b/crates/hir-def/src/expr_store.rs index 32d1695..5e6633d 100644 --- a/crates/hir-def/src/expr_store.rs +++ b/crates/hir-def/src/expr_store.rs
@@ -117,7 +117,7 @@ pats: Arena<Pat>, bindings: Arena<Binding>, labels: Arena<Label>, - /// Id of the closure/coroutine that owns the corresponding binding. If a binding is owned by the + /// Id of the closure/coroutine/anon const that owns the corresponding binding. If a binding is owned by the /// top level expression, it will not be listed in here. binding_owners: FxHashMap<BindingId, ExprId>, /// Block expressions in this store that may contain inner items. @@ -613,8 +613,10 @@ visitor.on_expr_opt(*start); visitor.on_expr_opt(*end); } - Pat::Lit(expr) | Pat::ConstBlock(expr) | Pat::Expr(expr) => visitor.on_expr(*expr), - Pat::Path(_) | Pat::Wild | Pat::Missing | Pat::Rest | Pat::NotNull => {} + Pat::Lit(expr) | Pat::Expr(expr) => visitor.on_expr(*expr), + Pat::ConstBlock(expr) => visitor.on_anon_const_expr(*expr), + Pat::Path(path) => visitor.on_path(path), + Pat::Wild | Pat::Missing | Pat::Rest | Pat::NotNull => {} &Pat::Bind { subpat, id: _ } => visitor.on_pat_opt(subpat), Pat::Or(args) | Pat::Tuple { args, ellipsis: _ } => visitor.on_pats(args), Pat::TupleStruct { args, ellipsis: _, path } => { @@ -652,18 +654,6 @@ self.walk_pats_shallow(pat_id, |p| self.walk_pats(p, f)); } - pub fn is_binding_upvar(&self, binding: BindingId, relative_to: ExprId) -> bool { - let Some(expr_only) = &self.expr_only else { return false }; - match expr_only.binding_owners.get(&binding) { - Some(it) => { - // We assign expression ids in a way that outer closures will receive - // a higher id (allocated after their body is collected) - it.into_raw() > relative_to.into_raw() - } - None => true, - } - } - #[inline] pub fn binding_owner(&self, id: BindingId) -> Option<ExprId> { self.expr_only.as_ref()?.binding_owners.get(&id).copied() @@ -775,10 +765,8 @@ Expr::Field { expr, name: _ } | Expr::Await { expr } | Expr::Ref { expr, mutability: _, rawness: _ } - | Expr::UnaryOp { expr, op: _ } - | Expr::Const(expr) => { - visitor.on_expr(*expr); - } + | Expr::UnaryOp { expr, op: _ } => visitor.on_expr(*expr), + Expr::Const(expr) => visitor.on_anon_const_expr(*expr), Expr::Tuple { exprs } => visitor.on_exprs(exprs), Expr::Array(a) => match a { Array::ElementList { elements } => visitor.on_exprs(elements), @@ -1003,7 +991,7 @@ } } -trait StoreVisitorExt: StoreVisitor { +pub trait StoreVisitorExt: StoreVisitor { fn on_type_bound(&mut self, bound: &TypeBound) { match bound { TypeBound::Path(path_id, _) => self.on_type(path_id.type_ref()),
diff --git a/crates/hir-def/src/expr_store/expander.rs b/crates/hir-def/src/expr_store/expander.rs index c40f244..a974815 100644 --- a/crates/hir-def/src/expr_store/expander.rs +++ b/crates/hir-def/src/expr_store/expander.rs
@@ -24,9 +24,9 @@ span_map: SpanMap<'db>, current_file_id: HirFileId, ast_id_map: &'db AstIdMap, - /// `recursion_depth == usize::MAX` indicates that the recursion limit has been reached. - recursion_depth: u32, - recursion_limit: usize, + /// `recursion_depth == u32::MAX` indicates that the recursion limit has been reached. + macro_depth: u32, + recursion_limit: u32, } impl<'db> Expander<'db> { @@ -35,7 +35,7 @@ current_file_id: HirFileId, def_map: &'db DefMap, ) -> Expander<'db> { - let recursion_limit = def_map.recursion_limit() as usize; + let recursion_limit = def_map.recursion_limit(); let recursion_limit = if cfg!(test) { // Without this, `body::tests::your_stack_belongs_to_me` stack-overflows in debug std::cmp::min(32, recursion_limit) @@ -44,7 +44,7 @@ }; Expander { current_file_id, - recursion_depth: 0, + macro_depth: current_file_id.macro_expansion_depth(db), recursion_limit, span_map: current_file_id.span_map(db), ast_id_map: current_file_id.ast_id_map(db), @@ -111,6 +111,7 @@ call_site.ctx, expands_to, krate, + this.macro_depth, |path| resolver(path).map(|it| it.definition(db)), eager_callback, ) { @@ -137,14 +138,14 @@ self.span_map = span_map; self.current_file_id = file_id; self.ast_id_map = ast_id_map; - if self.recursion_depth == u32::MAX { + if self.macro_depth == u32::MAX { // Recursion limit has been reached somewhere in the macro expansion tree. Reset the // depth only when we get out of the tree. if !self.current_file_id.is_macro() { - self.recursion_depth = 0; + self.macro_depth = 0; } } else { - self.recursion_depth -= 1; + self.macro_depth -= 1; } bomb.defuse(); } @@ -165,7 +166,7 @@ where F: FnOnce(&mut Self) -> ExpandResult<Option<MacroCallId>>, { - if self.recursion_depth == u32::MAX { + if self.macro_depth == u32::MAX { // Recursion limit has been reached somewhere in the macro expansion tree. We should // stop expanding other macro calls in this tree, or else this may result in // exponential number of macro expansions, leading to a hang. @@ -180,8 +181,8 @@ let Some(call_id) = value else { return ExpandResult { value: None, err }; }; - if self.recursion_depth as usize > self.recursion_limit { - self.recursion_depth = u32::MAX; + if self.macro_depth > self.recursion_limit { + self.macro_depth = u32::MAX; cov_mark::hit!(your_stack_belongs_to_me); return ExpandResult::only_err(ExpandError::new( call_id.macro_arg_considering_derives(db, &call_id.lookup(db).kind).2, @@ -196,7 +197,7 @@ value: { let parse = res.value.0.clone().cast::<T>(); - self.recursion_depth += 1; + self.macro_depth += 1; let old_file_id = std::mem::replace(&mut self.current_file_id, call_id.into()); let old_span_map = std::mem::replace(&mut self.span_map, SpanMap::ExpansionSpanMap(&res.value.1));
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs index df4fc6e..1e5d9c2 100644 --- a/crates/hir-def/src/expr_store/lower.rs +++ b/crates/hir-def/src/expr_store/lower.rs
@@ -665,7 +665,7 @@ lifetime: ast::Lifetime, ) -> LifetimeRefId { // FIXME: Keyword check? - let lifetime_ref = match &*lifetime.text() { + let lifetime_ref = match lifetime.text() { "" | "'" => LifetimeRef::Error, "'static" => LifetimeRef::Static, "'_" => LifetimeRef::Placeholder, @@ -1295,7 +1295,7 @@ match binder.generic_param_list() { Some(gpl) => gpl .lifetime_params() - .flat_map(|lp| lp.lifetime().map(|lt| Name::new_lifetime(<.text()))) + .flat_map(|lp| lp.lifetime().map(|lt| Name::new_lifetime(lt.text()))) .collect(), None => ThinVec::default(), } @@ -3175,7 +3175,7 @@ name: ast_label .lifetime() .as_ref() - .map_or_else(Name::missing, |lt| Name::new_lifetime(<.text())), + .map_or_else(Name::missing, |lt| Name::new_lifetime(lt.text())), }; self.alloc_label(label, AstPtr::new(&ast_label)) } @@ -3195,7 +3195,7 @@ (hygiene_id.syntax_context().parent(self.db), expansion.def) }) }; - let name = Name::new_lifetime(&lifetime.text()); + let name = Name::new_lifetime(lifetime.text()); for (rib_idx, rib) in self.label_ribs.iter().enumerate().rev() { match &rib.kind { @@ -3331,7 +3331,7 @@ Some((hir_lit, ast_lit)) } -impl ExprCollector<'_> { +impl<'db> ExprCollector<'db> { fn with_fresh_binding_expr_root(&mut self, f: impl FnOnce(&mut Self) -> ExprId) -> ExprId { self.with_expr_root(|this| this.with_binding_owner(f)) } @@ -3482,19 +3482,16 @@ } } - fn extend_type_alias_lifetime(&mut self, lifetimes: impl Iterator<Item = Name>) { - self.named_lifetime_store.lifetimes_constrained_by_input.extend(lifetimes); - } - fn is_argument_lt_bound_scope(&mut self) -> bool { matches!(self.named_lifetime_store.lifetime_bound_scope, Some(LifetimeBoundScope::Argument)) } - fn get_constrained_lifetimes_if_type_alias( + fn get_constrained_lifetimes_if_type_alias<'a>( &mut self, - mod_path: &intern::Interned<ModPath>, - generic_args: Option<&GenericArgs>, - ) -> Option<FxIndexSet<Name>> { + mod_path: &'a ModPath, + generic_args: Option<&'a GenericArgs>, + ) -> Option<impl Iterator<Item = LifetimeRefId> + use<'a, 'db>> { + let generic_args = generic_args?; let r_path = self.def_map.resolve_path( self.local_def_map, self.db, @@ -3503,33 +3500,20 @@ BuiltinShadowMode::Module, None, ); - let def_id = r_path.0.types.map(|item| item.def)?; - let res = if let crate::ModuleDefId::TypeAliasId(id) = def_id { - let Some(generic_args) = generic_args else { return Some(FxIndexSet::default()) }; + let ModuleDefId::TypeAliasId(id) = r_path.0.take_types()? else { return None }; - let constrained_lt_indices = get_constrained_lifetimes(self.db, id); - let res = constrained_lt_indices + let constrained_lt_indices = get_constrained_lifetimes(self.db, id); + let res = constrained_lt_indices.iter().filter_map(|&idx| { + generic_args + .args .iter() - .filter_map(|&idx| { - let lt_ref = generic_args - .args - .iter() - .filter_map(|arg| match arg { - &GenericArg::Lifetime(lt_ref) => Some(lt_ref), - GenericArg::Type(_) | GenericArg::Const(_) => None, - }) - .nth(idx as usize)?; - match &self.store.lifetimes[lt_ref] { - LifetimeRef::Named(name) => Some(name.clone()), - _ => None, - } + .filter_map(|arg| match arg { + &GenericArg::Lifetime(lt_ref) => Some(lt_ref), + GenericArg::Type(_) | GenericArg::Const(_) => None, }) - .collect(); - Some(res) - } else { - None - }; - return res; + .nth(idx as usize) + }); + return Some(res); #[salsa::tracked(returns(deref), cycle_result = get_constrained_lifetimes_cycle_result)] fn get_constrained_lifetimes(
diff --git a/crates/hir-def/src/expr_store/lower/asm.rs b/crates/hir-def/src/expr_store/lower/asm.rs index 63a0594..fb0a5b0 100644 --- a/crates/hir-def/src/expr_store/lower/asm.rs +++ b/crates/hir-def/src/expr_store/lower/asm.rs
@@ -39,7 +39,7 @@ Some(InlineAsmRegOrRegClass::Reg(Symbol::intern(string.text()))) } else { reg.name_ref().map(|name_ref| { - InlineAsmRegOrRegClass::RegClass(Symbol::intern(&name_ref.text())) + InlineAsmRegOrRegClass::RegClass(Symbol::intern(name_ref.text())) }) } }; @@ -69,7 +69,7 @@ continue; } ast::AsmPiece::AsmOperandNamed(op) => { - let name = op.name().map(|name| Symbol::intern(&name.text())); + let name = op.name().map(|name| Symbol::intern(name.text())); if let Some(name) = &name { named_args.insert(name.clone(), slot); named_pos.insert(slot, name.clone());
diff --git a/crates/hir-def/src/expr_store/lower/format_args.rs b/crates/hir-def/src/expr_store/lower/format_args.rs index 5552213..ad548c6 100644 --- a/crates/hir-def/src/expr_store/lower/format_args.rs +++ b/crates/hir-def/src/expr_store/lower/format_args.rs
@@ -1,10 +1,13 @@ //! Lowering of `format_args!()`. use base_db::FxIndexSet; -use hir_expand::name::Name; +use hir_expand::name::{AsName, Name}; use intern::{Symbol, sym}; use span::SyntaxContext; -use syntax::{AstPtr, AstToken as _, ast}; +use syntax::{ + AstPtr, AstToken as _, + ast::{self, HasName}, +}; use crate::{ expr_store::{HygieneId, lower::ExprCollector, path::Path}, @@ -29,8 +32,8 @@ f.args().for_each(|arg| { let expr = arg.expr(); args.add(FormatArgument { - kind: match arg.arg_name() { - Some(name) => FormatArgumentKind::Named(Name::new_root(name.name().text())), + kind: match arg.name() { + Some(name) => FormatArgumentKind::Named(name.as_name()), None => FormatArgumentKind::Normal, }, syntax: expr.as_ref().map(AstPtr::new),
diff --git a/crates/hir-def/src/expr_store/lower/generics.rs b/crates/hir-def/src/expr_store/lower/generics.rs index ce6e736..65877fb 100644 --- a/crates/hir-def/src/expr_store/lower/generics.rs +++ b/crates/hir-def/src/expr_store/lower/generics.rs
@@ -181,7 +181,7 @@ .map(|lifetime_param| { lifetime_param .lifetime() - .map_or_else(Name::missing, |lt| Name::new_lifetime(<.text())) + .map_or_else(Name::missing, |lt| Name::new_lifetime(lt.text())) }) .collect() });
diff --git a/crates/hir-def/src/expr_store/lower/path.rs b/crates/hir-def/src/expr_store/lower/path.rs index 5d45a4f..8f71ea8 100644 --- a/crates/hir-def/src/expr_store/lower/path.rs +++ b/crates/hir-def/src/expr_store/lower/path.rs
@@ -5,9 +5,12 @@ use std::iter; -use crate::expr_store::{ - lower::{ExprCollector, generics::ImplTraitLowerFn}, - path::NormalPath, +use crate::{ + expr_store::{ + lower::{ExprCollector, generics::ImplTraitLowerFn}, + path::NormalPath, + }, + type_ref::LifetimeRef, }; use hir_expand::{ @@ -255,15 +258,20 @@ let mod_path = Interned::new(ModPath::from_segments(kind, segments)); - let type_alias_constrained_lifetimes = collector.get_constrained_lifetimes_if_type_alias( - &mod_path, - generic_args.last().and_then(|g| g.as_ref()), - ); if let Some(old_lifetimes_constrained_by_input) = old_lifetimes_constrained_by_input { + let type_alias_constrained_lifetimes = collector.get_constrained_lifetimes_if_type_alias( + &mod_path, + generic_args.last().and_then(|g| g.as_ref()), + ); if let Some(lifetimes) = type_alias_constrained_lifetimes { collector.named_lifetime_store.lifetimes_constrained_by_input = old_lifetimes_constrained_by_input; - collector.extend_type_alias_lifetime(lifetimes.into_iter()); + collector.named_lifetime_store.lifetimes_constrained_by_input.extend( + lifetimes.filter_map(|lt_ref| match &collector.store.lifetimes[lt_ref] { + LifetimeRef::Named(name) => Some(name.clone()), + _ => None, + }), + ); } else { collector .named_lifetime_store
diff --git a/crates/hir-def/src/expr_store/scope.rs b/crates/hir-def/src/expr_store/scope.rs index d6568b8..f0a4b5a 100644 --- a/crates/hir-def/src/expr_store/scope.rs +++ b/crates/hir-def/src/expr_store/scope.rs
@@ -1,13 +1,15 @@ //! Name resolution for expressions. +use std::mem; + use base_db::SourceDatabase; use hir_expand::{MacroDefId, name::Name}; use la_arena::{Arena, ArenaMap, Idx, IdxRange, RawIdx}; use crate::{ BlockId, DefWithBodyId, ExpressionStoreOwnerId, GenericDefId, VariantId, - expr_store::{Body, ExpressionStore, HygieneId, StoreVisitor, body::Param}, + expr_store::{Body, ExpressionStore, HygieneId, StoreVisitor, StoreVisitorExt, body::Param}, hir::{ - Array, Binding, BindingId, Expr, ExprId, Item, LabelId, Pat, PatId, Statement, + Binding, BindingId, Expr, ExprId, Item, LabelId, Pat, PatId, Statement, generics::GenericParams, }, signatures::VariantFields, @@ -162,12 +164,14 @@ body.expr_only.as_ref().map_or(0, |it| it.exprs.len()), ), }; - let mut root = scopes.root_scope(); + let root = scopes.root_scope(); if let Some(Param { formal: self_param, user_written: _ }) = body.self_param { scopes.add_bindings(body, root, self_param, body.binding_hygiene(self_param)); } - body.params.iter().for_each(|param| scopes.add_pat_bindings(body, root, param.formal)); - compute_expr_scopes(body.root_expr(), body, &mut scopes, &mut { root }, &mut root); + let mut visitor = + ExprScopeVisitor { store: body, scopes: &mut scopes, scope: root, const_scope: root }; + body.params.iter().for_each(|param| visitor.on_pat(param.formal)); + visitor.on_expr(body.root_expr()); scopes } @@ -181,8 +185,9 @@ }; let root = scopes.root_scope(); for root_expr in roots { - let mut scope = scopes.new_scope(root); - compute_expr_scopes(root_expr, store, &mut scopes, &mut { scope }, &mut scope); + let scope = scopes.new_scope(root); + ExprScopeVisitor { store, scopes: &mut scopes, scope, const_scope: scope } + .on_expr(root_expr); } scopes } @@ -254,15 +259,6 @@ IdxRange::new_inclusive(self.scopes[scope].entries.start()..=entry); } - fn add_pat_bindings(&mut self, store: &ExpressionStore, scope: ScopeId, pat: PatId) { - let pattern = &store[pat]; - if let Pat::Bind { id, .. } = *pattern { - self.add_bindings(store, scope, id, store.binding_hygiene(id)); - } - - pattern.walk_child_pats(|pat| self.add_pat_bindings(store, scope, pat)); - } - fn set_scope(&mut self, node: ExprId, scope: ScopeId) { self.scope_by_expr.insert(node, scope); } @@ -278,180 +274,131 @@ struct ExprScopeVisitor<'a> { store: &'a ExpressionStore, scopes: &'a mut ExprScopes, - scope: &'a mut ScopeId, - const_scope: &'a mut ScopeId, + scope: ScopeId, + const_scope: ScopeId, +} + +impl ExprScopeVisitor<'_> { + fn with_scope(&mut self, scope: ScopeId, f: impl FnOnce(&mut Self)) { + let old_scope = mem::replace(&mut self.scope, scope); + f(self); + self.scope = old_scope; + } + + fn visit_block( + &mut self, + expr: ExprId, + id: Option<BlockId>, + statements: &[Statement], + tail: Option<ExprId>, + label: Option<LabelId>, + ) { + let scope = self.scopes.new_block_scope(self.scope, id, label); + self.with_scope(scope, |this| { + let old_const_scope = if id.is_some() { + let const_scope = this.scopes.new_block_scope(this.const_scope, id, None); + mem::replace(&mut this.const_scope, const_scope) + } else { + // We don't need to allocate a new scope, since only items matter to us. + this.const_scope + }; + // Overwrite the old scope for the block expr, so that every block scope can be found + // via the block itthis (important for blocks that only contain items, no expressions). + this.scopes.set_scope(expr, this.scope); + + for stmt in statements { + match stmt { + Statement::Let { pat, initializer, else_branch, type_ref } => { + this.on_type_opt(*type_ref); + this.on_expr_opt(*initializer); + this.on_expr_opt(*else_branch); + this.scope = this.scopes.new_scope(this.scope); + this.on_pat(*pat); + } + Statement::Expr { expr, has_semi: _ } => this.on_expr(*expr), + Statement::Item(Item::MacroDef(macro_id)) => { + this.scope = this.scopes.new_macro_def_scope(this.scope, macro_id.clone()); + this.const_scope = + this.scopes.new_macro_def_scope(this.const_scope, macro_id.clone()); + } + Statement::Item(Item::Other) => (), + } + } + this.on_expr_opt(tail); + + this.const_scope = old_const_scope; + }); + } } impl StoreVisitor for ExprScopeVisitor<'_> { fn on_expr(&mut self, expr: ExprId) { - compute_expr_scopes(expr, self.store, self.scopes, self.scope, self.const_scope); + self.scopes.set_scope(expr, self.scope); + match &self.store[expr] { + Expr::Block { statements, tail, id, label } => { + self.visit_block(expr, *id, statements, *tail, *label); + } + Expr::Unsafe { id, statements, tail } => { + self.visit_block(expr, *id, statements, *tail, None); + } + Expr::Loop { body, label, source: _ } => { + let scope = self.scopes.new_labeled_scope(self.scope, *label); + self.with_scope(scope, |this| this.on_expr(*body)); + } + Expr::Closure { args, arg_types, ret_type, body, capture_by: _, closure_kind: _ } => { + arg_types.iter().for_each(|type_ref| self.on_type_opt(*type_ref)); + self.on_type_opt(*ret_type); + let scope = self.scopes.new_scope(self.scope); + self.with_scope(scope, |this| { + this.on_pats(args); + this.on_expr(*body); + }); + } + Expr::Match { expr, arms } => { + self.on_expr(*expr); + for arm in arms.iter() { + let scope = self.scopes.new_scope(self.scope); + self.with_scope(scope, |this| { + this.on_pat(arm.pat); + this.on_expr_opt(arm.guard); + this.on_expr(arm.expr); + }); + } + } + &Expr::If { condition, then_branch, else_branch } => { + let then_branch_scope = self.scopes.new_scope(self.scope); + self.with_scope(then_branch_scope, |this| { + this.on_expr(condition); + this.on_expr(then_branch); + }); + self.on_expr_opt(else_branch); + } + &Expr::Let { pat, expr } => { + self.on_expr(expr); + self.scope = self.scopes.new_scope(self.scope); + self.on_pat(pat); + } + _ => self.store.visit_expr_children(expr, self), + } } fn on_anon_const_expr(&mut self, expr: ExprId) { - let mut scope = *self.const_scope; - compute_expr_scopes(expr, self.store, self.scopes, &mut scope, self.const_scope); + self.with_scope(self.const_scope, |this| this.on_expr(expr)); } fn on_pat(&mut self, pat: PatId) { - self.store.visit_pat_children(pat, &mut *self); + if let Pat::Bind { id, .. } = self.store[pat] { + self.scopes.add_bindings(self.store, self.scope, id, self.store.binding_hygiene(id)); + } + + self.store.visit_pat_children(pat, self); } fn on_type(&mut self, ty: TypeRefId) { - self.store.visit_type_ref_children(ty, &mut *self); + self.with_scope(self.const_scope, |this| self.store.visit_type_ref_children(ty, this)); } } -fn compute_type_scopes( - ty: TypeRefId, - store: &ExpressionStore, - scopes: &mut ExprScopes, - const_scope: &mut ScopeId, -) { - let mut scope = *const_scope; - ExprScopeVisitor { store, scopes, scope: &mut scope, const_scope }.on_type(ty); -} - -fn compute_block_scopes( - statements: &[Statement], - tail: Option<ExprId>, - store: &ExpressionStore, - scopes: &mut ExprScopes, - scope: &mut ScopeId, - const_scope: &mut ScopeId, -) { - for stmt in statements { - match stmt { - Statement::Let { pat, initializer, else_branch, type_ref } => { - if let Some(type_ref) = type_ref { - compute_type_scopes(*type_ref, store, scopes, const_scope); - } - if let Some(expr) = initializer { - compute_expr_scopes(*expr, store, scopes, scope, const_scope); - } - if let Some(expr) = else_branch { - compute_expr_scopes(*expr, store, scopes, scope, const_scope); - } - - *scope = scopes.new_scope(*scope); - scopes.add_pat_bindings(store, *scope, *pat); - } - Statement::Expr { expr, .. } => { - compute_expr_scopes(*expr, store, scopes, scope, const_scope); - } - Statement::Item(Item::MacroDef(macro_id)) => { - *scope = scopes.new_macro_def_scope(*scope, macro_id.clone()); - *const_scope = scopes.new_macro_def_scope(*const_scope, macro_id.clone()); - } - Statement::Item(Item::Other) => (), - } - } - if let Some(expr) = tail { - compute_expr_scopes(expr, store, scopes, scope, const_scope); - } -} - -fn compute_expr_scopes( - expr: ExprId, - store: &ExpressionStore, - scopes: &mut ExprScopes, - scope: &mut ScopeId, - const_scope: &mut ScopeId, -) { - let compute_expr_scopes = - |scopes: &mut ExprScopes, expr: ExprId, scope: &mut ScopeId, const_scope: &mut ScopeId| { - compute_expr_scopes(expr, store, scopes, scope, const_scope) - }; - let handle_block = |id, - statements, - tail, - label, - scopes: &mut ExprScopes, - scope: &mut ScopeId, - const_scope: &mut ScopeId| { - let mut scope = scopes.new_block_scope(*scope, id, label); - let mut const_scope = if id.is_some() { - scopes.new_block_scope(*const_scope, id, None) - } else { - // We don't need to allocate a new scope, since only items matter to us. - *const_scope - }; - // Overwrite the old scope for the block expr, so that every block scope can be found - // via the block itself (important for blocks that only contain items, no expressions). - scopes.set_scope(expr, scope); - compute_block_scopes(statements, tail, store, scopes, &mut scope, &mut const_scope); - }; - - scopes.set_scope(expr, *scope); - match &store[expr] { - Expr::Block { statements, tail, id, label } => { - handle_block(*id, statements, *tail, *label, scopes, scope, const_scope); - } - Expr::Const(id) => { - let mut scope = *const_scope; - compute_expr_scopes(scopes, *id, &mut scope, const_scope); - } - Expr::Array(Array::Repeat { initializer, repeat }) => { - compute_expr_scopes(scopes, *initializer, scope, const_scope); - let mut repeat_scope = *const_scope; - compute_expr_scopes(scopes, *repeat, &mut repeat_scope, const_scope); - } - Expr::Unsafe { id, statements, tail } => { - handle_block(*id, statements, *tail, None, scopes, scope, const_scope); - } - Expr::Loop { body: body_expr, label, source: _ } => { - let mut scope = scopes.new_labeled_scope(*scope, *label); - compute_expr_scopes(scopes, *body_expr, &mut scope, const_scope); - } - Expr::Closure { - args, - arg_types, - ret_type, - body: body_expr, - capture_by: _, - closure_kind: _, - } => { - arg_types - .iter() - .flatten() - .for_each(|type_ref| compute_type_scopes(*type_ref, store, scopes, const_scope)); - if let Some(type_ref) = ret_type { - compute_type_scopes(*type_ref, store, scopes, const_scope); - } - let mut scope = scopes.new_scope(*scope); - args.iter().for_each(|arg| scopes.add_pat_bindings(store, scope, *arg)); - compute_expr_scopes(scopes, *body_expr, &mut scope, const_scope); - } - Expr::Match { expr, arms } => { - compute_expr_scopes(scopes, *expr, scope, const_scope); - for arm in arms.iter() { - let mut scope = scopes.new_scope(*scope); - scopes.add_pat_bindings(store, scope, arm.pat); - if let Some(guard) = arm.guard { - scope = scopes.new_scope(scope); - compute_expr_scopes(scopes, guard, &mut scope, const_scope); - } - compute_expr_scopes(scopes, arm.expr, &mut scope, const_scope); - } - } - &Expr::If { condition, then_branch, else_branch } => { - let mut then_branch_scope = scopes.new_scope(*scope); - compute_expr_scopes(scopes, condition, &mut then_branch_scope, const_scope); - compute_expr_scopes(scopes, then_branch, &mut then_branch_scope, const_scope); - if let Some(else_branch) = else_branch { - compute_expr_scopes(scopes, else_branch, scope, const_scope); - } - } - &Expr::Let { pat, expr } => { - compute_expr_scopes(scopes, expr, scope, const_scope); - *scope = scopes.new_scope(*scope); - scopes.add_pat_bindings(store, *scope, pat); - } - _ => { - store.visit_expr_children(expr, ExprScopeVisitor { store, scopes, scope, const_scope }) - } - }; -} - #[cfg(test)] mod tests { use hir_expand::{InFile, name::AsName}; @@ -497,16 +444,27 @@ let (file_id, _) = editioned_file_id.unpack(&db); let file_syntax = editioned_file_id.parse(&db).syntax_node(); - let marker: ast::PathExpr = find_node_at_offset(&file_syntax, offset).unwrap(); + let marker: Option<ast::PathExpr> = find_node_at_offset(&file_syntax, offset); let function = find_function(&db, file_id); let scopes = ExprScopes::of(&db, DefWithBodyId::from(function)); - let (_body, source_map) = Body::with_source_map(&db, function.into()); + let (body, source_map) = Body::with_source_map(&db, function.into()); - let expr_id = source_map - .node_expr(InFile { file_id: editioned_file_id.into(), value: &marker.into() }) - .unwrap() - .as_expr() + let expr_id = marker + .and_then(|marker| { + source_map + .node_expr(InFile { file_id: editioned_file_id.into(), value: &marker.into() }) + .and_then(|expr| expr.as_expr()) + }) + .or_else(|| { + body.exprs().find_map(|(expr, value)| { + let crate::hir::Expr::Path(path) = value else { return None }; + path.mod_path() + .and_then(|path| path.as_ident()) + .is_some_and(|name| name.as_str() == "marker") + .then_some(expr) + }) + }) .unwrap(); let scope = scopes.scope_for(expr_id); @@ -534,6 +492,58 @@ } #[test] + fn pattern_type_expr_scope() { + do_check( + r#" +fn f(param: usize) { + let local = 0; + let _: builtin#pattern_type (usize is 0..=$0) = 0; +} +"#, + &["param"], + ); + } + + #[test] + fn closure_pattern_type_expr_scope() { + do_check( + r#" +fn f(param: usize) { + let local = 0; + let _ = |_: builtin#pattern_type (usize is 0..=$0)| {}; +} +"#, + &["param"], + ); + } + + #[test] + fn array_repeat_expr_scope() { + do_check( + r#" +fn f(param: usize) { + let local = 0; + let _ = [(); $0]; +} +"#, + &["param"], + ); + } + + #[test] + fn inline_const_expr_scope() { + do_check( + r#" +fn f(param: usize) { + let local = 0; + let _ = const { $0 }; +} +"#, + &["param"], + ); + } + + #[test] fn test_lambda_scope() { do_check( r" @@ -811,4 +821,66 @@ 100, ); } + #[test] + fn pattern_const_block_expressions_have_scopes() { + do_check( + r#" +fn foo() { + match () { + const { |x: i32| { let y = x; $0 } } => (), + } +} +"#, + &["y", "x"], + ); + } + + #[test] + fn let_pattern_expr_scope() { + do_check( + r#" +fn foo(param: usize) { + let local = 0; + let const { $0 } = (); +} +"#, + &["param"], + ); + } + + #[test] + fn closure_param_pattern_expr_scope() { + do_check( + r#" +fn foo(param: usize) { + let local = 0; + let _ = |const { $0 }: ()| (); +} +"#, + &["param"], + ); + } + + #[test] + fn fn_param_pattern_expr_scope() { + do_check( + r#" +fn foo(param: usize, const { $0 }: ()) {} +"#, + &["param"], + ); + } + + #[test] + fn if_let_pattern_expr_scope() { + do_check( + r#" +fn foo(param: usize) { + let local = 0; + if let const { $0 } = () {} +} +"#, + &["param"], + ); + } }
diff --git a/crates/hir-def/src/expr_store/tests/body/block.rs b/crates/hir-def/src/expr_store/tests/body/block.rs index a39a00b..850919e 100644 --- a/crates/hir-def/src/expr_store/tests/body/block.rs +++ b/crates/hir-def/src/expr_store/tests/body/block.rs
@@ -190,13 +190,13 @@ "#, expect![[r#" ModuleIdLt { - [salsa id]: Id(2c03), + [salsa id]: Id(583), krate: Crate( - Id(1c00), + Id(380), ), block: Some( BlockId( - Id(3c01), + Id(781), ), ), containing_module_inside_def_map: None,
diff --git a/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs index 7e56282..a275780 100644 --- a/crates/hir-def/src/hir.rs +++ b/crates/hir-def/src/hir.rs
@@ -74,7 +74,7 @@ } } -#[derive(Copy, Clone, Hash, PartialEq, Eq, salsa::Update)] +#[derive(Copy, Clone, Hash, PartialEq, Eq, salsa::SalsaValue)] pub struct ExprOrPatIdPacked(u32); const _: () = assert!(mem::size_of::<ExprOrPatIdPacked>() == mem::size_of::<u32>());
diff --git a/crates/hir-def/src/hir/generics.rs b/crates/hir-def/src/hir/generics.rs index 039b229..2e0b6f1 100644 --- a/crates/hir-def/src/hir/generics.rs +++ b/crates/hir-def/src/hir/generics.rs
@@ -4,6 +4,7 @@ use base_db::SourceDatabase; use hir_expand::name::Name; use la_arena::{Arena, Idx, RawIdx}; +use salsa::SalsaValue; use stdx::impl_from; use thin_vec::ThinVec; @@ -63,7 +64,7 @@ ArgumentImplTrait, } -#[derive(Clone, PartialEq, Eq, Debug, Hash)] +#[derive(Clone, PartialEq, Eq, Debug, Hash, SalsaValue)] pub enum TypeOrConstParamData { TypeParamData(TypeParamData), ConstParamData(ConstParamData),
diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs index 14f1065..61d578b 100644 --- a/crates/hir-def/src/item_scope.rs +++ b/crates/hir-def/src/item_scope.rs
@@ -161,7 +161,7 @@ /// Module scoped macros will be inserted into `items` instead of here. // FIXME: Macro shadowing in one module is not properly handled. Non-item place macros will // be all resolved to the last one defined if shadowing happens. - legacy_macros: FxHashMap<Name, SmallVec<[MacroId; 2]>>, + legacy_macros: FxHashMap<Name, SmallVec<[MacroId; 1]>>, /// The attribute macro invocations in this scope. attr_macros: FxHashMap<AstId<ast::Item>, MacroCallId>, /// The macro invocations in this scope.
diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs index 423ffd5..63ce6bc 100644 --- a/crates/hir-def/src/item_tree.rs +++ b/crates/hir-def/src/item_tree.rs
@@ -46,6 +46,7 @@ use ast::{AstNode, StructKind}; use base_db::{Crate, SourceDatabase}; use cfg::CfgOptions; +use either::Either; use hir_expand::{ ExpandTo, HirFileId, mod_path::{ModPath, PathKind}, @@ -53,7 +54,6 @@ }; use intern::Interned; use la_arena::{Idx, RawIdx}; -use rustc_hash::FxHashMap; use span::{ AstIdNode, Edition, FileAstId, NO_DOWNMAP_ERASED_FILE_AST_ID_MARKER, Span, SpanAnchor, SyntaxContext, @@ -128,10 +128,9 @@ static EMPTY: OnceLock<ItemTree> = OnceLock::new(); EMPTY.get_or_init(|| ItemTree { top_level: Box::new([]), - attrs: FxHashMap::default(), - small_data: FxHashMap::default(), - big_data: FxHashMap::default(), - top_attrs: AttrsOrCfg::empty(), + attrs: ThinVec::new(), + small_data: ThinVec::new(), + big_data: ThinVec::new(), vis: ItemVisibilities { arena: ThinVec::new() }, }) } @@ -146,7 +145,7 @@ ) -> Option<Box<ItemTree>> { let _p = tracing::info_span!("file_item_tree_query", ?file_id).entered(); - let ctx = lower::Ctx::new(db, file_id, krate); + let mut ctx = lower::Ctx::new(db, file_id, krate); let syntax = file_id.parse_or_expand(db); let mut item_tree = match_ast! { match syntax { @@ -162,9 +161,8 @@ Some(attrs @ AttrsOrCfg::CfgDisabled(_)) => attrs, None => ctx.lower_attrs(&file) }; - let mut item_tree = ctx.lower_module_items(&file); - item_tree.top_attrs = top_attrs; - item_tree + ctx.add_attrs(ModItemId::TOP_OWNER, top_attrs); + ctx.lower_module_items(&file) }, ast::MacroItems(items) => { ctx.lower_module_items(&items) @@ -182,17 +180,10 @@ }, } }; - let ItemTree { top_level, top_attrs, attrs, vis, big_data, small_data } = &item_tree; - if small_data.is_empty() - && big_data.is_empty() - && top_level.is_empty() - && attrs.is_empty() - && top_attrs.is_empty() - && vis.arena.is_empty() - { + if item_tree.is_empty() { None } else { - item_tree.shrink_to_fit(); + item_tree.finalize(); Some(Box::new(item_tree)) } } @@ -209,7 +200,7 @@ let ctx = lower::Ctx::new(db, ast_id.file_id, krate); let mut item_tree = ctx.lower_block(&block); - item_tree.shrink_to_fit(); + item_tree.finalize(); item_tree } @@ -217,11 +208,11 @@ #[derive(Debug, Default, Eq, PartialEq)] pub struct ItemTree { top_level: Box<[ModItemId]>, - top_attrs: AttrsOrCfg, - attrs: FxHashMap<FileAstId<ast::Item>, AttrsOrCfg>, + /// Sorted by the id. The last item, if it has [`ModItemId::TOP_OWNER`], is the top level attrs. + attrs: ThinVec<(ModItemId, AttrsOrCfg)>, vis: ItemVisibilities, - big_data: FxHashMap<FileAstId<ast::Item>, BigModItem>, - small_data: FxHashMap<FileAstId<ast::Item>, SmallModItem>, + big_data: ThinVec<(FileAstId<ast::Item>, BigModItem)>, + small_data: ThinVec<(FileAstId<ast::Item>, SmallModItem)>, } impl ItemTree { @@ -232,12 +223,14 @@ } /// Returns the inner attributes of the source file. - pub(crate) fn top_level_attrs(&self) -> &AttrsOrCfg { - &self.top_attrs + #[inline] + pub(crate) fn top_level_attrs(&self) -> Option<&AttrsOrCfg> { + self.attrs.last().filter(|(id, _)| *id == ModItemId::TOP_OWNER).map(|(_, attrs)| attrs) } - pub(crate) fn attrs(&self, of: FileAstId<ast::Item>) -> Option<&AttrsOrCfg> { - self.attrs.get(&of) + #[inline] + pub(crate) fn attrs(&self, of: ModItemId) -> Option<&AttrsOrCfg> { + self.attrs.binary_search_by_key(&of, |(id, _)| *id).ok().map(|index| &self.attrs[index].1) } /// Returns a count of a few, expensive items. @@ -249,7 +242,7 @@ let mut mods = 0; let mut macro_calls = 0; let mut macro_rules = 0; - for item in self.small_data.values() { + for (_, item) in &self.small_data { match item { SmallModItem::Trait(_) => traits += 1, SmallModItem::Impl(_) => impls += 1, @@ -258,7 +251,7 @@ _ => {} } } - for item in self.big_data.values() { + for (_, item) in &self.big_data { match item { BigModItem::Mod(_) => mods += 1, _ => {} @@ -271,11 +264,21 @@ pretty::print_item_tree(db, self, edition) } - fn shrink_to_fit(&mut self) { - let ItemTree { top_level: _, attrs, big_data, small_data, vis: _, top_attrs: _ } = self; + fn is_empty(&self) -> bool { + let ItemTree { top_level, attrs, vis: _, big_data: _, small_data: _ } = self; + // We don't need to check the rest since if those are empty, everything is empty. + // `attrs` might contain the top-level attrs so we need to check it separately. + top_level.is_empty() && attrs.is_empty() + } + + fn finalize(&mut self) { + self.attrs.sort_unstable_by_key(|(id, _)| *id); + + let ItemTree { top_level: _, attrs, big_data, small_data, vis } = self; attrs.shrink_to_fit(); big_data.shrink_to_fit(); small_data.shrink_to_fit(); + vis.arena.shrink_to_fit(); } } @@ -366,71 +369,105 @@ } } +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, PartialOrd, Ord)] +pub struct ModItemId { + /// The LSB is 0 for small_data and 1 for big_data. + index: u32, +} + +impl ModItemId { + const TOP_OWNER: ModItemId = ModItemId { index: u32::MAX }; + + #[inline] + fn new_small(index: u32) -> Self { + debug_assert!( + (index & 0x80_00_00_00) == 0 && index != (ModItemId::TOP_OWNER.index >> 1), + "too big index" + ); + Self { index: index << 1 } + } + #[inline] + fn new_big(index: u32) -> Self { + debug_assert!( + (index & 0x80_00_00_00) == 0 && index != (ModItemId::TOP_OWNER.index >> 1), + "too big index" + ); + Self { index: (index << 1) | 0b1 } + } + + #[inline] + fn get( + self, + item_tree: &ItemTree, + ) -> (FileAstId<ast::Item>, Either<&SmallModItem, &BigModItem>) { + let index = self.index >> 1; + match self.index & 0b1 { + 0 => { + let (ast_id, data) = &item_tree.small_data[index as usize]; + (*ast_id, Either::Left(data)) + } + 1 => { + let (ast_id, data) = &item_tree.big_data[index as usize]; + (*ast_id, Either::Right(data)) + } + _ => unreachable!(), + } + } + + #[inline] + pub(crate) fn ast_id(self, item_tree: &ItemTree) -> FileAstId<ast::Item> { + self.get(item_tree).0 + } +} + macro_rules! mod_items { - ($mod_item:ident -> $( $typ:ident in $fld:ident -> $ast:ty ),+ $(,)? ) => { - #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] - pub(crate) enum $mod_item { + ($mod_item:ident -> $( $typ:ident by $either:ident -> $ast:ty ),+ $(,)? ) => { + #[derive(Debug, Copy, Clone)] + pub(crate) enum $mod_item<'a> { $( - $typ(FileAstId<$ast>), + $typ(FileAstId<$ast>, &'a $typ), )+ } - impl $mod_item { - pub(crate) fn ast_id(self) -> FileAstId<ast::Item> { - match self { - $($mod_item::$typ(it) => it.upcast()),+ + impl ItemTree { + #[inline] + pub(crate) fn index(&self, id: ModItemId) -> $mod_item<'_> { + use BigModItem::*; + use SmallModItem::*; + + let (ast_id, item) = id.get(self); + match item { + $( Either::$either($typ(item)) => $mod_item::$typ(ast_id.downcast_unchecked(), item), )+ } } } $( - impl From<FileAstId<$ast>> for $mod_item { - fn from(id: FileAstId<$ast>) -> $mod_item { - ModItemId::$typ(id) - } - } - )+ - - $( impl ItemTreeNode for $typ { type Source = $ast; } - - impl Index<FileAstId<$ast>> for ItemTree { - type Output = $typ; - - #[allow(unused_imports)] - fn index(&self, index: FileAstId<$ast>) -> &Self::Output { - use BigModItem::*; - use SmallModItem::*; - match &self.$fld[&index.upcast()] { - $typ(item) => item, - _ => panic!("expected item of type `{}` at index `{:?}`", stringify!($typ), index), - } - } - } )+ }; } mod_items! { -ModItemId -> - Const in small_data -> ast::Const, - Enum in small_data -> ast::Enum, - ExternBlock in small_data -> ast::ExternBlock, - ExternCrate in big_data -> ast::ExternCrate, - Function in small_data -> ast::Fn, - Impl in small_data -> ast::Impl, - Macro2 in small_data -> ast::MacroDef, - MacroCall in small_data -> ast::MacroCall, - MacroRules in small_data -> ast::MacroRules, - Mod in big_data -> ast::Module, - Static in small_data -> ast::Static, - Struct in small_data -> ast::Struct, - Trait in small_data -> ast::Trait, - TypeAlias in small_data -> ast::TypeAlias, - Union in small_data -> ast::Union, - Use in big_data -> ast::Use, +ModItemKind -> + Const by Left -> ast::Const, + Enum by Left -> ast::Enum, + ExternBlock by Left -> ast::ExternBlock, + ExternCrate by Right -> ast::ExternCrate, + Function by Left -> ast::Fn, + Impl by Left -> ast::Impl, + Macro2 by Left -> ast::MacroDef, + MacroCall by Left -> ast::MacroCall, + MacroRules by Left -> ast::MacroRules, + Mod by Right -> ast::Module, + Static by Left -> ast::Static, + Struct by Left -> ast::Struct, + Trait by Left -> ast::Trait, + TypeAlias by Left -> ast::TypeAlias, + Union by Left -> ast::Union, + Use by Right -> ast::Use, } impl Index<RawVisibilityId> for ItemTree {
diff --git a/crates/hir-def/src/item_tree/attrs.rs b/crates/hir-def/src/item_tree/attrs.rs index 9671ceb..4ee2f56 100644 --- a/crates/hir-def/src/item_tree/attrs.rs +++ b/crates/hir-def/src/item_tree/attrs.rs
@@ -254,12 +254,7 @@ impl AttrsOrCfg { #[inline] - pub(super) fn empty() -> Self { - AttrsOrCfg::Enabled { attrs: AttrsOwned(Box::new([])) } - } - - #[inline] pub(super) fn is_empty(&self) -> bool { - matches!(self, AttrsOrCfg::Enabled { attrs } if attrs.as_ref().is_empty()) + matches!(self, AttrsOrCfg::Enabled { attrs } if attrs.0.is_empty()) } }
diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs index 6f916c8..85fc0e4 100644 --- a/crates/hir-def/src/item_tree/lower.rs +++ b/crates/hir-def/src/item_tree/lower.rs
@@ -14,9 +14,9 @@ use crate::item_tree::{ BigModItem, Const, Enum, ExternBlock, ExternCrate, FieldsShape, Function, Impl, ImportAlias, - Interned, ItemTree, ItemTreeAstId, Macro2, MacroCall, MacroRules, Mod, ModItemId, ModKind, - ModPath, RawVisibility, RawVisibilityId, SmallModItem, Static, Struct, StructKind, Trait, - TypeAlias, Union, Use, UseTree, UseTreeKind, VisibilityExplicitness, attrs::AttrsOrCfg, + Interned, ItemTree, Macro2, MacroCall, MacroRules, Mod, ModItemId, ModKind, ModPath, + RawVisibility, RawVisibilityId, SmallModItem, Static, Struct, StructKind, Trait, TypeAlias, + Union, Use, UseTree, UseTreeKind, VisibilityExplicitness, attrs::AttrsOrCfg, }; pub(super) struct Ctx<'db> { @@ -98,7 +98,7 @@ } pub(super) fn lower_block(mut self, block: &ast::BlockExpr) -> ItemTree { - self.tree.top_attrs = self.lower_attrs(block); + self.add_attrs(ModItemId::TOP_OWNER, self.lower_attrs(block)); self.top_level = block .statements() .filter_map(|stmt| match stmt { @@ -124,68 +124,76 @@ } fn lower_mod_item(&mut self, item: &ast::Item) -> Option<ModItemId> { - let mod_item: ModItemId = match item { - ast::Item::Struct(ast) => self.lower_struct(ast)?.into(), - ast::Item::Union(ast) => self.lower_union(ast)?.into(), - ast::Item::Enum(ast) => self.lower_enum(ast)?.into(), - ast::Item::Fn(ast) => self.lower_function(ast)?.into(), - ast::Item::TypeAlias(ast) => self.lower_type_alias(ast)?.into(), - ast::Item::Static(ast) => self.lower_static(ast)?.into(), - ast::Item::Const(ast) => self.lower_const(ast).into(), - ast::Item::Module(ast) => self.lower_module(ast)?.into(), - ast::Item::Trait(ast) => self.lower_trait(ast)?.into(), - ast::Item::Impl(ast) => self.lower_impl(ast).into(), - ast::Item::Use(ast) => self.lower_use(ast)?.into(), - ast::Item::ExternCrate(ast) => self.lower_extern_crate(ast)?.into(), - ast::Item::MacroCall(ast) => self.lower_macro_call(ast)?.into(), - ast::Item::MacroRules(ast) => self.lower_macro_rules(ast)?.into(), - ast::Item::MacroDef(ast) => self.lower_macro_def(ast)?.into(), - ast::Item::ExternBlock(ast) => self.lower_extern_block(ast).into(), + let mod_item = match item { + ast::Item::Struct(ast) => self.lower_struct(ast)?, + ast::Item::Union(ast) => self.lower_union(ast)?, + ast::Item::Enum(ast) => self.lower_enum(ast)?, + ast::Item::Fn(ast) => self.lower_function(ast)?, + ast::Item::TypeAlias(ast) => self.lower_type_alias(ast)?, + ast::Item::Static(ast) => self.lower_static(ast)?, + ast::Item::Const(ast) => self.lower_const(ast), + ast::Item::Module(ast) => self.lower_module(ast)?, + ast::Item::Trait(ast) => self.lower_trait(ast)?, + ast::Item::Impl(ast) => self.lower_impl(ast), + ast::Item::Use(ast) => self.lower_use(ast)?, + ast::Item::ExternCrate(ast) => self.lower_extern_crate(ast)?, + ast::Item::MacroCall(ast) => self.lower_macro_call(ast)?, + ast::Item::MacroRules(ast) => self.lower_macro_rules(ast)?, + ast::Item::MacroDef(ast) => self.lower_macro_def(ast)?, + ast::Item::ExternBlock(ast) => self.lower_extern_block(ast), // FIXME: Handle `global_asm!()`. ast::Item::AsmExpr(_) => return None, }; let attrs = self.lower_attrs(item); - self.add_attrs(mod_item.ast_id(), attrs); + self.add_attrs(mod_item, attrs); Some(mod_item) } - fn add_attrs(&mut self, item: FileAstId<ast::Item>, attrs: AttrsOrCfg) { + pub(super) fn add_attrs(&mut self, item: ModItemId, attrs: AttrsOrCfg) { if !attrs.is_empty() { - self.tree.attrs.insert(item, attrs); + self.tree.attrs.push((item, attrs)); } } - fn lower_struct(&mut self, strukt: &ast::Struct) -> Option<ItemTreeAstId<Struct>> { + fn alloc_small(&mut self, ast_id: FileAstId<ast::Item>, item: SmallModItem) -> ModItemId { + let index = self.tree.small_data.len() as u32; + self.tree.small_data.push((ast_id, item)); + ModItemId::new_small(index) + } + + fn alloc_big(&mut self, ast_id: FileAstId<ast::Item>, item: BigModItem) -> ModItemId { + let index = self.tree.big_data.len() as u32; + self.tree.big_data.push((ast_id, item)); + ModItemId::new_big(index) + } + + fn lower_struct(&mut self, strukt: &ast::Struct) -> Option<ModItemId> { let visibility = self.lower_visibility(strukt); let name = strukt.name()?.as_name(); let ast_id = self.source_ast_id_map.ast_id(strukt); let shape = adt_shape(strukt.kind()); let res = Struct { name, visibility, shape }; - self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Struct(res)); - - Some(ast_id) + Some(self.alloc_small(ast_id.upcast(), SmallModItem::Struct(res))) } - fn lower_union(&mut self, union: &ast::Union) -> Option<ItemTreeAstId<Union>> { + fn lower_union(&mut self, union: &ast::Union) -> Option<ModItemId> { let visibility = self.lower_visibility(union); let name = union.name()?.as_name(); let ast_id = self.source_ast_id_map.ast_id(union); let res = Union { name, visibility }; - self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Union(res)); - Some(ast_id) + Some(self.alloc_small(ast_id.upcast(), SmallModItem::Union(res))) } - fn lower_enum(&mut self, enum_: &ast::Enum) -> Option<ItemTreeAstId<Enum>> { + fn lower_enum(&mut self, enum_: &ast::Enum) -> Option<ModItemId> { let visibility = self.lower_visibility(enum_); let name = enum_.name()?.as_name(); let ast_id = self.source_ast_id_map.ast_id(enum_); let res = Enum { name, visibility }; - self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Enum(res)); - Some(ast_id) + Some(self.alloc_small(ast_id.upcast(), SmallModItem::Enum(res))) } - fn lower_function(&mut self, func: &ast::Fn) -> Option<ItemTreeAstId<Function>> { + fn lower_function(&mut self, func: &ast::Fn) -> Option<ModItemId> { let visibility = self.lower_visibility(func); let name = func.name()?.as_name(); @@ -193,41 +201,34 @@ let res = Function { name, visibility }; - self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Function(res)); - Some(ast_id) + Some(self.alloc_small(ast_id.upcast(), SmallModItem::Function(res))) } - fn lower_type_alias( - &mut self, - type_alias: &ast::TypeAlias, - ) -> Option<ItemTreeAstId<TypeAlias>> { + fn lower_type_alias(&mut self, type_alias: &ast::TypeAlias) -> Option<ModItemId> { let name = type_alias.name()?.as_name(); let visibility = self.lower_visibility(type_alias); let ast_id = self.source_ast_id_map.ast_id(type_alias); let res = TypeAlias { name, visibility }; - self.tree.small_data.insert(ast_id.upcast(), SmallModItem::TypeAlias(res)); - Some(ast_id) + Some(self.alloc_small(ast_id.upcast(), SmallModItem::TypeAlias(res))) } - fn lower_static(&mut self, static_: &ast::Static) -> Option<ItemTreeAstId<Static>> { + fn lower_static(&mut self, static_: &ast::Static) -> Option<ModItemId> { let name = static_.name()?.as_name(); let visibility = self.lower_visibility(static_); let ast_id = self.source_ast_id_map.ast_id(static_); let res = Static { name, visibility }; - self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Static(res)); - Some(ast_id) + Some(self.alloc_small(ast_id.upcast(), SmallModItem::Static(res))) } - fn lower_const(&mut self, konst: &ast::Const) -> ItemTreeAstId<Const> { + fn lower_const(&mut self, konst: &ast::Const) -> ModItemId { let name = konst.name().map(|it| it.as_name()); let visibility = self.lower_visibility(konst); let ast_id = self.source_ast_id_map.ast_id(konst); let res = Const { name, visibility }; - self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Const(res)); - ast_id + self.alloc_small(ast_id.upcast(), SmallModItem::Const(res)) } - fn lower_module(&mut self, module: &ast::Module) -> Option<ItemTreeAstId<Mod>> { + fn lower_module(&mut self, module: &ast::Module) -> Option<ModItemId> { let name = module.name()?.as_name(); let visibility = self.lower_visibility(module); let kind = if module.semicolon_token().is_some() { @@ -247,30 +248,27 @@ }; let ast_id = self.source_ast_id_map.ast_id(module); let res = Mod { name, visibility, kind }; - self.tree.big_data.insert(ast_id.upcast(), BigModItem::Mod(res)); - Some(ast_id) + Some(self.alloc_big(ast_id.upcast(), BigModItem::Mod(res))) } - fn lower_trait(&mut self, trait_def: &ast::Trait) -> Option<ItemTreeAstId<Trait>> { + fn lower_trait(&mut self, trait_def: &ast::Trait) -> Option<ModItemId> { let name = trait_def.name()?.as_name(); let visibility = self.lower_visibility(trait_def); let ast_id = self.source_ast_id_map.ast_id(trait_def); let def = Trait { name, visibility }; - self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Trait(def)); - Some(ast_id) + Some(self.alloc_small(ast_id.upcast(), SmallModItem::Trait(def))) } - fn lower_impl(&mut self, impl_def: &ast::Impl) -> ItemTreeAstId<Impl> { + fn lower_impl(&mut self, impl_def: &ast::Impl) -> ModItemId { let ast_id = self.source_ast_id_map.ast_id(impl_def); // Note that trait impls don't get implicit `Self` unlike traits, because here they are a // type alias rather than a type parameter, so this is handled by the resolver. let res = Impl { is_trait_impl: impl_def.trait_().is_some() }; - self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Impl(res)); - ast_id + self.alloc_small(ast_id.upcast(), SmallModItem::Impl(res)) } - fn lower_use(&mut self, use_item: &ast::Use) -> Option<ItemTreeAstId<Use>> { + fn lower_use(&mut self, use_item: &ast::Use) -> Option<ModItemId> { let visibility = self.lower_visibility(use_item); let ast_id = self.source_ast_id_map.ast_id(use_item); let (use_tree, _) = lower_use_tree(self.db, use_item.use_tree()?, &mut |range| { @@ -278,14 +276,10 @@ })?; let res = Use { visibility, use_tree }; - self.tree.big_data.insert(ast_id.upcast(), BigModItem::Use(res)); - Some(ast_id) + Some(self.alloc_big(ast_id.upcast(), BigModItem::Use(res))) } - fn lower_extern_crate( - &mut self, - extern_crate: &ast::ExternCrate, - ) -> Option<ItemTreeAstId<ExternCrate>> { + fn lower_extern_crate(&mut self, extern_crate: &ast::ExternCrate) -> Option<ModItemId> { let name = extern_crate.name_ref()?.as_name(); let alias = extern_crate.rename().map(|a| { a.name().map(|it| it.as_name()).map_or(ImportAlias::Underscore, ImportAlias::Alias) @@ -294,11 +288,10 @@ let ast_id = self.source_ast_id_map.ast_id(extern_crate); let res = ExternCrate { name, alias, visibility }; - self.tree.big_data.insert(ast_id.upcast(), BigModItem::ExternCrate(res)); - Some(ast_id) + Some(self.alloc_big(ast_id.upcast(), BigModItem::ExternCrate(res))) } - fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<ItemTreeAstId<MacroCall>> { + fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<ModItemId> { let span_map = self.span_map(); let path = m.path()?; let range = path.syntax().text_range(); @@ -308,31 +301,28 @@ let ast_id = self.source_ast_id_map.ast_id(m); let expand_to = hir_expand::ExpandTo::from_call_site(m); let res = MacroCall { path, expand_to, ctxt: span_map.span_for_range(range).ctx }; - self.tree.small_data.insert(ast_id.upcast(), SmallModItem::MacroCall(res)); - Some(ast_id) + Some(self.alloc_small(ast_id.upcast(), SmallModItem::MacroCall(res))) } - fn lower_macro_rules(&mut self, m: &ast::MacroRules) -> Option<ItemTreeAstId<MacroRules>> { + fn lower_macro_rules(&mut self, m: &ast::MacroRules) -> Option<ModItemId> { let name = m.name()?; let ast_id = self.source_ast_id_map.ast_id(m); let res = MacroRules { name: name.as_name() }; - self.tree.small_data.insert(ast_id.upcast(), SmallModItem::MacroRules(res)); - Some(ast_id) + Some(self.alloc_small(ast_id.upcast(), SmallModItem::MacroRules(res))) } - fn lower_macro_def(&mut self, m: &ast::MacroDef) -> Option<ItemTreeAstId<Macro2>> { + fn lower_macro_def(&mut self, m: &ast::MacroDef) -> Option<ModItemId> { let name = m.name()?; let ast_id = self.source_ast_id_map.ast_id(m); let visibility = self.lower_visibility(m); let res = Macro2 { name: name.as_name(), visibility }; - self.tree.small_data.insert(ast_id.upcast(), SmallModItem::Macro2(res)); - Some(ast_id) + Some(self.alloc_small(ast_id.upcast(), SmallModItem::Macro2(res))) } - fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> ItemTreeAstId<ExternBlock> { + fn lower_extern_block(&mut self, block: &ast::ExternBlock) -> ModItemId { let ast_id = self.source_ast_id_map.ast_id(block); let children: Box<[_]> = block.extern_item_list().map_or(Box::new([]), |list| { list.extern_items() @@ -341,22 +331,21 @@ // (in other words, the knowledge that they're in an extern block must not be used). // This is because an extern block can contain macros whose ItemTree's top-level items // should be considered to be in an extern block too. - let mod_item: ModItemId = match &item { - ast::ExternItem::Fn(ast) => self.lower_function(ast)?.into(), - ast::ExternItem::Static(ast) => self.lower_static(ast)?.into(), - ast::ExternItem::TypeAlias(ty) => self.lower_type_alias(ty)?.into(), - ast::ExternItem::MacroCall(call) => self.lower_macro_call(call)?.into(), + let mod_item = match &item { + ast::ExternItem::Fn(ast) => self.lower_function(ast)?, + ast::ExternItem::Static(ast) => self.lower_static(ast)?, + ast::ExternItem::TypeAlias(ty) => self.lower_type_alias(ty)?, + ast::ExternItem::MacroCall(call) => self.lower_macro_call(call)?, }; let attrs = self.lower_attrs(&item); - self.add_attrs(mod_item.ast_id(), attrs); + self.add_attrs(mod_item, attrs); Some(mod_item) }) .collect() }); let res = ExternBlock { children }; - self.tree.small_data.insert(ast_id.upcast(), SmallModItem::ExternBlock(res)); - ast_id + self.alloc_small(ast_id.upcast(), SmallModItem::ExternBlock(res)) } fn lower_visibility(&mut self, item: &dyn ast::HasVisibility) -> RawVisibilityId {
diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs index fc62468..9fefe8b 100644 --- a/crates/hir-def/src/item_tree/pretty.rs +++ b/crates/hir-def/src/item_tree/pretty.rs
@@ -7,8 +7,9 @@ use crate::{ item_tree::{ Const, Enum, ExternBlock, ExternCrate, FieldsShape, Function, Impl, ItemTree, Macro2, - MacroCall, MacroRules, Mod, ModItemId, ModKind, RawVisibilityId, SourceDatabase, Static, - Struct, Trait, TypeAlias, Union, Use, UseTree, UseTreeKind, attrs::AttrsOrCfg, + MacroCall, MacroRules, Mod, ModItemId, ModItemKind, ModKind, RawVisibilityId, + SourceDatabase, Static, Struct, Trait, TypeAlias, Union, Use, UseTree, UseTreeKind, + attrs::AttrsOrCfg, }, visibility::RawVisibility, }; @@ -21,8 +22,10 @@ let mut p = Printer { db, tree, buf: String::new(), indent_level: 0, needs_indent: true, edition }; - p.print_attrs(&tree.top_attrs, true, "\n"); - p.blank(); + if let Some(top_attrs) = tree.top_level_attrs() { + p.print_attrs(top_attrs, true, "\n"); + p.blank(); + } for item in tree.top_level_items() { p.print_mod_item(*item); @@ -111,7 +114,7 @@ } fn print_attrs_of(&mut self, of: ModItemId, separated_by: &str) { - if let Some(attrs) = self.tree.attrs.get(&of.ast_id()) { + if let Some(attrs) = self.tree.attrs(of) { self.print_attrs(attrs, false, separated_by); } } @@ -173,17 +176,17 @@ fn print_mod_item(&mut self, item: ModItemId) { self.print_attrs_of(item, "\n"); - match item { - ModItemId::Use(ast_id) => { - let Use { visibility, use_tree } = &self.tree[ast_id]; + match self.tree.index(item) { + ModItemKind::Use(ast_id, item) => { + let Use { visibility, use_tree } = item; self.print_ast_id(ast_id.erase()); self.print_visibility(*visibility); w!(self, "use "); self.print_use_tree(use_tree); wln!(self, ";"); } - ModItemId::ExternCrate(ast_id) => { - let ExternCrate { name, alias, visibility } = &self.tree[ast_id]; + ModItemKind::ExternCrate(ast_id, item) => { + let ExternCrate { name, alias, visibility } = item; self.print_ast_id(ast_id.erase()); self.print_visibility(*visibility); w!(self, "extern crate {}", name.display(self.db, self.edition)); @@ -192,8 +195,8 @@ } wln!(self, ";"); } - ModItemId::ExternBlock(ast_id) => { - let ExternBlock { children } = &self.tree[ast_id]; + ModItemKind::ExternBlock(ast_id, item) => { + let ExternBlock { children } = item; self.print_ast_id(ast_id.erase()); w!(self, "extern {{"); self.indented(|this| { @@ -203,14 +206,14 @@ }); wln!(self, "}}"); } - ModItemId::Function(ast_id) => { - let Function { name, visibility } = &self.tree[ast_id]; + ModItemKind::Function(ast_id, item) => { + let Function { name, visibility } = item; self.print_ast_id(ast_id.erase()); self.print_visibility(*visibility); wln!(self, "fn {};", name.display(self.db, self.edition)); } - ModItemId::Struct(ast_id) => { - let Struct { visibility, name, shape: kind } = &self.tree[ast_id]; + ModItemKind::Struct(ast_id, item) => { + let Struct { visibility, name, shape: kind } = item; self.print_ast_id(ast_id.erase()); self.print_visibility(*visibility); w!(self, "struct {}", name.display(self.db, self.edition)); @@ -221,22 +224,22 @@ wln!(self, ";"); } } - ModItemId::Union(ast_id) => { - let Union { name, visibility } = &self.tree[ast_id]; + ModItemKind::Union(ast_id, item) => { + let Union { name, visibility } = item; self.print_ast_id(ast_id.erase()); self.print_visibility(*visibility); w!(self, "union {}", name.display(self.db, self.edition)); self.print_fields(FieldsShape::Record); wln!(self); } - ModItemId::Enum(ast_id) => { - let Enum { name, visibility } = &self.tree[ast_id]; + ModItemKind::Enum(ast_id, item) => { + let Enum { name, visibility } = item; self.print_ast_id(ast_id.erase()); self.print_visibility(*visibility); w!(self, "enum {} {{ ... }}", name.display(self.db, self.edition)); } - ModItemId::Const(ast_id) => { - let Const { name, visibility } = &self.tree[ast_id]; + ModItemKind::Const(ast_id, item) => { + let Const { name, visibility } = item; self.print_ast_id(ast_id.erase()); self.print_visibility(*visibility); w!(self, "const "); @@ -246,8 +249,8 @@ } wln!(self, " = _;"); } - ModItemId::Static(ast_id) => { - let Static { name, visibility } = &self.tree[ast_id]; + ModItemKind::Static(ast_id, item) => { + let Static { name, visibility } = item; self.print_ast_id(ast_id.erase()); self.print_visibility(*visibility); w!(self, "static "); @@ -255,27 +258,27 @@ w!(self, " = _;"); wln!(self); } - ModItemId::Trait(ast_id) => { - let Trait { name, visibility } = &self.tree[ast_id]; + ModItemKind::Trait(ast_id, item) => { + let Trait { name, visibility } = item; self.print_ast_id(ast_id.erase()); self.print_visibility(*visibility); w!(self, "trait {} {{ ... }}", name.display(self.db, self.edition)); } - ModItemId::Impl(ast_id) => { - let Impl { is_trait_impl: _ } = &self.tree[ast_id]; + ModItemKind::Impl(ast_id, item) => { + let Impl { is_trait_impl: _ } = item; self.print_ast_id(ast_id.erase()); w!(self, "impl {{ ... }}"); } - ModItemId::TypeAlias(ast_id) => { - let TypeAlias { name, visibility } = &self.tree[ast_id]; + ModItemKind::TypeAlias(ast_id, item) => { + let TypeAlias { name, visibility } = item; self.print_ast_id(ast_id.erase()); self.print_visibility(*visibility); w!(self, "type {}", name.display(self.db, self.edition)); w!(self, ";"); wln!(self); } - ModItemId::Mod(ast_id) => { - let Mod { name, visibility, kind } = &self.tree[ast_id]; + ModItemKind::Mod(ast_id, item) => { + let Mod { name, visibility, kind } = item; self.print_ast_id(ast_id.erase()); self.print_visibility(*visibility); w!(self, "mod {}", name.display(self.db, self.edition)); @@ -294,8 +297,8 @@ } } } - ModItemId::MacroCall(ast_id) => { - let MacroCall { path, expand_to, ctxt } = &self.tree[ast_id]; + ModItemKind::MacroCall(ast_id, item) => { + let MacroCall { path, expand_to, ctxt } = item; let _ = writeln!( self, "// AstId: {:#?}, SyntaxContextId: {}, ExpandTo: {:?}", @@ -305,13 +308,13 @@ ); wln!(self, "{}!(...);", path.display(self.db, self.edition)); } - ModItemId::MacroRules(ast_id) => { - let MacroRules { name } = &self.tree[ast_id]; + ModItemKind::MacroRules(ast_id, item) => { + let MacroRules { name } = item; self.print_ast_id(ast_id.erase()); wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db, self.edition)); } - ModItemId::Macro2(ast_id) => { - let Macro2 { name, visibility } = &self.tree[ast_id]; + ModItemKind::Macro2(ast_id, item) => { + let Macro2 { name, visibility } = item; self.print_ast_id(ast_id.erase()); self.print_visibility(*visibility); wln!(self, "macro {} {{ ... }}", name.display(self.db, self.edition));
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index bed38d3..288fefd 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs
@@ -354,7 +354,7 @@ pub derive_index: u32, } -#[salsa::interned(debug, no_lifetime)] +#[salsa::interned(debug, unsafe(no_lifetime), revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct BuiltinDeriveImplId { #[returns(ref)] @@ -465,7 +465,7 @@ impl_intern!(ProcMacroId, ProcMacroLoc); impl_loc!(ProcMacroLoc, id: Fn, container: ModuleId); -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::SalsaValue)] pub enum LoweringMode { Analysis, Ide, @@ -495,8 +495,10 @@ #[salsa::tracked(constructor = new_)] #[derive(PartialOrd, Ord)] pub struct BlockIdLt<'db> { + #[returns(copy)] pub ast_id: AstId<ast::BlockExpr>, /// The containing module. + #[returns(copy)] pub module: ModuleIdLt<'db>, } pub type BlockId = BlockIdLt<'static>; @@ -538,15 +540,18 @@ #[derive(PartialOrd, Ord)] pub struct ModuleIdLt<'db> { /// The crate this module belongs to. + #[returns(copy)] pub krate: Crate, /// If this `ModuleId` was derived from a `DefMap` for a block expression, this stores the /// `BlockId` of that block expression. If `None`, this module is part of the crate-level /// `DefMap` of `krate`. + #[returns(copy)] pub block: Option<BlockIdLt<'db>>, /// The parent module of this module, or `None` if this is the root module inside the def /// map (including for block def maps). pub containing_module_inside_def_map: Option<ModuleIdLt<'db>>, /// The name of this module, or [`sym::__empty`] for the root module. + #[returns(clone)] name_or_empty: Name, } pub type ModuleId = ModuleIdLt<'static>; @@ -620,17 +625,17 @@ } } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::SalsaValue)] pub struct FieldId { // FIXME: Store this as an erased `salsa::Id` to save space pub parent: VariantId, pub local_id: LocalFieldId, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::SalsaValue)] pub struct TupleId(pub u32); -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::SalsaValue)] pub struct TupleFieldId { pub tuple: TupleId, pub index: u32, @@ -754,7 +759,7 @@ #[salsa::tracked] impl MacroId { /// Turns a MacroId into a MacroDefId, describing the macro's definition post name resolution. - #[salsa::tracked] + #[salsa::tracked(returns(copy))] pub fn definition(self, db: &dyn SourceDatabase) -> MacroDefId { let kind = |expander, file_id, m| { let in_file = InFile::new(file_id, m); @@ -1124,7 +1129,7 @@ ); #[derive( - Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, salsa::Supertype, salsa::Update, + Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, salsa::Supertype, salsa::SalsaValue, )] pub enum VariantId { EnumVariantId(EnumVariantId), @@ -1435,6 +1440,7 @@ call_site: SyntaxContext, expand_to: ExpandTo, krate: Crate, + macro_depth: u32, resolver: impl Fn(&ModPath) -> Option<MacroDefId> + Copy, eager_callback: &mut dyn FnMut( InFile<(syntax::AstPtr<ast::MacroCall>, span::FileAstId<ast::MacroCall>)>, @@ -1451,6 +1457,7 @@ ast_id, def, call_site, + macro_depth, &|path| resolver(path).filter(MacroDefId::is_fn_like), eager_callback, ), @@ -1460,6 +1467,7 @@ krate, MacroCallKind::FnLike { ast_id, expand_to, eager: None }, call_site, + macro_depth, )), err: None, },
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs index 9a46ddb..e290601 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -35,9 +35,9 @@ }; } -struct#0:MacroRules[BE8F, 0]@58..64#15360# MyTraitMap2#0:MacroCall[BE8F, 0]@31..42#ROOT2024# {#0:MacroRules[BE8F, 0]@72..73#15360# - map#0:MacroRules[BE8F, 0]@86..89#15360#:#0:MacroRules[BE8F, 0]@89..90#15360# #0:MacroRules[BE8F, 0]@89..90#15360#::#0:MacroRules[BE8F, 0]@91..93#15360#std#0:MacroRules[BE8F, 0]@93..96#15360#::#0:MacroRules[BE8F, 0]@96..98#15360#collections#0:MacroRules[BE8F, 0]@98..109#15360#::#0:MacroRules[BE8F, 0]@109..111#15360#HashSet#0:MacroRules[BE8F, 0]@111..118#15360#<#0:MacroRules[BE8F, 0]@118..119#15360#(#0:MacroRules[BE8F, 0]@119..120#15360#)#0:MacroRules[BE8F, 0]@120..121#15360#>#0:MacroRules[BE8F, 0]@121..122#15360#,#0:MacroRules[BE8F, 0]@122..123#15360# -}#0:MacroRules[BE8F, 0]@132..133#15360# +struct#0:MacroRules[BE8F, 0]@58..64#1920# MyTraitMap2#0:MacroCall[BE8F, 0]@31..42#ROOT2024# {#0:MacroRules[BE8F, 0]@72..73#1920# + map#0:MacroRules[BE8F, 0]@86..89#1920#:#0:MacroRules[BE8F, 0]@89..90#1920# #0:MacroRules[BE8F, 0]@89..90#1920#::#0:MacroRules[BE8F, 0]@91..93#1920#std#0:MacroRules[BE8F, 0]@93..96#1920#::#0:MacroRules[BE8F, 0]@96..98#1920#collections#0:MacroRules[BE8F, 0]@98..109#1920#::#0:MacroRules[BE8F, 0]@109..111#1920#HashSet#0:MacroRules[BE8F, 0]@111..118#1920#<#0:MacroRules[BE8F, 0]@118..119#1920#(#0:MacroRules[BE8F, 0]@119..120#1920#)#0:MacroRules[BE8F, 0]@120..121#1920#>#0:MacroRules[BE8F, 0]@121..122#1920#,#0:MacroRules[BE8F, 0]@122..123#1920# +}#0:MacroRules[BE8F, 0]@132..133#1920# "#]], ); } @@ -197,7 +197,7 @@ #[macro_use] mod foo; -struct#1:MacroRules[DB0C, 0]@59..65#15360# Foo#0:MacroCall[DB0C, 0]@32..35#ROOT2024#(#1:MacroRules[DB0C, 0]@70..71#15360#u32#0:MacroCall[DB0C, 0]@41..44#ROOT2024#)#1:MacroRules[DB0C, 0]@74..75#15360#;#1:MacroRules[DB0C, 0]@75..76#15360# +struct#1:MacroRules[DB0C, 0]@59..65#1920# Foo#0:MacroCall[DB0C, 0]@32..35#ROOT2024#(#1:MacroRules[DB0C, 0]@70..71#1920#u32#0:MacroCall[DB0C, 0]@41..44#ROOT2024#)#1:MacroRules[DB0C, 0]@74..75#1920#;#1:MacroRules[DB0C, 0]@75..76#1920# "#]], ); } @@ -423,10 +423,10 @@ macro_rules! m { ($($i:ident),*) => ( impl Bar { $(fn $i() {})* } ); } -impl#\15360# Bar#\15360# {#\15360# - fn#\15360# foo#\ROOT2024#(#\15360#)#\15360# {#\15360#}#\15360# - fn#\15360# bar#\ROOT2024#(#\15360#)#\15360# {#\15360#}#\15360# -}#\15360# +impl#\1920# Bar#\1920# {#\1920# + fn#\1920# foo#\ROOT2024#(#\1920#)#\1920# {#\1920#}#\1920# + fn#\1920# bar#\ROOT2024#(#\1920#)#\1920# {#\1920#}#\1920# +}#\1920# "#]], ); }
diff --git a/crates/hir-def/src/nameres/assoc.rs b/crates/hir-def/src/nameres/assoc.rs index 9560d78..8dea9a4 100644 --- a/crates/hir-def/src/nameres/assoc.rs +++ b/crates/hir-def/src/nameres/assoc.rs
@@ -144,7 +144,7 @@ diagnostics: Vec<DefDiagnostic>, container: ItemContainerId, - depth: usize, + macro_depth: u32, items: Vec<(Name, AssocItemId)>, macro_calls: ThinVec<(AstId<ast::Item>, MacroCallId)>, } @@ -169,7 +169,7 @@ container, items: Vec::new(), - depth: 0, + macro_depth: file_id.macro_expansion_depth(db), macro_calls: ThinVec::new(), diagnostics: Vec::new(), } @@ -215,6 +215,7 @@ ast_id_with_path, attr, attr_id, + self.macro_depth + 1, ) { Ok(ResolvedAttr::Macro(call_id)) => { let loc = call_id.loc(self.db); @@ -321,6 +322,7 @@ ctxt, ExpandTo::Items, self.module_id.krate(self.db), + self.macro_depth + 1, resolver, &mut |ptr, call_id| { self.macro_calls.push((ptr.map(|(_, it)| it.upcast()), call_id)) @@ -351,7 +353,7 @@ } fn collect_macro_items(&mut self, macro_call_id: MacroCallId) { - if self.depth > self.def_map.recursion_limit() as usize { + if self.macro_depth > self.def_map.recursion_limit() { tracing::warn!("macro expansion is too deep"); return; } @@ -360,7 +362,7 @@ let old_file_id = mem::replace(&mut self.file_id, macro_call_id.into()); let old_ast_id_map = mem::replace(&mut self.ast_id_map, self.file_id.ast_id_map(self.db)); let old_span_map = mem::replace(&mut self.span_map, SpanMap::ExpansionSpanMap(span_map)); - self.depth += 1; + self.macro_depth += 1; let items = ast::MacroItems::cast(syntax.syntax_node()).expect("not `MacroItems`"); for item in items.items() { @@ -375,7 +377,7 @@ self.collect_item(item); } - self.depth -= 1; + self.macro_depth -= 1; self.file_id = old_file_id; self.ast_id_map = old_ast_id_map; self.span_map = old_span_map;
diff --git a/crates/hir-def/src/nameres/attr_resolution.rs b/crates/hir-def/src/nameres/attr_resolution.rs index e78c38b..411b80f 100644 --- a/crates/hir-def/src/nameres/attr_resolution.rs +++ b/crates/hir-def/src/nameres/attr_resolution.rs
@@ -35,6 +35,7 @@ ast_id: AstIdWithPath<ast::Item>, attr: &Attr, attr_id: AttrId, + macro_depth: u32, ) -> Result<ResolvedAttr, UnresolvedMacro> { // NB: does not currently work for derive helpers as they aren't recorded in the `DefMap` @@ -73,6 +74,7 @@ AttrMacroAttrIds::from_one(attr_id), self.krate, def.definition(db), + macro_depth, ))) } @@ -108,6 +110,7 @@ censored_attr_ids: AttrMacroAttrIds, krate: Crate, def: MacroDefId, + macro_depth: u32, ) -> MacroCallId { let arg = match macro_attr.input.as_deref() { Some(AttrInput::TokenTree(tt)) => { @@ -128,6 +131,7 @@ censored_attr_ids, }, macro_attr.ctxt, + macro_depth, ) } @@ -140,6 +144,7 @@ krate: Crate, resolver: impl Fn(&ModPath) -> Option<(MacroId, MacroDefId)>, derive_macro_id: MacroCallId, + macro_depth: u32, ) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> { let (macro_id, def_id) = resolver(&item_attr.path) .filter(|(_, def_id)| def_id.is_derive()) @@ -154,6 +159,7 @@ derive_macro_id, }, call_site, + macro_depth, ); Ok((macro_id, def_id, call_id)) }
diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index bb61ceb..97450c8 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs
@@ -37,7 +37,7 @@ item_scope::{GlobId, ImportId, ImportOrExternCrate, PerNsGlobImports}, item_tree::{ self, Attrs, AttrsOrCfg, FieldsShape, ImportAlias, ImportKind, ItemTree, ItemTreeAstId, - Macro2, MacroCall, MacroRules, Mod, ModItemId, ModKind, TreeId, + Macro2, MacroCall, MacroRules, Mod, ModItemId, ModItemKind, ModKind, TreeId, Use, }, macro_call_as_call_id, nameres::{ @@ -157,14 +157,13 @@ impl Import { fn from_use( tree: &ItemTree, - item: FileAstId<ast::Use>, + item: &Use, id: UseId, is_prelude: bool, mut cb: impl FnMut(Self), ) { - let it = &tree[item]; - let visibility = &tree[it.visibility]; - it.expand(|idx, path, kind, alias| { + let visibility = &tree[item.visibility]; + item.expand(|idx, path, kind, alias| { cb(Self { path, alias, @@ -186,7 +185,7 @@ #[derive(Clone, Debug, Eq, PartialEq)] struct MacroDirective<'db> { module_id: ModuleId, - depth: usize, + depth: u32, kind: MacroDirectiveKind<'db>, container: ItemContainerId, } @@ -221,7 +220,7 @@ call_id: MacroCallId, derive: BuiltinDeriveExpander, module_id: ModuleId, - depth: usize, + depth: u32, container: ItemContainerId, derive_attr_id: AttrId, derive_index: u32, @@ -261,7 +260,7 @@ /// This also stores the attributes to skip when we resolve derive helpers and non-macro /// non-builtin attributes in general. // FIXME: There has to be a better way to do this - skip_attrs: FxHashMap<AstId<ast::Item>, AttrId>, + skip_attrs: FxHashMap<InFile<ModItemId>, AttrId>, /// When we expand attributes, we need to censor all previous active attributes /// on the same item. Therefore, this holds all active attributes that we already /// expanded. @@ -280,8 +279,9 @@ let file_id = self.def_map.krate.root_file_id(self.db); let item_tree = file_item_tree(self.db, file_id.into(), self.def_map.krate); let attrs = match item_tree.top_level_attrs() { - AttrsOrCfg::Enabled { attrs } => attrs.as_ref(), - AttrsOrCfg::CfgDisabled(it) => it.1.as_ref(), + Some(AttrsOrCfg::Enabled { attrs }) => attrs.as_ref(), + None => Attrs::EMPTY, + Some(AttrsOrCfg::CfgDisabled(it)) => it.1.as_ref(), }; let crate_data = Arc::get_mut(&mut self.def_map.data).unwrap(); @@ -368,7 +368,7 @@ self.inject_prelude(); - if let AttrsOrCfg::CfgDisabled(attrs) = item_tree.top_level_attrs() { + if let Some(AttrsOrCfg::CfgDisabled(attrs)) = item_tree.top_level_attrs() { let (cfg_expr, _) = &**attrs; self.def_map.diagnostics.push(DefDiagnostic::unconfigured_code( self.def_map.root, @@ -394,14 +394,19 @@ fn seed_with_inner(&mut self, tree_id: TreeId) { let item_tree = tree_id.item_tree(self.db, self.def_map.krate); - let is_cfg_enabled = matches!(item_tree.top_level_attrs(), AttrsOrCfg::Enabled { .. }); + let is_cfg_enabled = + matches!(item_tree.top_level_attrs(), Some(AttrsOrCfg::Enabled { .. }) | None); if is_cfg_enabled { self.inject_prelude(); let module_id = self.def_map.root; + // A block `DefMap` is collected from scratch, but it may itself live inside a macro + // expansion. Continue that chain's budget instead of restarting it, or a macro + // expanding to an item that invokes the macro again never reaches the limit. + let macro_depth = tree_id.file_id().macro_expansion_depth(self.db); ModCollector { def_collector: self, - macro_depth: 0, + macro_depth, module_id, tree_id, item_tree, @@ -498,7 +503,7 @@ (*attr.path).clone(), )); - self.skip_attrs.insert(ast_id.ast_id.with_value(mod_item.ast_id()), *attr_id); + self.skip_attrs.insert(ast_id.ast_id.with_value(*mod_item), *attr_id); Some((idx, directive, *mod_item, *tree, *item_tree)) } @@ -1348,6 +1353,7 @@ *call_site, *expand_to, self.def_map.krate, + directive.depth, resolver_def_id, &mut |ptr, call_id| { eager_callback_buffer.push((directive.module_id, ptr, call_id)); @@ -1384,6 +1390,7 @@ self.def_map.krate, |path| resolver(&self.def_map, path), *derive_macro_id, + directive.depth, ); if let Ok((macro_id, def_id, call_id)) = id { @@ -1429,9 +1436,7 @@ let mut recollect_without = |collector: &mut Self| { // Remove the original directive since we resolved it. let mod_dir = collector.mod_dirs[&directive.module_id].clone(); - collector - .skip_attrs - .insert(InFile::new(file_id, mod_item.ast_id()), *attr_id); + collector.skip_attrs.insert(InFile::new(file_id, *mod_item), *attr_id); ModCollector { def_collector: collector, @@ -1489,6 +1494,7 @@ AttrMacroAttrIds::from_many(active_attrs), self.def_map.krate, def, + directive.depth, ) }; if matches!(def, @@ -1499,10 +1505,10 @@ // normal (as that would just be an identity expansion with extra output) // Instead we treat derive attributes special and apply them separately. - let ast_adt_id: FileAstId<ast::Adt> = match *mod_item { - ModItemId::Struct(ast_id) => ast_id.upcast(), - ModItemId::Union(ast_id) => ast_id.upcast(), - ModItemId::Enum(ast_id) => ast_id.upcast(), + let ast_adt_id: FileAstId<ast::Adt> = match item_tree.index(*mod_item) { + ModItemKind::Struct(ast_id, _) => ast_id.upcast(), + ModItemKind::Union(ast_id, _) => ast_id.upcast(), + ModItemKind::Enum(ast_id, _) => ast_id.upcast(), _ => { let diag = DefDiagnostic::invalid_derive_target( directive.module_id, @@ -1541,6 +1547,7 @@ self.def_map.krate, |path| resolver(&self.def_map, path), call_id, + directive.depth, ); let ast_id_without_path = ast_id.ast_id; @@ -1703,11 +1710,11 @@ &mut self, module_id: ModuleId, macro_call_id: MacroCallId, - depth: usize, + depth: u32, container: ItemContainerId, attr_macro_item: Option<AstId<ast::Item>>, ) { - if depth > self.def_map.recursion_limit() as usize { + if depth > self.def_map.recursion_limit() { cov_mark::hit!(macro_expansion_overflow); tracing::warn!("macro expansion is too deep"); return; @@ -1738,7 +1745,7 @@ for item in item_tree.top_level_items() { self.def_map .derive_helpers_in_scope - .entry(InFile::new(file_id, item.ast_id())) + .entry(InFile::new(file_id, item.ast_id(item_tree))) .or_default() .extend(derive_helpers.iter().cloned()); } @@ -1776,6 +1783,7 @@ *call_site, *expand_to, self.def_map.krate, + directive.depth, |path| { let resolved_res = self.def_map.resolve_path_fp_with_macro( self.crate_local_def_map.unwrap_or(&self.local_def_map), @@ -1864,7 +1872,7 @@ /// Walks a single module, populating defs, imports and macros struct ModCollector<'a, 'db> { def_collector: &'a mut DefCollector<'db>, - macro_depth: usize, + macro_depth: u32, module_id: ModuleId, tree_id: TreeId, item_tree: &'db ItemTree, @@ -1944,18 +1952,20 @@ .unwrap_or(Visibility::Public) }; - let mut process_mod_item = |item: ModItemId| { - let attrs = match self.item_tree.attrs(item.ast_id()) { + let item_tree = self.item_tree; + + let mut process_mod_item = |item_id: ModItemId| { + let attrs = match self.item_tree.attrs(item_id) { Some(AttrsOrCfg::Enabled { attrs }) => attrs.as_ref(), None => Attrs::EMPTY, Some(AttrsOrCfg::CfgDisabled(cfg)) => { - let ast_id = item.ast_id().erase(); + let ast_id = item_id.ast_id(self.item_tree).erase(); self.emit_unconfigured_diagnostic(InFile::new(self.file_id(), ast_id), &cfg.0); return; } }; - if let Err(()) = self.resolve_attributes(attrs, item, container) { + if let Err(()) = self.resolve_attributes(attrs, item_id, container) { // Do not process the item. It has at least one non-builtin attribute, so the // fixed-point algorithm is required to resolve the rest of them. return; @@ -1965,16 +1975,14 @@ let local_def_map = self.def_collector.crate_local_def_map.unwrap_or(&self.def_collector.local_def_map); - match item { - ModItemId::Mod(m) => self.collect_module(m, attrs), - ModItemId::Use(item_tree_id) => { - let id = UseLoc { - container: module_id, - id: InFile::new(self.file_id(), item_tree_id), - } - .intern(db); + match self.item_tree.index(item_id) { + ModItemKind::Mod(ast_id, module) => self.collect_module(ast_id, module, attrs), + ModItemKind::Use(ast_id, use_) => { + let id = + UseLoc { container: module_id, id: InFile::new(self.file_id(), ast_id) } + .intern(db); let is_prelude = attrs.by_key(sym::prelude_import).exists(); - Import::from_use(self.item_tree, item_tree_id, id, is_prelude, |import| { + Import::from_use(self.item_tree, use_, id, is_prelude, |import| { self.def_collector.unresolved_imports.push(ImportDirective { module_id: self.module_id, import, @@ -1982,13 +1990,12 @@ }); }) } - ModItemId::ExternCrate(item_tree_id) => { - let item_tree::ExternCrate { name, visibility, alias } = - &self.item_tree[item_tree_id]; + ModItemKind::ExternCrate(ast_id, item) => { + let item_tree::ExternCrate { name, visibility, alias } = item; let id = ExternCrateLoc { container: module_id, - id: InFile::new(self.tree_id.file_id(), item_tree_id), + id: InFile::new(self.tree_id.file_id(), ast_id), } .intern(db); def_map.modules[self.module_id].scope.define_extern_crate_decl(id); @@ -2051,40 +2058,42 @@ self.def_collector.def_map.diagnostics.push( DefDiagnostic::unresolved_extern_crate( module_id, - InFile::new(self.file_id(), item_tree_id), + InFile::new(self.file_id(), ast_id), ), ); } } - ModItemId::ExternBlock(block) => { + ModItemKind::ExternBlock(ast_id, block) => { let extern_block_id = ExternBlockLoc { container: module_id, - id: InFile::new(self.file_id(), block), + id: InFile::new(self.file_id(), ast_id), } .intern(db); self.def_collector.def_map.modules[self.module_id] .scope .define_extern_block(extern_block_id); - self.collect( - &self.item_tree[block].children, - ItemContainerId::ExternBlockId(extern_block_id), - ) + self.collect(&block.children, ItemContainerId::ExternBlockId(extern_block_id)) } - ModItemId::MacroCall(mac) => self.collect_macro_call(mac, container), - ModItemId::MacroRules(id) => self.collect_macro_rules(id, module_id), - ModItemId::Macro2(id) => self.collect_macro_def(id, module_id), - ModItemId::Impl(imp) => { + ModItemKind::MacroCall(ast_id, mac) => { + self.collect_macro_call(ast_id, mac, container) + } + ModItemKind::MacroRules(ast_id, mac) => { + self.collect_macro_rules(attrs, ast_id, mac, module_id) + } + ModItemKind::Macro2(ast_id, mac) => { + self.collect_macro_def(attrs, ast_id, mac, module_id) + } + ModItemKind::Impl(ast_id, imp) => { let impl_id = - ImplLoc { container: module_id, id: InFile::new(self.file_id(), imp) } + ImplLoc { container: module_id, id: InFile::new(self.file_id(), ast_id) } .intern(db); self.def_collector.def_map.modules[self.module_id] .scope - .define_impl(impl_id, self.item_tree[imp].is_trait_impl) + .define_impl(impl_id, imp.is_trait_impl) } - ModItemId::Function(id) => { - let it = &self.item_tree[id]; + ModItemKind::Function(ast_id, it) => { let fn_id = - FunctionLoc { container, id: InFile::new(self.tree_id.file_id(), id) } + FunctionLoc { container, id: InFile::new(self.tree_id.file_id(), ast_id) } .intern(db); let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]); @@ -2098,7 +2107,7 @@ { self.def_collector.export_proc_macro( proc_macro, - InFile::new(self.file_id(), id), + InFile::new(self.file_id(), ast_id), fn_id, ); @@ -2110,19 +2119,17 @@ .remove_from_value_ns(&it.name, fn_id.into()); } } - ModItemId::Struct(id) => { - let it = &self.item_tree[id]; - + ModItemKind::Struct(ast_id, it) => { let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]); let interned = StructLoc { container: module_id, - id: InFile::new(self.tree_id.file_id(), id), + id: InFile::new(self.tree_id.file_id(), ast_id), } .intern(db); consider_deferred_derives( self.tree_id.file_id(), &mut self.def_collector.deferred_builtin_derives, - id.upcast(), + ast_id.upcast(), interned.into(), def_map, ); @@ -2134,46 +2141,42 @@ !matches!(it.shape, FieldsShape::Record), ); } - ModItemId::Union(id) => { - let it = &self.item_tree[id]; - + ModItemKind::Union(ast_id, it) => { let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]); let interned = UnionLoc { container: module_id, - id: InFile::new(self.tree_id.file_id(), id), + id: InFile::new(self.tree_id.file_id(), ast_id), } .intern(db); consider_deferred_derives( self.tree_id.file_id(), &mut self.def_collector.deferred_builtin_derives, - id.upcast(), + ast_id.upcast(), interned.into(), def_map, ); update_def(self.def_collector, interned.into(), &it.name, vis, false); } - ModItemId::Enum(id) => { - let it = &self.item_tree[id]; + ModItemKind::Enum(ast_id, it) => { let enum_ = EnumLoc { container: module_id, - id: InFile::new(self.tree_id.file_id(), id), + id: InFile::new(self.tree_id.file_id(), ast_id), } .intern(db); consider_deferred_derives( self.tree_id.file_id(), &mut self.def_collector.deferred_builtin_derives, - id.upcast(), + ast_id.upcast(), enum_.into(), def_map, ); let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]); update_def(self.def_collector, enum_.into(), &it.name, vis, false); } - ModItemId::Const(id) => { - let it = &self.item_tree[id]; + ModItemKind::Const(ast_id, it) => { let const_id = - ConstLoc { container, id: InFile::new(self.tree_id.file_id(), id) } + ConstLoc { container, id: InFile::new(self.tree_id.file_id(), ast_id) } .intern(db); match &it.name { @@ -2190,13 +2193,11 @@ } } } - ModItemId::Static(id) => { - let it = &self.item_tree[id]; - + ModItemKind::Static(ast_id, it) => { let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]); update_def( self.def_collector, - StaticLoc { container, id: InFile::new(self.file_id(), id) } + StaticLoc { container, id: InFile::new(self.file_id(), ast_id) } .intern(db) .into(), &it.name, @@ -2204,13 +2205,11 @@ false, ); } - ModItemId::Trait(id) => { - let it = &self.item_tree[id]; - + ModItemKind::Trait(ast_id, it) => { let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]); update_def( self.def_collector, - TraitLoc { container: module_id, id: InFile::new(self.file_id(), id) } + TraitLoc { container: module_id, id: InFile::new(self.file_id(), ast_id) } .intern(db) .into(), &it.name, @@ -2218,13 +2217,11 @@ false, ); } - ModItemId::TypeAlias(id) => { - let it = &self.item_tree[id]; - + ModItemKind::TypeAlias(ast_id, it) => { let vis = resolve_vis(def_map, local_def_map, &self.item_tree[it.visibility]); update_def( self.def_collector, - TypeAliasLoc { container, id: InFile::new(self.file_id(), id) } + TypeAliasLoc { container, id: InFile::new(self.file_id(), ast_id) } .intern(db) .into(), &it.name, @@ -2241,12 +2238,12 @@ if is_crate_root { items .iter() - .filter(|it| matches!(it, ModItemId::ExternCrate(..))) + .filter(move |&&it| matches!(item_tree.index(it), ModItemKind::ExternCrate(..))) .copied() .for_each(&mut process_mod_item); items .iter() - .filter(|it| !matches!(it, ModItemId::ExternCrate(..))) + .filter(move |&&it| !matches!(item_tree.index(it), ModItemKind::ExternCrate(..))) .copied() .for_each(process_mod_item); } else { @@ -2287,10 +2284,14 @@ ); } - fn collect_module(&mut self, module_ast_id: ItemTreeAstId<Mod>, attrs: Attrs<'_>) { + fn collect_module( + &mut self, + module_ast_id: ItemTreeAstId<Mod>, + module: &Mod, + attrs: Attrs<'_>, + ) { let path_attr = attrs.by_key(sym::path).string_value_unescape(); let is_macro_use = attrs.by_key(sym::macro_use).exists(); - let module = &self.item_tree[module_ast_id]; match &module.kind { // inline module, just recurse ModKind::Inline { items } => { @@ -2332,14 +2333,15 @@ Ok((file_id, is_mod_rs, mod_dir)) => { let item_tree = file_item_tree(db, file_id.into(), self.def_collector.def_map.krate); - match item_tree.top_level_attrs() { - AttrsOrCfg::CfgDisabled(cfg) => { + let top_level_attrs = item_tree.top_level_attrs(); + match top_level_attrs { + Some(AttrsOrCfg::CfgDisabled(cfg)) => { self.emit_unconfigured_diagnostic( InFile::new(self.file_id(), module_ast_id.erase()), &cfg.0, ); } - AttrsOrCfg::Enabled { attrs } => { + Some(AttrsOrCfg::Enabled { .. }) | None => { let module_id = self.push_child_module( module.name.clone(), ast_id.value, @@ -2355,8 +2357,8 @@ mod_dir, } .collect_in_top_module(item_tree.top_level_items()); - let is_macro_use = - is_macro_use || attrs.as_ref().by_key(sym::macro_use).exists(); + let is_macro_use = is_macro_use + || matches!(top_level_attrs, Some(AttrsOrCfg::Enabled { attrs }) if attrs.as_ref().by_key(sym::macro_use).exists()); if is_macro_use { self.import_all_legacy_macros(module_id); } @@ -2455,11 +2457,8 @@ mod_item: ModItemId, container: ItemContainerId, ) -> Result<(), ()> { - let ignore_up_to = self - .def_collector - .skip_attrs - .get(&InFile::new(self.file_id(), mod_item.ast_id())) - .copied(); + let ignore_up_to = + self.def_collector.skip_attrs.get(&InFile::new(self.file_id(), mod_item)).copied(); for (attr_id, attr) in attrs.iter_after(ignore_up_to) { if self.def_collector.def_map.is_builtin_or_registered_attr(&attr.path) { continue; @@ -2469,7 +2468,11 @@ attr.path.display(self.def_collector.db, Edition::LATEST) ); - let ast_id = AstIdWithPath::new(self.file_id(), mod_item.ast_id(), attr.path.clone()); + let ast_id = AstIdWithPath::new( + self.file_id(), + mod_item.ast_id(self.item_tree), + attr.path.clone(), + ); self.def_collector.unresolved_macros.push(MacroDirective { module_id: self.module_id, depth: self.macro_depth + 1, @@ -2490,16 +2493,14 @@ Ok(()) } - fn collect_macro_rules(&mut self, ast_id: ItemTreeAstId<MacroRules>, module: ModuleId) { + fn collect_macro_rules( + &mut self, + attrs: Attrs<'_>, + ast_id: ItemTreeAstId<MacroRules>, + mac: &MacroRules, + module: ModuleId, + ) { let krate = self.def_collector.def_map.krate; - let mac = &self.item_tree[ast_id]; - let attrs = match self.item_tree.attrs(ast_id.upcast()) { - Some(AttrsOrCfg::Enabled { attrs }) => attrs.as_ref(), - None => Attrs::EMPTY, - Some(AttrsOrCfg::CfgDisabled(_)) => { - unreachable!("we only get here if the macro is not cfg'ed out") - } - }; let f_ast_id = InFile::new(self.file_id(), ast_id.upcast()); let export_attr = || attrs.by_key(sym::macro_export); @@ -2574,16 +2575,14 @@ ); } - fn collect_macro_def(&mut self, ast_id: ItemTreeAstId<Macro2>, module: ModuleId) { + fn collect_macro_def( + &mut self, + attrs: Attrs<'_>, + ast_id: ItemTreeAstId<Macro2>, + mac: &Macro2, + module: ModuleId, + ) { let krate = self.def_collector.def_map.krate; - let mac = &self.item_tree[ast_id]; - let attrs = match self.item_tree.attrs(ast_id.upcast()) { - Some(AttrsOrCfg::Enabled { attrs }) => attrs.as_ref(), - None => Attrs::EMPTY, - Some(AttrsOrCfg::CfgDisabled(_)) => { - unreachable!("we only get here if the macro is not cfg'ed out") - } - }; let f_ast_id = InFile::new(self.file_id(), ast_id.upcast()); // Case 1: builtin macros @@ -2659,9 +2658,10 @@ fn collect_macro_call( &mut self, ast_id: FileAstId<ast::MacroCall>, + macro_call: &MacroCall, container: ItemContainerId, ) { - let &MacroCall { ref path, expand_to, ctxt } = &self.item_tree[ast_id]; + let &MacroCall { ref path, expand_to, ctxt } = macro_call; let ast_id = AstIdWithPath::new(self.file_id(), ast_id, path.clone()); let db = self.def_collector.db; @@ -2678,6 +2678,7 @@ ctxt, expand_to, self.def_collector.def_map.krate, + self.macro_depth + 1, |path| { path.as_ident().and_then(|name| { let def_map = &self.def_collector.def_map;
diff --git a/crates/hir-def/src/nameres/tests/imports.rs b/crates/hir-def/src/nameres/tests/imports.rs index b1960b7..9fb685c 100644 --- a/crates/hir-def/src/nameres/tests/imports.rs +++ b/crates/hir-def/src/nameres/tests/imports.rs
@@ -41,6 +41,26 @@ } #[test] +fn trailing_self_import() { + check( + r#" +mod fmt { + pub struct Thing; +} +pub use fmt::self as alias; +"#, + expect![[r#" + crate + - alias : type (import) + - fmt : type + + crate::fmt + - Thing : type value + "#]], + ); +} + +#[test] fn primitive_reexport() { check( r#"
diff --git a/crates/hir-def/src/signatures.rs b/crates/hir-def/src/signatures.rs index ab5d377..b46d258 100644 --- a/crates/hir-def/src/signatures.rs +++ b/crates/hir-def/src/signatures.rs
@@ -1152,7 +1152,7 @@ } } -#[salsa::tracked] +#[salsa::tracked(returns(copy))] pub(crate) fn extern_block_abi(db: &dyn SourceDatabase, extern_block: ExternBlockId) -> ExternAbi { let source = extern_block.lookup(db).source(db); source
diff --git a/crates/hir-def/src/visibility.rs b/crates/hir-def/src/visibility.rs index 317be34..2a406cf 100644 --- a/crates/hir-def/src/visibility.rs +++ b/crates/hir-def/src/visibility.rs
@@ -278,6 +278,11 @@ } } } + + #[inline] + pub fn is_public(&self) -> bool { + matches!(self, Visibility::Public) + } } #[salsa::tracked] @@ -324,7 +329,7 @@ #[salsa::tracked] impl AssocItemId { /// Resolve visibility of an assoc item. - #[salsa::tracked] + #[salsa::tracked(returns(copy))] pub fn assoc_visibility(self, db: &dyn SourceDatabase) -> Visibility { match self { AssocItemId::FunctionId(function_id) => {
diff --git a/crates/hir-expand/src/builtin/derive_macro.rs b/crates/hir-expand/src/builtin/derive_macro.rs index 5e85a71..63e5764 100644 --- a/crates/hir-expand/src/builtin/derive_macro.rs +++ b/crates/hir-expand/src/builtin/derive_macro.rs
@@ -412,7 +412,7 @@ })?; let span = token_map.span_at(name.syntax().text_range().start()); - let name_token = tt::Ident::new(name.text().as_ref(), span); + let name_token = tt::Ident::new(name.text(), span); Ok(name_token) } @@ -1184,7 +1184,7 @@ let new_bounds = bounds.bounds().filter_map(|bound| { let new_bound = substitute_type_bound( bound.clone(), - &pointee_param_name.text(), + pointee_param_name.text(), ADDED_PARAM, ); @@ -1197,7 +1197,7 @@ let new_bounds_target = if is_pointee { make.name_ref(ADDED_PARAM) } else { - make.name_ref(¶m_name.text()) + make.name_ref(param_name.text()) }; new_predicates.push(make.where_pred( Either::Right( @@ -1240,12 +1240,12 @@ // If the target type references the pointee, duplicate the bound as whole. // Otherwise, duplicate only bounds that mention the pointee. if let Some(predicate_with_substituted_target) = - substitute_where_pred(&predicate, &pointee_param_name.text(), ADDED_PARAM) + substitute_where_pred(&predicate, pointee_param_name.text(), ADDED_PARAM) { new_predicates.push(predicate_with_substituted_target); } else if let Some(bounds) = predicate.type_bound_list() { let new_bounds = bounds.bounds().filter_map(|bound| { - substitute_type_bound(bound, &pointee_param_name.text(), ADDED_PARAM) + substitute_type_bound(bound, pointee_param_name.text(), ADDED_PARAM) }); new_predicates.push(make.where_pred(Either::Right(pred_target), new_bounds)); } @@ -1259,7 +1259,7 @@ new_predicates.push( make.where_pred( Either::Right(make.ty_path_from_segments( - [make.path_segment(make.name_ref(&pointee_param_name.text()))], + [make.path_segment(make.name_ref(pointee_param_name.text()))], false, )), [make.type_bound( @@ -1294,7 +1294,7 @@ .filter_map(|param| { Some(match param { ast::GenericParam::ConstParam(param) => { - ast::GenericArg::ConstArg(make.expr_const_value(¶m.name()?.text())) + ast::GenericArg::ConstArg(make.expr_const_value(param.name()?.text())) } ast::GenericParam::LifetimeParam(param) => { make.lifetime_arg(param.lifetime()?).into() @@ -1303,7 +1303,7 @@ let name = if pointee_param_idx == type_param_idx { make.name_ref(ADDED_PARAM) } else { - make.name_ref(¶m.name()?.text()) + make.name_ref(param.name()?.text()) }; type_param_idx += 1; make.type_arg(make.ty_path_from_segments([make.path_segment(name)], false)) @@ -1314,7 +1314,7 @@ make.path_from_segments( [make.generic_ty_path_segment( - make.name_ref(&struct_name.text()), + make.name_ref(struct_name.text()), self_params_for_traits, )], false,
diff --git a/crates/hir-expand/src/eager.rs b/crates/hir-expand/src/eager.rs index 22413e7..7002b70 100644 --- a/crates/hir-expand/src/eager.rs +++ b/crates/hir-expand/src/eager.rs
@@ -44,6 +44,7 @@ ast_id: AstId<ast::MacroCall>, def: MacroDefId, call_site: SyntaxContext, + macro_depth: u32, resolver: &dyn Fn(&ModPath) -> Option<MacroDefId>, eager_callback: EagerCallBackFn<'_>, ) -> ExpandResult<Option<MacroCallId>> { @@ -58,6 +59,7 @@ krate, kind: MacroCallKind::FnLike { ast_id, expand_to: ExpandTo::Expr, eager: None }, ctxt: call_site, + macro_depth, }; let arg_id = MacroCallId::new(db, loc); #[allow(deprecated)] // builtin eager macros are never derives @@ -76,6 +78,7 @@ InFile::new(arg_id.into(), arg_exp.syntax_node()), krate, call_site, + macro_depth, resolver, eager_callback, ) @@ -112,6 +115,7 @@ })), }, ctxt: call_site, + macro_depth, }; ExpandResult { value: Some(MacroCallId::new(db, loc)), err } @@ -124,6 +128,7 @@ ast_id: AstId<ast::MacroCall>, krate: Crate, call_site: SyntaxContext, + macro_depth: u32, eager_callback: EagerCallBackFn<'_>, ) -> ExpandResult<(InFile<Parse<SyntaxNode>>, &'db ExpansionSpanMap)> { let expand_to = ExpandTo::from_call_site(macro_call); @@ -132,6 +137,7 @@ krate, MacroCallKind::FnLike { ast_id, expand_to, eager: None }, call_site, + macro_depth, ); eager_callback(ast_id.map(|ast_id| (AstPtr::new(macro_call), ast_id)), id); @@ -148,6 +154,7 @@ curr: InFile<SyntaxNode>, krate: Crate, call_site: SyntaxContext, + macro_depth: u32, macro_resolver: &dyn Fn(&ModPath) -> Option<MacroDefId>, eager_callback: EagerCallBackFn<'_>, ) -> ExpandResult<Option<(SyntaxNode, TextSize)>> { @@ -215,6 +222,7 @@ curr.with_value(ast_id), def, call_site, + macro_depth + 1, macro_resolver, eager_callback, ); @@ -254,6 +262,7 @@ curr.with_value(ast_id), krate, call_site, + macro_depth, eager_callback, ); @@ -267,6 +276,7 @@ parse.as_ref().map(|it| it.syntax_node()), krate, call_site, + macro_depth + 1, macro_resolver, eager_callback, );
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index 58ab0e5..f7ac3f1 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs
@@ -241,6 +241,11 @@ pub krate: Crate, pub kind: MacroCallKind, pub ctxt: SyntaxContext, + /// The macro recursion depth of this expansion. + /// + /// Because macro expansions can cross `DefMap` boundaries, we must track this inside the expansion + /// to not recurse infinitely. + pub macro_depth: u32, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] @@ -960,7 +965,7 @@ /// parse queries being LRU cached. If they weren't the invalidations would only happen if the /// user wrote in the file that defines the proc-macro. fn proc_macro_span(db: &dyn SourceDatabase, ast: AstId<ast::Fn>) -> Span { - #[salsa::tracked] + #[salsa::tracked(returns(copy))] fn proc_macro_span(db: &dyn SourceDatabase, ast: AstId<ast::Fn>, _: ()) -> Span { let (parse, span_map) = ast.file_id.parse_with_map(db); let root = parse.syntax_node(); @@ -1014,8 +1019,9 @@ krate: Crate, kind: MacroCallKind, ctxt: SyntaxContext, + macro_depth: u32, ) -> MacroCallId { - MacroCallId::new(db, MacroCallLoc { def: self, krate, kind, ctxt }) + MacroCallId::new(db, MacroCallLoc { def: self, krate, kind, ctxt, macro_depth }) } pub fn definition_range(&self, db: &dyn SourceDatabase) -> InFile<TextRange> { @@ -1544,7 +1550,7 @@ /// /// We encode macro definitions into ids of macro calls, this what allows us /// to be incremental. -#[salsa::interned(no_lifetime, debug, revisions = usize::MAX)] +#[salsa::interned(unsafe(no_lifetime), debug, revisions = usize::MAX)] #[doc(alias = "MacroFileId")] pub struct MacroCallId { #[returns(ref)] @@ -1722,6 +1728,24 @@ } } } + + /// How many macro expansions separate this file from the real file it originates from. + /// + /// `Expander` and `DefCollector` seed their expansion depth counters from this. Both otherwise + /// start at zero for every body and every block, so a macro expanding to an item that invokes + /// the macro again advances neither counter past one and never reaches the recursion limit. + /// + /// Written recursively rather than as a loop so that salsa memoizes each file's depth. Callers + /// ask for this once per body, and the bodies of one expansion chain sit at depths 1..n, so a + /// loop would walk 1 + 2 + ... + n steps for that chain. Reusing the parent's memoized depth + /// makes each file O(1) once computed, and the chain linear. + #[inline] + pub fn macro_expansion_depth(self, db: &dyn SourceDatabase) -> u32 { + match self { + HirFileId::MacroFile(macro_call) => macro_call.loc(db).macro_depth, + HirFileId::FileId(_) => 0, + } + } } #[salsa::tracked]
diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs index 59578ba..4cc1944 100644 --- a/crates/hir-expand/src/mod_path.rs +++ b/crates/hir-expand/src/mod_path.rs
@@ -315,6 +315,7 @@ for segment in segments { let name = match segment.kind()? { ast::PathSegmentKind::Name(name) => name.as_name(), + ast::PathSegmentKind::SelfKw => continue, _ => return None, }; mod_path.segments.push(name);
diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs index d91b0f3..7968ada 100644 --- a/crates/hir-expand/src/name.rs +++ b/crates/hir-expand/src/name.rs
@@ -246,14 +246,14 @@ fn as_name(&self) -> Name { match self.as_tuple_field() { Some(idx) => Name::new_tuple_field(idx), - None => Name::new_root(&self.text()), + None => Name::new_root(self.text()), } } } impl AsName for ast::Name { fn as_name(&self) -> Name { - Name::new_root(&self.text()) + Name::new_root(self.text()) } }
diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs index 15dd530..1e631ca 100644 --- a/crates/hir-ty/src/consteval.rs +++ b/crates/hir-ty/src/consteval.rs
@@ -16,8 +16,7 @@ use rustc_apfloat::Float; use rustc_ast_ir::Mutability; use rustc_type_ir::inherent::{Const as _, GenericArgs as _, IntoKind, Ty as _}; -use salsa::Update; -use stdx::never; +use salsa::SalsaValue; use crate::{ ParamEnvAndCrate, Span, @@ -36,7 +35,7 @@ use super::mir::interpret_mir; -#[derive(Debug, Clone, PartialEq, Eq, Update)] +#[derive(Debug, Clone, PartialEq, Eq, SalsaValue)] pub enum ConstEvalError<'db> { MirLowerError(MirLowerError<'db>), MirEvalError(MirEvalError<'db>), @@ -81,15 +80,15 @@ interner: DbInterner<'db>, value: &Literal, ty: Ty<'db>, -) -> Result<Const<'db>, CreateConstError<'db>> { +) -> Option<Result<Const<'db>, CreateConstError<'db>>> { let Ok(data_layout) = interner.db.target_data_layout(interner.expect_crate()) else { - return Ok(Const::error(interner)); + return Some(Ok(Const::error(interner))); }; let valtree = match (ty.kind(), value) { (TyKind::Uint(uint), Literal::Uint(value, _)) => { let size = uint.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size()); let Some(scalar) = ScalarInt::try_from_uint(*value, size) else { - return Ok(Const::error(interner)); + return Some(Ok(Const::error(interner))); }; ValTreeKind::Leaf(scalar) } @@ -97,14 +96,14 @@ // `Literal::Int` is the default, so we also need to account for the type being uint. let size = uint.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size()); let Some(scalar) = ScalarInt::try_from_uint(*value as u128, size) else { - return Ok(Const::error(interner)); + return Some(Ok(Const::error(interner))); }; ValTreeKind::Leaf(scalar) } (TyKind::Int(int), Literal::Int(value, _)) => { let size = int.bit_width().map(Size::from_bits).unwrap_or(data_layout.pointer_size()); let Some(scalar) = ScalarInt::try_from_int(*value, size) else { - return Ok(Const::error(interner)); + return Some(Ok(Const::error(interner))); }; ValTreeKind::Leaf(scalar) } @@ -121,26 +120,21 @@ let scalar = ScalarInt::try_from_uint(value, size).unwrap(); ValTreeKind::Leaf(scalar) } - (_, Literal::String(value)) => { + (TyKind::Ref(_, inner_ty, _), Literal::String(value)) + if matches!(inner_ty.kind(), TyKind::Str) => + { let u8_values = &interner.default_types().consts.u8_values; ValTreeKind::Branch(Consts::new_from_iter( interner, value.as_str().as_bytes().iter().map(|&byte| u8_values[usize::from(byte)]), )) } - (_, Literal::ByteString(value)) => { - let u8_values = &interner.default_types().consts.u8_values; - ValTreeKind::Branch(Consts::new_from_iter( - interner, - value.iter().map(|&byte| u8_values[usize::from(byte)]), - )) - } - (_, Literal::CString(_)) => { - // FIXME: - return Ok(Const::error(interner)); + (_, Literal::ByteString(_) | Literal::CString(_)) => { + // This literals are complicated to construct and/or are possible to coerce. + // So we just allocate an anon const for them, they should be rare so it's not a problem. + return None; } _ => { - never!("mismatching type for literal"); let actual = literal_ty( interner, value, @@ -148,10 +142,10 @@ |types| types.types.u32, |types| types.types.f64, ); - return Err(CreateConstError::TypeMismatch { actual }); + return Some(Err(CreateConstError::TypeMismatch { actual })); } }; - Ok(Const::new_valtree(interner, ty, valtree)) + Some(Ok(Const::new_valtree(interner, ty, valtree))) } pub(crate) fn literal_ty<'db>( @@ -361,7 +355,7 @@ interner: DbInterner<'db>, owner: ExpressionStoreOwnerId, store: &ExpressionStore, - expr: ExprId, + expr_id: ExprId, resolver: &Resolver<'db>, expected_ty: Ty<'db>, generics: &dyn Fn() -> &'a Generics<'db>, @@ -369,10 +363,23 @@ lowering_mode: LoweringMode, forbid_params_after: Option<u32>, ) -> Result<Const<'db>, CreateConstError<'db>> { - match &store[expr] { - Expr::Literal(literal) => intern_const_ref(interner, literal, expected_ty), + let mut expr = &store[expr_id]; + if let Expr::Block { statements, tail: Some(tail), .. } = expr + && statements.is_empty() + { + // rustc unwraps *one* layer of blocks, so we do too (this impacts whether the const can use generic parameters. + // Anon consts sometimes cannot while bare paths can). mGCA allows arbitrarily many blocks, but we don't implement + // it yet. + expr = &store[*tail]; + } + match expr { + Expr::Literal(literal) + if let Some(literal) = intern_const_ref(interner, literal, expected_ty) => + { + literal + } Expr::Underscore => match create_var { - Some(create_var) => Ok(create_var(expr.into())), + Some(create_var) => Ok(create_var(expr_id.into())), None => Err(CreateConstError::UnderscoreExpr), }, Expr::Path(path) @@ -395,7 +402,7 @@ interner.db, AnonConstLoc { owner, - expr, + expr: expr_id, ty: StoredEarlyBinder::bind(expected_ty.store()), allow_using_generic_params, }, @@ -414,7 +421,7 @@ } } -#[salsa::tracked(cycle_result = const_eval_discriminant_cycle_result)] +#[salsa::tracked(cycle_result = const_eval_discriminant_cycle_result, returns(clone))] pub(crate) fn const_eval_discriminant_variant<'db>( db: &'db dyn HirDatabase, variant_id: EnumVariantId,
diff --git a/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index 8e7e55a..d005d62 100644 --- a/crates/hir-ty/src/db.rs +++ b/crates/hir-ty/src/db.rs
@@ -10,13 +10,13 @@ StaticId, TraitId, TypeAliasId, VariantId, builtin_derive::BuiltinDeriveImplMethod, expr_store::ExpressionStore, - hir::{ClosureKind, ExprId, generics::LocalTypeOrConstParamId}, + hir::{ClosureKind, ExprId}, layout::TargetDataLayout, resolver::{HasResolver, Resolver}, signatures::{ConstSignature, StaticSignature}, }; use la_arena::ArenaMap; -use salsa::Update; +use salsa::SalsaValue; use span::Edition; use stdx::impl_from; use triomphe::Arc; @@ -27,7 +27,10 @@ consteval::ConstEvalError, dyn_compatibility::DynCompatibilityViolation, layout::{Layout, LayoutError}, - lower::{GenericDefaults, TrackedStructToken, TypeAliasBounds}, + lower::{ + ConstParamTypes, FieldTypes, GenericDefaults, TrackedStructToken, TypeAliasBounds, + WithDefinedOpaques, + }, mir::{MirBody, MirLowerError}, next_solver::{ Allocation, Clause, EarlyBinder, GenericArgs, ParamEnv, PolyFnSig, StoredClauses, @@ -172,7 +175,7 @@ fn type_for_type_alias_with_diagnostics<'db>( &'db self, def: TypeAliasId, - ) -> &'db TyLoweringResult<'db, StoredEarlyBinder<StoredTy>> { + ) -> &'db TyLoweringResult<'db, WithDefinedOpaques<StoredEarlyBinder<StoredTy>>> { let db = self.as_dyn(); crate::lower::type_for_type_alias_with_diagnostics(db, def) } @@ -226,12 +229,12 @@ fn const_param_types_with_diagnostics<'db>( &'db self, def: GenericDefId, - ) -> &'db TyLoweringResult<'db, ArenaMap<LocalTypeOrConstParamId, StoredTy>> { + ) -> &'db TyLoweringResult<'db, ConstParamTypes> { let db = self.as_dyn(); crate::lower::const_param_types_with_diagnostics(db, def) } - fn const_param_types(&self, def: GenericDefId) -> &ArenaMap<LocalTypeOrConstParamId, StoredTy> { + fn const_param_types(&self, def: GenericDefId) -> &ConstParamTypes { let db = self.as_dyn(); crate::lower::const_param_types(db, def) } @@ -257,7 +260,7 @@ fn field_types_with_diagnostics<'db>( &'db self, var: VariantId, - ) -> &'db TyLoweringResult<'db, ArenaMap<LocalFieldId, FieldType>> { + ) -> &'db TyLoweringResult<'db, FieldTypes> { let db = self.as_dyn(); crate::lower::field_types_with_diagnostics(db, var) } @@ -275,12 +278,12 @@ crate::lower::callable_item_signature(db, def) } - fn callable_item_signature_with_diagnostics<'db>( + fn fn_sig_for_fn_with_diagnostics<'db>( &'db self, - def: CallableDefId, - ) -> &'db TyLoweringResult<'db, StoredEarlyBinder<StoredPolyFnSig>> { + def: FunctionId, + ) -> &'db TyLoweringResult<'db, WithDefinedOpaques<StoredEarlyBinder<StoredPolyFnSig>>> { let db = self.as_dyn(); - crate::lower::callable_item_signature_with_diagnostics(db, def) + crate::lower::fn_sig_for_fn(db, def) } fn trait_environment<'db>(&'db self, def: GenericDefId) -> ParamEnv<'db> { @@ -349,10 +352,11 @@ #[salsa::interned(debug, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct InternedOpaqueTyId { + #[returns(copy)] pub loc: ImplTraitId, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Update)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, SalsaValue)] pub struct InternedClosure<'db> { pub owner: InferBodyId<'db>, pub expr: ExprId, @@ -362,6 +366,7 @@ #[salsa::interned(constructor = new_impl, debug, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct InternedClosureId<'db> { + #[returns(copy)] pub loc: InternedClosure<'db>, } @@ -390,6 +395,7 @@ #[salsa::interned(constructor = new_impl, debug, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct InternedCoroutineId<'db> { + #[returns(copy)] pub loc: InternedClosure<'db>, } @@ -419,6 +425,7 @@ #[salsa::interned(constructor = new_impl, debug, revisions = usize::MAX)] #[derive(PartialOrd, Ord)] pub struct InternedCoroutineClosureId<'db> { + #[returns(copy)] pub loc: InternedClosure<'db>, } @@ -513,8 +520,9 @@ result.push(db.type_for_type_alias_with_diagnostics(id).defined_anon_consts()); result.push(db.type_alias_bounds_with_diagnostics(id).defined_anon_consts()); } - GenericDefId::FunctionId(id) => result - .push(db.callable_item_signature_with_diagnostics(id.into()).defined_anon_consts()), + GenericDefId::FunctionId(id) => { + result.push(db.fn_sig_for_fn_with_diagnostics(id).defined_anon_consts()) + } GenericDefId::ConstId(def) => { result.push(db.type_for_const_with_diagnostics(def).defined_anon_consts()) }
diff --git a/crates/hir-ty/src/dyn_compatibility.rs b/crates/hir-ty/src/dyn_compatibility.rs index bf7970d..4fd6539 100644 --- a/crates/hir-ty/src/dyn_compatibility.rs +++ b/crates/hir-ty/src/dyn_compatibility.rs
@@ -122,7 +122,7 @@ ControlFlow::Continue(()) } -#[salsa::tracked] +#[salsa::tracked(returns(clone))] pub fn dyn_compatibility_of_trait_query( db: &dyn HirDatabase, trait_: TraitId,
diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index a5a8220..1dd6f95 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs
@@ -65,7 +65,7 @@ AliasTyKind, TypeFoldable, TypeVisitableExt, inherent::{GenericArgs as _, IntoKind, Ty as _}, }; -use salsa::Update; +use salsa::SalsaValue; use smallvec::SmallVec; use span::Edition; use stdx::never; @@ -757,7 +757,7 @@ /// When you add a field that stores types (including `Substitution` and the like), don't forget /// `resolve_completely()`'ing them in `InferenceContext::resolve_all()`. Inference variables must /// not appear in the final inference result. -#[derive(Clone, PartialEq, Eq, Debug, Update)] +#[derive(Clone, PartialEq, Eq, Debug, SalsaValue)] pub struct InferenceResult<'db> { /// For each method call expr, records the function it resolves to. method_resolutions: FxHashMap<ExprId, (FunctionId, StoredGenericArgs)>, @@ -1085,10 +1085,7 @@ fn for_body(db: &dyn HirDatabase, def: DefWithBodyId) -> InferenceResult<'_> { infer_query(db, def) } -} -#[salsa::tracked] -impl<'db> InferenceResult<'db> { /// Infer types for all const expressions in an item's signature. /// /// Returns an `InferenceResult` containing type information for array lengths, @@ -1371,9 +1368,6 @@ breakables: Vec<BreakableContext<'db>>, types: &'db crate::next_solver::DefaultAny<'db>, - /// Whether we are inside the pattern of a destructuring assignment. - inside_assignment: bool, - deferred_cast_checks: Vec<CastCheck<'db>>, /// The key is an expression defining a closure or a coroutine closure. @@ -1441,7 +1435,7 @@ lowering_mode: LoweringMode, ) -> Self { let trait_env = db.trait_environment(generic_def); - let table = unify::InferenceTable::new(db, trait_env, resolver.krate(), store_owner); + let table = unify::InferenceTable::new(db, trait_env, resolver.krate(), owner); let types = crate::next_solver::default_types(db); InferenceContext { result: InferenceResult::new(types.types.error), @@ -1468,7 +1462,6 @@ diverges: Diverges::Maybe, breakables: Vec::new(), deferred_cast_checks: Vec::new(), - inside_assignment: false, diagnostics: Diagnostics::default(), vars_emitted_type_must_be_known_for: FxHashSet::default(), deferred_call_resolutions: FxHashMap::default(),
diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 570df6b..96c48fe 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs
@@ -220,15 +220,26 @@ result } - fn is_syntactic_place_expr(&self, expr: ExprId) -> bool { + fn is_syntactic_place_expr(&mut self, expr: ExprId) -> bool { match &self.store[expr] { // Lang item paths cannot currently be local variables or statics. Expr::Path(Path::LangItem(_, _)) => false, Expr::Path(Path::Normal(path)) => path.type_anchor.is_none(), - Expr::Path(path) => self - .resolver - .resolve_path_in_value_ns_fully(self.db, path, self.store.expr_path_hygiene(expr)) - .is_none_or(|res| matches!(res, ValueNs::LocalBinding(_) | ValueNs::StaticId(_))), + Expr::Path(path) => { + let guard = self.resolver.update_to_inner_scope(self.db, self.store_owner, expr); + let is_place = self + .resolver + .resolve_path_in_value_ns_fully( + self.db, + path, + self.store.expr_path_hygiene(expr), + ) + .is_none_or(|res| { + matches!(res, ValueNs::LocalBinding(_) | ValueNs::StaticId(_)) + }); + self.resolver.reset_to_guard(guard); + is_place + } Expr::Underscore => true, Expr::UnaryOp { op: UnaryOp::Deref, .. } => true, Expr::Field { .. } | Expr::Index { .. } => true, @@ -266,7 +277,7 @@ } } - pub(crate) fn check_lhs_assignable(&self, lhs: ExprId) { + pub(crate) fn check_lhs_assignable(&mut self, lhs: ExprId) { if self.is_syntactic_place_expr(lhs) { return; } @@ -647,13 +658,15 @@ self.write_pat_ty(target, lhs_ty); self.infer_expr_coerce(value, &Expectation::has_type(lhs_ty), ExprIsRead::Yes); } else { - let rhs_ty = self.infer_expr(value, &Expectation::none(), ExprIsRead::Yes); - let resolver_guard = - self.resolver.update_to_inner_scope(self.db, self.store_owner, tgt_expr); - self.inside_assignment = true; + // Let the target pattern constrain the RHS coercion target. In particular, + // this allows a diverging RHS to coerce to the pattern's type. + let expected_ty = self.table.next_ty_var(target.into()); + let rhs_ty = self.infer_expr_coerce( + value, + &Expectation::has_type(expected_ty), + ExprIsRead::Yes, + ); self.infer_top_pat(target, rhs_ty, PatOrigin::DestructuringAssignment); - self.inside_assignment = false; - self.resolver.reset_to_guard(resolver_guard); } if is_destructuring_assignment && self.diverges.is_always() { // Ordinary assignments always return `()`, even when they diverge. @@ -1887,7 +1900,12 @@ )), None => field_with_same_name_exists.and_then(|field_ty| { let callable_sig = field_ty.callable_sig(self.interner())?; - Some((field_ty, callable_sig.skip_binder(), false)) + let callable_sig = self.infcx().instantiate_binder_with_fresh_vars( + tgt_expr.into(), + BoundRegionConversionTime::FnCall, + callable_sig, + ); + Some((field_ty, callable_sig, false)) }), }; match recovered {
diff --git a/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index 6157f51..7b589ef 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/crates/hir-ty/src/infer/unify.rs
@@ -3,7 +3,7 @@ use std::fmt; use base_db::Crate; -use hir_def::{ExpressionStoreOwnerId, GenericParamId, TraitId}; +use hir_def::{GenericParamId, TraitId}; use rustc_hash::FxHashSet; use rustc_type_ir::{ TyVid, TypeFoldable, TypeVisitableExt, @@ -14,7 +14,7 @@ use thin_vec::ThinVec; use crate::{ - InferenceDiagnostic, Span, + InferBodyId, InferenceDiagnostic, Span, db::HirDatabase, next_solver::{ Canonical, ClauseKind, Const, ConstKind, DbInterner, ErrorGuaranteed, GenericArg, @@ -144,7 +144,7 @@ db: &'db dyn HirDatabase, trait_env: ParamEnv<'db>, krate: Crate, - owner: ExpressionStoreOwnerId, + owner: InferBodyId<'db>, ) -> Self { let interner = DbInterner::new_with(db, krate); let typing_mode = TypingMode::typeck_for_body(interner, owner.into());
diff --git a/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index db1f7b8..1fbf150 100644 --- a/crates/hir-ty/src/layout.rs +++ b/crates/hir-ty/src/layout.rs
@@ -160,7 +160,7 @@ Ok(Arc::new(cx.calc.simd_type(e_ly, e_len, repr_packed)?)) } -#[salsa::tracked(cycle_result = layout_of_ty_cycle_result)] +#[salsa::tracked(cycle_result = layout_of_ty_cycle_result, returns(clone))] pub fn layout_of_ty_query( db: &dyn HirDatabase, ty: StoredTy,
diff --git a/crates/hir-ty/src/layout/adt.rs b/crates/hir-ty/src/layout/adt.rs index 777d080..47a960e 100644 --- a/crates/hir-ty/src/layout/adt.rs +++ b/crates/hir-ty/src/layout/adt.rs
@@ -19,7 +19,7 @@ traits::StoredParamEnvAndCrate, }; -#[salsa::tracked(cycle_result = layout_of_adt_cycle_result)] +#[salsa::tracked(cycle_result = layout_of_adt_cycle_result, returns(clone))] pub fn layout_of_adt_query( db: &dyn HirDatabase, def: AdtId,
diff --git a/crates/hir-ty/src/layout/tests.rs b/crates/hir-ty/src/layout/tests.rs index b5db24e..72befc5 100644 --- a/crates/hir-ty/src/layout/tests.rs +++ b/crates/hir-ty/src/layout/tests.rs
@@ -30,6 +30,7 @@ QueryConfig::Rustc(&Sysroot::empty(), &std::env::current_dir().unwrap()), None, &FxHashMap::default(), + None, ) .unwrap() }
diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index 0dd5588..21e68a6 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs
@@ -81,7 +81,7 @@ BoundVarIndexKind, TypeSuperVisitable, TypeVisitableExt, inherent::{IntoKind, Ty as _}, }; -use salsa::Update; +use salsa::SalsaValue; use stdx::impl_from; use syntax::ast::{ConstArg, make}; use traits::FnTrait; @@ -111,9 +111,9 @@ infer_query_with_inspect, }; pub use lower::{ - FieldType, GenericDefaults, GenericDefaultsRef, GenericPredicates, ImplTraits, - LifetimeElisionKind, LifetimeLoweringMode, LoweringMode, TyDefId, TyLoweringContext, - TyLoweringInferVarsCtx, TyLoweringResult, ValueTyDefId, diagnostics::*, + FieldType, GenericDefaults, GenericDefaultsRef, GenericPredicates, LifetimeElisionKind, + LifetimeLoweringMode, LoweringMode, TyDefId, TyLoweringContext, TyLoweringInferVarsCtx, + TyLoweringResult, ValueTyDefId, diagnostics::*, }; pub use next_solver::interner::{attach_db, attach_db_allow_change, with_attached_db}; pub use target_feature::TargetFeatures; @@ -554,7 +554,9 @@ } /// A [`DefWithBodyId`], or an anon const. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, salsa::Supertype, Update)] +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, salsa::Supertype, SalsaValue, +)] pub enum InferBodyId<'db> { DefWithBodyId(DefWithBodyId), AnonConstId(AnonConstId<'db>),
diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index bdb882b..7b28689 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs
@@ -8,9 +8,8 @@ pub(crate) mod diagnostics; pub(crate) mod path; -use std::{cell::OnceCell, iter, mem, sync::OnceLock}; +use std::{cell::OnceCell, iter, mem, ops::Deref, sync::OnceLock}; -use base_db::salsa::update_fallback_db; use either::Either; use hir_def::{ AdtId, AssocItemId, CallableDefId, ConstId, ConstParamId, EnumId, EnumVariantId, @@ -51,7 +50,7 @@ UpcastFrom, elaborate, inherent::{Clause as _, GenericArgs as _, IntoKind as _, Region as _, Ty as _}, }; -use salsa::Update; +use salsa::SalsaValue; use smallvec::SmallVec; use stdx::{impl_from, never}; use thin_vec::ThinVec; @@ -79,14 +78,15 @@ pub(crate) struct PathDiagnosticCallbackData(pub(crate) TypeRefId); -#[derive(PartialEq, Eq, Debug, Hash)] -pub struct ImplTraits { - pub(crate) impl_traits: Arena<ImplTrait>, +#[derive(PartialEq, Eq, Debug, Hash, SalsaValue)] +pub struct WithDefinedOpaques<T> { + value: T, + impl_traits: Option<Box<Arena<ImplTrait>>>, } #[derive(PartialEq, Eq, Debug, Hash)] pub struct ImplTrait { - pub(crate) predicates: StoredClauses, + pub(crate) predicates: StoredEarlyBinder<StoredClauses>, pub(crate) assoc_ty_bounds_start: u32, } @@ -417,6 +417,15 @@ BoundVarKinds::new_from_iter(interner, args) } + + fn take_defined_opaques(&mut self) -> Option<Box<Arena<ImplTrait>>> { + if self.impl_trait_mode.opaque_type_data.is_empty() { + None + } else { + self.impl_trait_mode.opaque_type_data.shrink_to_fit(); + Some(Box::new(mem::take(&mut self.impl_trait_mode.opaque_type_data))) + } + } } #[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] @@ -631,7 +640,7 @@ // place even if we encounter more opaque types while // lowering the bounds let idx = self.impl_trait_mode.opaque_type_data.alloc(ImplTrait { - predicates: Clauses::empty(interner).store(), + predicates: StoredEarlyBinder::bind(Clauses::empty(interner).store()), assoc_ty_bounds_start: 0, }); @@ -1319,7 +1328,7 @@ self.is_lowering_impl_trait_bounds = prev_is_lowering_impl_trait_bounds; ImplTrait { - predicates: Clauses::new_from_slice(&predicates).store(), + predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&predicates).store()), assoc_ty_bounds_start, } } @@ -1361,15 +1370,13 @@ } } -#[derive(Clone, PartialEq, Eq, Update)] +#[derive(Clone, PartialEq, Eq, SalsaValue)] pub struct TyLoweringResult<'db, T> { - #[update(fallback)] pub value: T, - #[update(bounds(TyLoweringResultInfo<'db>: Update), unsafe(with(update_fallback_db::<'db, _>)))] info: Option<Box<TyLoweringResultInfo<'db>>>, } -#[derive(Clone, PartialEq, Eq, Update)] +#[derive(Clone, PartialEq, Eq, SalsaValue)] struct TyLoweringResultInfo<'db> { diagnostics: ThinVec<TyLoweringDiagnostic>, anon_consts: ThinVec<AnonConstId<'db>>, @@ -1497,19 +1504,21 @@ impl ImplTraitId { #[inline] - pub fn predicates<'db>(self, db: &'db dyn HirDatabase) -> EarlyBinder<'db, &'db [Clause<'db>]> { + fn data(self, db: &dyn HirDatabase) -> &ImplTrait { let (impl_traits, idx) = match self { ImplTraitId::ReturnTypeImplTrait(owner, idx) => { - (ImplTraits::return_type_impl_traits(db, owner), idx) + (ImplTrait::return_type_impl_traits(db, owner), idx) } ImplTraitId::TypeAliasImplTrait(owner, idx) => { - (ImplTraits::type_alias_impl_traits(db, owner), idx) + (ImplTrait::type_alias_impl_traits(db, owner), idx) } }; - impl_traits - .as_deref() - .expect("owner should have opaque type") - .get_with(|it| it.impl_traits[idx].predicates.as_ref().as_slice()) + &impl_traits[idx] + } + + #[inline] + pub fn predicates<'db>(self, db: &'db dyn HirDatabase) -> EarlyBinder<'db, &'db [Clause<'db>]> { + self.data(db).predicates.get().map_bound(|it| it.as_slice()) } #[inline] @@ -1517,24 +1526,8 @@ self, db: &'db dyn HirDatabase, ) -> EarlyBinder<'db, &'db [Clause<'db>]> { - let (impl_traits, idx) = match self { - ImplTraitId::ReturnTypeImplTrait(owner, idx) => { - (ImplTraits::return_type_impl_traits(db, owner), idx) - } - ImplTraitId::TypeAliasImplTrait(owner, idx) => { - (ImplTraits::type_alias_impl_traits(db, owner), idx) - } - }; - let predicates = - impl_traits.as_deref().expect("owner should have opaque type").get_with(|it| { - let impl_trait = &it.impl_traits[idx]; - ( - impl_trait.predicates.as_ref().as_slice(), - impl_trait.assoc_ty_bounds_start as usize, - ) - }); - - predicates.map_bound(|(preds, len)| &preds[..len]) + let data = self.data(db); + data.predicates.get().map_bound(|it| &it.as_slice()[..data.assoc_ty_bounds_start as usize]) } } @@ -1553,71 +1546,25 @@ } } -#[salsa::tracked] -impl ImplTraits { - #[salsa::tracked(returns(ref))] +impl ImplTrait { + #[inline] pub(crate) fn return_type_impl_traits( db: &dyn HirDatabase, - def: hir_def::FunctionId, - ) -> Option<Box<StoredEarlyBinder<ImplTraits>>> { - // FIXME unify with fn_sig_for_fn instead of doing lowering twice, maybe - let data = FunctionSignature::of(db, def); - let resolver = def.resolver(db); - let generics = OnceCell::new(); - let mut ctx_ret = TyLoweringContext::new( - db, - &resolver, - &data.store, - ExpressionStoreOwnerId::Signature(def.into()), - def.into(), - &generics, - LifetimeElisionKind::Infer, - LifetimeLoweringMode::Bound, - ) - .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); - if let Some(ret_type) = data.ret_type { - let _ret = ctx_ret.lower_ty(ret_type); - } - let mut return_type_impl_traits = - ImplTraits { impl_traits: ctx_ret.impl_trait_mode.opaque_type_data }; - if return_type_impl_traits.impl_traits.is_empty() { - None - } else { - return_type_impl_traits.impl_traits.shrink_to_fit(); - Some(Box::new(StoredEarlyBinder::bind(return_type_impl_traits))) - } + def: FunctionId, + ) -> &Arena<ImplTrait> { + fn_sig_for_fn(db, def).value.impl_traits.as_deref().unwrap_or(const { &Arena::new() }) } - #[salsa::tracked(returns(ref))] + #[inline] pub(crate) fn type_alias_impl_traits( db: &dyn HirDatabase, - def: hir_def::TypeAliasId, - ) -> Option<Box<StoredEarlyBinder<ImplTraits>>> { - let data = TypeAliasSignature::of(db, def); - let resolver = def.resolver(db); - let generics = OnceCell::new(); - let mut ctx = TyLoweringContext::new( - db, - &resolver, - &data.store, - ExpressionStoreOwnerId::Signature(def.into()), - def.into(), - &generics, - LifetimeElisionKind::AnonymousReportError, - LifetimeLoweringMode::Bound, - ) - .with_impl_trait_mode(ImplTraitLoweringMode::Opaque); - if let Some(type_ref) = data.ty { - let _ty = ctx.lower_ty(type_ref); - } - let mut type_alias_impl_traits = - ImplTraits { impl_traits: ctx.impl_trait_mode.opaque_type_data }; - if type_alias_impl_traits.impl_traits.is_empty() { - None - } else { - type_alias_impl_traits.impl_traits.shrink_to_fit(); - Some(Box::new(StoredEarlyBinder::bind(type_alias_impl_traits))) - } + def: TypeAliasId, + ) -> &Arena<ImplTrait> { + type_for_type_alias_with_diagnostics(db, def) + .value + .impl_traits + .as_deref() + .unwrap_or(const { &Arena::new() }) } } @@ -1666,7 +1613,7 @@ it, GenericArgs::identity_for_item(interner, it.into()), )), - TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics(it).value.get(), + TyDefId::TypeAliasId(it) => db.type_for_type_alias_with_diagnostics(it).value.value.get(), } } @@ -1804,13 +1751,14 @@ pub(crate) fn type_for_type_alias_with_diagnostics<'db>( db: &'db dyn HirDatabase, t: TypeAliasId, -) -> TyLoweringResult<'db, StoredEarlyBinder<StoredTy>> { +) -> TyLoweringResult<'db, WithDefinedOpaques<StoredEarlyBinder<StoredTy>>> { let type_alias_data = TypeAliasSignature::of(db, t); let interner = DbInterner::new_no_crate(db); if type_alias_data.flags.contains(TypeAliasFlags::IS_EXTERN) { - TyLoweringResult::empty(StoredEarlyBinder::bind( - Ty::new_foreign(interner, t.into()).store(), - )) + TyLoweringResult::empty(WithDefinedOpaques { + value: StoredEarlyBinder::bind(Ty::new_foreign(interner, t.into()).store()), + impl_traits: None, + }) } else { let resolver = t.resolver(db); let generics = OnceCell::new(); @@ -1832,7 +1780,10 @@ .unwrap_or_else(|| Ty::new_error(interner, ErrorGuaranteed)) .store(), ); - TyLoweringResult::from_ctx(res, ctx) + TyLoweringResult::from_ctx( + WithDefinedOpaques { value: res, impl_traits: ctx.take_defined_opaques() }, + ctx, + ) } } @@ -1840,10 +1791,13 @@ db: &'db dyn HirDatabase, _: salsa::Id, _adt: TypeAliasId, -) -> TyLoweringResult<'db, StoredEarlyBinder<StoredTy>> { - TyLoweringResult::empty(StoredEarlyBinder::bind( - Ty::new_error(DbInterner::new_no_crate(db), ErrorGuaranteed).store(), - )) +) -> TyLoweringResult<'db, WithDefinedOpaques<StoredEarlyBinder<StoredTy>>> { + TyLoweringResult::empty(WithDefinedOpaques { + value: StoredEarlyBinder::bind( + Ty::new_error(DbInterner::new_no_crate(db), ErrorGuaranteed).store(), + ), + impl_traits: None, + }) } pub(crate) fn impl_self_ty_query<'db>( @@ -1894,10 +1848,23 @@ } } -pub(crate) fn const_param_types( - db: &dyn HirDatabase, - def: GenericDefId, -) -> &ArenaMap<LocalTypeOrConstParamId, StoredTy> { +/// Wrapper struct around `ArenaMap` which implements [`SalsaValue`]. +/// +/// Required to make the `SalsaValue` derive for [`TyLoweringResult`] work. +#[derive(Default, PartialEq, Eq, SalsaValue)] +pub struct ConstParamTypes { + map: ArenaMap<LocalTypeOrConstParamId, StoredTy>, +} + +impl Deref for ConstParamTypes { + type Target = ArenaMap<LocalTypeOrConstParamId, StoredTy>; + + fn deref(&self) -> &Self::Target { + &self.map + } +} + +pub(crate) fn const_param_types(db: &dyn HirDatabase, def: GenericDefId) -> &ConstParamTypes { &const_param_types_with_diagnostics(db, def).value } @@ -1905,7 +1872,7 @@ pub(crate) fn const_param_types_with_diagnostics<'db>( db: &'db dyn HirDatabase, def: GenericDefId, -) -> TyLoweringResult<'db, ArenaMap<LocalTypeOrConstParamId, StoredTy>> { +) -> TyLoweringResult<'db, ConstParamTypes> { let mut result = ArenaMap::new(); let (data, store) = GenericParams::with_store(db, def); let resolver = def.resolver(db); @@ -1927,21 +1894,34 @@ } } result.shrink_to_fit(); - TyLoweringResult::from_ctx(result, ctx) + TyLoweringResult::from_ctx(ConstParamTypes { map: result }, ctx) } fn const_param_types_with_diagnostics_cycle_result<'db>( _db: &'db dyn HirDatabase, _: salsa::Id, _def: GenericDefId, -) -> TyLoweringResult<'db, ArenaMap<LocalTypeOrConstParamId, StoredTy>> { - TyLoweringResult::empty(ArenaMap::default()) +) -> TyLoweringResult<'db, ConstParamTypes> { + TyLoweringResult::empty(ConstParamTypes::default()) } -pub(crate) fn field_types_query( - db: &dyn HirDatabase, - variant_id: VariantId, -) -> &ArenaMap<LocalFieldId, FieldType> { +/// Wrapper struct around `ArenaMap` which implements [`SalsaValue`]. +/// +/// Required to make the `SalsaValue` derive for [`TyLoweringResult`] work. +#[derive(Default, PartialEq, Eq, SalsaValue)] +pub struct FieldTypes { + map: ArenaMap<LocalFieldId, FieldType>, +} + +impl Deref for FieldTypes { + type Target = ArenaMap<LocalFieldId, FieldType>; + + fn deref(&self) -> &Self::Target { + &self.map + } +} + +pub(crate) fn field_types_query(db: &dyn HirDatabase, variant_id: VariantId) -> &FieldTypes { &field_types_with_diagnostics(db, variant_id).value } @@ -1968,11 +1948,11 @@ pub(crate) fn field_types_with_diagnostics<'db>( db: &'db dyn HirDatabase, variant_id: VariantId, -) -> TyLoweringResult<'db, ArenaMap<LocalFieldId, FieldType>> { +) -> TyLoweringResult<'db, FieldTypes> { let var_data = variant_id.fields(db); let fields = var_data.fields(); if fields.is_empty() { - return TyLoweringResult::empty(ArenaMap::default()); + return TyLoweringResult::empty(FieldTypes::default()); } let (resolver, generic_def): (_, GenericDefId) = match variant_id { @@ -2003,7 +1983,7 @@ }, ); } - TyLoweringResult::from_ctx(res, ctx) + TyLoweringResult::from_ctx(FieldTypes { map: res }, ctx) } #[derive(Debug, PartialEq, Eq, Default)] @@ -2286,7 +2266,7 @@ predicates.get().map_bound(|it| &it.as_slice()[..*assoc_ty_bounds_start as usize]) } -#[derive(PartialEq, Eq, Debug, Hash)] +#[derive(PartialEq, Eq, Debug, Hash, SalsaValue)] pub struct TypeAliasBounds<T> { predicates: T, assoc_ty_bounds_start: u32, @@ -2352,7 +2332,7 @@ ) } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, SalsaValue)] pub struct GenericPredicates { // The order is the following: // @@ -2717,7 +2697,7 @@ } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, SalsaValue)] pub struct GenericDefaults(ThinVec<Option<StoredEarlyBinder<StoredGenericArg>>>); impl GenericDefaults { @@ -2822,27 +2802,18 @@ db: &'db dyn HirDatabase, def: CallableDefId, ) -> EarlyBinder<'db, PolyFnSig<'db>> { - callable_item_signature_with_diagnostics(db, def).value.get() -} - -#[salsa::tracked(returns(ref))] -pub(crate) fn callable_item_signature_with_diagnostics<'db>( - db: &'db dyn HirDatabase, - def: CallableDefId, -) -> TyLoweringResult<'db, StoredEarlyBinder<StoredPolyFnSig>> { match def { - CallableDefId::FunctionId(f) => fn_sig_for_fn(db, f), - CallableDefId::StructId(s) => TyLoweringResult::empty(fn_sig_for_struct_constructor(db, s)), - CallableDefId::EnumVariantId(e) => { - TyLoweringResult::empty(fn_sig_for_enum_variant_constructor(db, e)) - } + CallableDefId::FunctionId(f) => fn_sig_for_fn(db, f).value.value.get(), + CallableDefId::StructId(s) => fn_sig_for_struct_constructor(db, s).get(), + CallableDefId::EnumVariantId(e) => fn_sig_for_enum_variant_constructor(db, e).get(), } } -fn fn_sig_for_fn<'db>( +#[salsa::tracked(returns(ref))] +pub(crate) fn fn_sig_for_fn<'db>( db: &'db dyn HirDatabase, def: FunctionId, -) -> TyLoweringResult<'db, StoredEarlyBinder<StoredPolyFnSig>> { +) -> TyLoweringResult<'db, WithDefinedOpaques<StoredEarlyBinder<StoredPolyFnSig>>> { let data = FunctionSignature::of(db, def); let resolver = def.resolver(db); let interner = DbInterner::new_no_crate(db); @@ -2874,6 +2845,7 @@ Some(ret_type) => ctx_ret.lower_ty(ret_type), None => Ty::new_unit(interner), }; + let impl_traits = ctx_ret.take_defined_opaques(); let inputs_and_output = Tys::new_from_iter(interner, params.chain(Some(ret))); ctx_params.diagnostics.extend(ctx_ret.diagnostics); @@ -2891,7 +2863,7 @@ }, binder, ))); - TyLoweringResult::from_ctx(result, ctx_params) + TyLoweringResult::from_ctx(WithDefinedOpaques { value: result, impl_traits }, ctx_params) } fn type_for_adt<'db>(db: &'db dyn HirDatabase, adt: AdtId) -> EarlyBinder<'db, Ty<'db>> { @@ -2901,13 +2873,14 @@ EarlyBinder::bind(ty) } -fn fn_sig_for_struct_constructor( +fn ctor_signature( db: &dyn HirDatabase, - def: StructId, + variant: VariantId, + adt: AdtId, ) -> StoredEarlyBinder<StoredPolyFnSig> { - let field_tys = db.field_types(def.into()); + let field_tys = db.field_types(variant); let params = field_tys.iter().map(|(_, field)| field.ty().skip_binder()); - let ret = type_for_adt(db, def.into()).skip_binder(); + let ret = type_for_adt(db, adt).skip_binder(); let inputs_and_output = Tys::new_from_iter(DbInterner::new_no_crate(db), params.chain(Some(ret))); @@ -2917,21 +2890,20 @@ }))) } +#[salsa::tracked(returns(ref))] +fn fn_sig_for_struct_constructor( + db: &dyn HirDatabase, + def: StructId, +) -> StoredEarlyBinder<StoredPolyFnSig> { + ctor_signature(db, def.into(), def.into()) +} + +#[salsa::tracked(returns(ref))] fn fn_sig_for_enum_variant_constructor( db: &dyn HirDatabase, def: EnumVariantId, ) -> StoredEarlyBinder<StoredPolyFnSig> { - let field_tys = db.field_types(def.into()); - let params = field_tys.iter().map(|(_, field)| field.ty().skip_binder()); - let parent = def.lookup(db).parent; - let ret = type_for_adt(db, parent.into()).skip_binder(); - - let inputs_and_output = - Tys::new_from_iter(DbInterner::new_no_crate(db), params.chain(Some(ret))); - StoredEarlyBinder::bind(StoredPolyFnSig::new(Binder::dummy(FnSig { - fn_sig_kind: FnSigKind::new(ExternAbi::Rust, Safety::Safe, false), - inputs_and_output, - }))) + ctor_signature(db, def.into(), def.lookup(db).parent.into()) } // FIXME: Remove this.
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index 97e9d8b..07ff9ac 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs
@@ -11,11 +11,11 @@ use either::Either; use hir_expand::name::Name; -use salsa::Update; +use salsa::SalsaValue; use span::Edition; use tracing::{debug, instrument}; -use base_db::{Crate, salsa::update_fallback_db}; +use base_db::Crate; use hir_def::{ AssocItemId, BlockIdLt, BuiltinDeriveImplId, ConstId, FunctionId, GenericParamId, HasModule, ImplId, ItemContainerId, ModuleId, TraitId, @@ -34,6 +34,7 @@ TypeVisitableExt, VisitorResult, fast_reject::{TreatParams, simplify_type}, inherent::{BoundExistentialPredicates, IntoKind}, + try_visit, }; use stdx::impl_from; use triomphe::Arc; @@ -54,7 +55,6 @@ obligation_ctxt::ObligationCtxt, util::clauses_as_obligations, }, - ret, traits::ParamEnvAndCrate, }; @@ -73,7 +73,7 @@ pub receiver_span: Span, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::Update)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, salsa::SalsaValue)] pub enum CandidateId { FunctionId(FunctionId), ConstId(ConstId), @@ -558,9 +558,12 @@ } } -#[derive(Debug, PartialEq, Eq, Update)] +#[derive(Debug, PartialEq, Eq, SalsaValue)] pub struct InherentImpls<'db> { - #[update(bounds(SolverDefId<'db>: Update), unsafe(with(update_fallback_db::<'db, _>)))] + // SAFETY: necessary due to `SimplifiedType<'db>`. + // It's safe to retain, as it only contains `SolverDefId<'db>` (which is `SalsaValue`), + // and no `&'db` references. + #[salsa_value(unsafe(prove(SolverDefId<'db>: SalsaValue)))] map: FxHashMap<SimplifiedType<'db>, Box<[ImplId]>>, } @@ -644,9 +647,12 @@ } } -#[derive(Debug, PartialEq, Update)] +#[derive(Debug, PartialEq, SalsaValue)] struct OneTraitImpls<'db> { - #[update(bounds(SolverDefId<'db>: Update), unsafe(with(update_fallback_db::<'db, _>)))] + // SAFETY: necessary due to `SimplifiedType<'db>`. + // It's safe to retain, as it only contains `SolverDefId<'db>` (which is `SalsaValue`), + // and no `&'db` references. + #[salsa_value(unsafe(prove(SolverDefId<'db>: SalsaValue)))] non_blanket_impls: FxHashMap<SimplifiedType<'db>, (Box<[ImplId]>, Box<[BuiltinDeriveImplId]>)>, blanket_impls: Box<[ImplId]>, } @@ -672,7 +678,7 @@ } } -#[derive(Debug, PartialEq, Update)] +#[derive(Debug, PartialEq, SalsaValue)] pub struct TraitImpls<'db> { map: FxHashMap<TraitId, OneTraitImpls<'db>>, } @@ -844,10 +850,10 @@ ) -> R { let blocks = std::iter::successors(block, |block| block.module(db).block(db)); for impl_ in blocks.filter_map(|block| Self::for_block(db, block)) { - ret!(for_each(impl_)); + try_visit!(for_each(impl_)); } for impl_ in Self::for_crate_and_deps(db, krate) { - ret!(for_each(impl_)); + try_visit!(for_each(impl_)); } R::output() } @@ -862,7 +868,7 @@ ) -> R { let in_self_and_deps = TraitImpls::for_crate_and_deps(db, krate); for impl_ in in_self_and_deps { - ret!(for_each(impl_)); + try_visit!(for_each(impl_)); } // We must not provide duplicate impls to the solver. Therefore we work with the following strategy: @@ -885,14 +891,14 @@ for impl_ in blocks_iter(trait_block).filter_map(|block| TraitImpls::for_block(db, block)) { - ret!(for_each(impl_)); + try_visit!(for_each(impl_)); } } else { for impl_ in for_each_block(trait_block, type_block) { - ret!(for_each(impl_)); + try_visit!(for_each(impl_)); } for impl_ in for_each_block(type_block, trait_block) { - ret!(for_each(impl_)); + try_visit!(for_each(impl_)); } } R::output()
diff --git a/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs index fe4b383..6bfc99b 100644 --- a/crates/hir-ty/src/mir.rs +++ b/crates/hir-ty/src/mir.rs
@@ -15,7 +15,7 @@ CollectAndApply, GenericTypeVisitable, inherent::{GenericArgs as _, IntoKind, Ty as _}, }; -use salsa::Update; +use salsa::SalsaValue; use smallvec::{SmallVec, smallvec}; use stdx::impl_from; @@ -32,13 +32,11 @@ }, }; -mod borrowck; mod eval; mod lower; mod monomorphization; mod pretty; -pub use borrowck::{BorrowckResult, MutabilityReason}; pub use eval::{ Evaluator, MirEvalError, VTableMap, interpret_mir, pad16, render_const_using_debug_impl, }; @@ -148,7 +146,7 @@ /// The index of a field (whether of a struct/enum variant, tuple, or closure). /// For a struct/enum it converts from and to the LocalFieldId, for a tuple or closure it's simply the index. -#[derive(Copy, Clone, PartialEq, Eq, Hash, salsa::Update, PartialOrd, Ord, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, salsa::SalsaValue, PartialOrd, Ord, Debug)] pub struct FieldIndex(pub u32); impl FieldIndex { @@ -1055,7 +1053,7 @@ pub is_cleanup: bool, } -#[derive(Debug, Clone, PartialEq, Eq, Update)] +#[derive(Debug, Clone, PartialEq, Eq, SalsaValue)] pub struct MirBody<'db> { pub basic_blocks: Arena<BasicBlock>, pub locals: Arena<Local>, @@ -1180,7 +1178,7 @@ } } -#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[derive(Debug, PartialEq, Eq, Clone, Copy, SalsaValue)] pub enum MirSpan { ExprId(ExprId), PatId(PatId),
diff --git a/crates/hir-ty/src/mir/borrowck.rs b/crates/hir-ty/src/mir/borrowck.rs deleted file mode 100644 index e860ae8..0000000 --- a/crates/hir-ty/src/mir/borrowck.rs +++ /dev/null
@@ -1,696 +0,0 @@ -//! MIR borrow checker, which is used in diagnostics like `unused_mut` - -// Currently it is an ad-hoc implementation, only useful for mutability analysis. Feel free to remove all of these -// if needed for implementing a proper borrow checker. - -use std::iter; - -use either::Either; -use hir_def::HasModule; -use la_arena::ArenaMap; -use rustc_hash::FxHashMap; -use salsa::Update; -use stdx::never; - -use crate::{ - InferBodyId, - closure_analysis::ProjectionKind as HirProjectionKind, - db::{HirDatabase, InternedClosureId}, - display::DisplayTarget, - mir::{OperandKind, PlaceTy}, - next_solver::{ - DbInterner, ParamEnv, StoredTy, TypingMode, - infer::{DbInternerInferExt, InferCtxt}, - }, -}; - -use super::{ - BasicBlockId, BorrowKind, LocalId, MirBody, MirLowerError, MirSpan, MutBorrowKind, Operand, - Place, ProjectionElem, Rvalue, StatementKind, TerminatorKind, -}; - -#[derive(Debug, Clone, PartialEq, Eq)] -/// Stores spans which implies that the local should be mutable. -pub enum MutabilityReason { - Mut { spans: Vec<MirSpan> }, - Not, - Unused, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct MovedOutOfRef { - pub ty: StoredTy, - pub span: MirSpan, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct PartiallyMoved { - pub ty: StoredTy, - pub span: MirSpan, - pub local: LocalId, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct BorrowRegion { - pub local: LocalId, - pub kind: BorrowKind, - pub places: Vec<MirSpan>, -} - -#[derive(Debug, Clone, PartialEq, Eq, Update)] -pub struct BorrowckResult<'db> { - owner: Either<InferBodyId<'db>, InternedClosureId<'db>>, - pub mutability_of_locals: ArenaMap<LocalId, MutabilityReason>, - pub moved_out_of_ref: Vec<MovedOutOfRef>, - pub partially_moved: Vec<PartiallyMoved>, - pub borrow_regions: Vec<BorrowRegion>, -} - -impl<'db> BorrowckResult<'db> { - pub fn mir_body(&self, db: &'db dyn HirDatabase) -> &'db MirBody<'db> { - match self.owner { - Either::Left(it) => db.mir_body(it).unwrap(), - Either::Right(it) => db.mir_body_for_closure(it).unwrap(), - } - } -} - -fn all_mir_bodies<'db>( - db: &'db dyn HirDatabase, - def: InferBodyId<'db>, - mut cb: impl FnMut( - &'db MirBody<'db>, - Either<InferBodyId<'db>, InternedClosureId<'db>>, - ) -> BorrowckResult<'db>, - mut merge_from_closures: impl FnMut( - (&mut BorrowckResult<'db>, &'db MirBody<'db>), - (&BorrowckResult<'db>, &'db MirBody<'db>), - ), -) -> Result<Box<[BorrowckResult<'db>]>, MirLowerError<'db>> { - fn for_closure<'db>( - db: &'db dyn HirDatabase, - c: InternedClosureId<'db>, - results: &mut Vec<(BorrowckResult<'db>, &'db MirBody<'db>)>, - cb: &mut impl FnMut( - &'db MirBody<'db>, - Either<InferBodyId<'db>, InternedClosureId<'db>>, - ) -> BorrowckResult<'db>, - merge_from_closures: &mut impl FnMut( - (&mut BorrowckResult<'db>, &'db MirBody<'db>), - (&BorrowckResult<'db>, &'db MirBody<'db>), - ), - ) -> Result<(), MirLowerError<'db>> { - match db.mir_body_for_closure(c) { - Ok(body) => { - let parent_index = results.len(); - results.push((cb(body, Either::Right(c)), body)); - body.closures - .iter() - .try_for_each(|&it| for_closure(db, it, results, cb, merge_from_closures))?; - merge(results, merge_from_closures, parent_index); - Ok(()) - } - Err(e) => Err(e.clone()), - } - } - - fn merge<'db>( - results: &mut [(BorrowckResult<'db>, &'db MirBody<'db>)], - merge: &mut impl FnMut( - (&mut BorrowckResult<'db>, &'db MirBody<'db>), - (&BorrowckResult<'db>, &'db MirBody<'db>), - ), - parent_index: usize, - ) { - let (parent_and_before, children) = results.split_at_mut(parent_index + 1); - let (parent, parent_mir_body) = &mut parent_and_before[parent_and_before.len() - 1]; - children.iter().for_each(|(child, child_mir_body)| { - merge((parent, parent_mir_body), (child, child_mir_body)) - }); - } - - let mut results = Vec::new(); - match db.mir_body(def) { - Ok(body) => { - results.push((cb(body, Either::Left(def)), body)); - body.closures.iter().try_for_each(|&it| { - for_closure(db, it, &mut results, &mut cb, &mut merge_from_closures) - })?; - merge(&mut results, &mut merge_from_closures, 0); - Ok(results.into_iter().map(|(it, _)| it).collect()) - } - Err(e) => Err(e.clone()), - } -} - -impl<'db> InferBodyId<'db> { - pub fn borrowck( - self, - db: &'db dyn HirDatabase, - ) -> Result<&'db [BorrowckResult<'db>], MirLowerError<'db>> { - return borrowck_query(db, self).map_err(|e| e.clone()); - - #[salsa::tracked(returns(as_deref), lru = 2024)] - fn borrowck_query<'db>( - db: &'db dyn HirDatabase, - def: InferBodyId<'db>, - ) -> Result<Box<[BorrowckResult<'db>]>, MirLowerError<'db>> { - let _p = tracing::info_span!("InferBodyId::borrowck").entered(); - let module = def.module(db); - let interner = DbInterner::new_with(db, module.krate(db)); - let env = db.trait_environment(def.generic_def(db)); - // This calculates opaques defining scope which is a bit costly therefore is put outside `all_mir_bodies()`. - let typing_mode = TypingMode::borrowck(interner, def.into()); - all_mir_bodies( - db, - def, - |body, owner| { - // FIXME(next-solver): Opaques. - let infcx = interner.infer_ctxt().build(typing_mode); - BorrowckResult { - owner, - mutability_of_locals: mutability_of_locals(&infcx, env, body), - moved_out_of_ref: moved_out_of_ref(&infcx, env, body), - partially_moved: partially_moved(&infcx, env, body), - borrow_regions: borrow_regions(db, body), - } - }, - |(parent, parent_mir_body), (child, child_mir_body)| { - for (upvar, child_locals) in &child_mir_body.upvar_locals { - let Some(&parent_local) = parent_mir_body.binding_locals.get(*upvar) else { - continue; - }; - for (child_local, capture_place) in child_locals { - if !capture_place - .projections - .iter() - .any(|proj| matches!(proj.kind, HirProjectionKind::Deref)) - { - let parent_mol = &mut parent.mutability_of_locals[parent_local]; - match (&*parent_mol, &child.mutability_of_locals[*child_local]) { - (MutabilityReason::Mut { .. }, _) => {} - (_, MutabilityReason::Mut { .. }) => { - // FIXME: Fix the child spans. - *parent_mol = MutabilityReason::Mut { spans: Vec::new() } - } - (MutabilityReason::Not, _) => {} - (_, MutabilityReason::Not) => { - *parent_mol = MutabilityReason::Not - } - (MutabilityReason::Unused, MutabilityReason::Unused) => {} - } - } - } - } - }, - ) - } - } -} - -fn moved_out_of_ref<'db>( - infcx: &InferCtxt<'db>, - env: ParamEnv<'db>, - body: &MirBody<'db>, -) -> Vec<MovedOutOfRef> { - let db = infcx.interner.db; - let mut result = vec![]; - let mut for_operand = |op: &Operand, span: MirSpan| match &op.kind { - OperandKind::Copy(p) | OperandKind::Move(p) => { - let mut ty = PlaceTy::from_ty(body.locals[p.local].ty.as_ref()); - let mut is_dereference_of_ref = false; - for proj in p.projection.lookup() { - if *proj == ProjectionElem::Deref && ty.ty.as_reference().is_some() { - is_dereference_of_ref = true; - } - ty = ty.projection_ty(infcx, proj, env); - } - if is_dereference_of_ref - && !infcx.type_is_copy_modulo_regions(env, ty.ty) - && !ty.ty.references_non_lt_error() - { - result.push(MovedOutOfRef { span: op.span.unwrap_or(span), ty: ty.ty.store() }); - } - } - OperandKind::Constant { .. } | OperandKind::Static(_) | OperandKind::Allocation { .. } => {} - }; - for (_, block) in body.basic_blocks.iter() { - db.unwind_if_revision_cancelled(); - for statement in &block.statements { - match &statement.kind { - StatementKind::Assign(_, r) => match r { - Rvalue::UnaryOp(_, o) - | Rvalue::Cast(_, o, _) - | Rvalue::Repeat(o, _) - | Rvalue::Use(o) => for_operand(o, statement.span), - Rvalue::CopyForDeref(_) - | Rvalue::Discriminant(_) - | Rvalue::Len(_) - | Rvalue::Ref(_, _) => (), - Rvalue::CheckedBinaryOp(_, o1, o2) => { - for_operand(o1, statement.span); - for_operand(o2, statement.span); - } - Rvalue::Aggregate(_, ops) => { - for op in ops.iter() { - for_operand(op, statement.span); - } - } - Rvalue::ThreadLocalRef(n) - | Rvalue::AddressOf(n) - | Rvalue::BinaryOp(n) - | Rvalue::NullaryOp(n) => match *n {}, - }, - StatementKind::FakeRead(_) - | StatementKind::Deinit(_) - | StatementKind::StorageLive(_) - | StatementKind::StorageDead(_) - | StatementKind::Nop => (), - } - } - match &block.terminator { - Some(terminator) => match &terminator.kind { - TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, terminator.span), - TerminatorKind::FalseEdge { .. } - | TerminatorKind::FalseUnwind { .. } - | TerminatorKind::Goto { .. } - | TerminatorKind::UnwindResume - | TerminatorKind::CoroutineDrop - | TerminatorKind::Abort - | TerminatorKind::Return - | TerminatorKind::Unreachable - | TerminatorKind::Drop { .. } => (), - TerminatorKind::DropAndReplace { value, .. } => { - for_operand(value, terminator.span); - } - TerminatorKind::Call { func, args, .. } => { - for_operand(func, terminator.span); - args.iter().for_each(|it| for_operand(it, terminator.span)); - } - TerminatorKind::Assert { cond, .. } => { - for_operand(cond, terminator.span); - } - TerminatorKind::Yield { value, .. } => { - for_operand(value, terminator.span); - } - }, - None => (), - } - } - result.shrink_to_fit(); - result -} - -fn partially_moved<'db>( - infcx: &InferCtxt<'db>, - env: ParamEnv<'db>, - body: &MirBody<'db>, -) -> Vec<PartiallyMoved> { - let db = infcx.interner.db; - let mut result = vec![]; - let mut for_operand = |op: &Operand, span: MirSpan| match &op.kind { - OperandKind::Copy(p) | OperandKind::Move(p) => { - let ty = p.as_ref().ty(body, infcx, env).ty; - if !infcx.type_is_copy_modulo_regions(env, ty) && !ty.references_non_lt_error() { - result.push(PartiallyMoved { span, ty: ty.store(), local: p.local }); - } - } - OperandKind::Constant { .. } | OperandKind::Static(_) | OperandKind::Allocation { .. } => {} - }; - for (_, block) in body.basic_blocks.iter() { - db.unwind_if_revision_cancelled(); - for statement in &block.statements { - match &statement.kind { - StatementKind::Assign(_, r) => match r { - Rvalue::UnaryOp(_, o) - | Rvalue::Cast(_, o, _) - | Rvalue::Repeat(o, _) - | Rvalue::Use(o) => for_operand(o, statement.span), - Rvalue::CopyForDeref(_) - | Rvalue::Discriminant(_) - | Rvalue::Len(_) - | Rvalue::Ref(_, _) => (), - Rvalue::CheckedBinaryOp(_, o1, o2) => { - for_operand(o1, statement.span); - for_operand(o2, statement.span); - } - Rvalue::Aggregate(_, ops) => { - for op in ops.iter() { - for_operand(op, statement.span); - } - } - Rvalue::ThreadLocalRef(n) - | Rvalue::AddressOf(n) - | Rvalue::BinaryOp(n) - | Rvalue::NullaryOp(n) => match *n {}, - }, - StatementKind::FakeRead(_) - | StatementKind::Deinit(_) - | StatementKind::StorageLive(_) - | StatementKind::StorageDead(_) - | StatementKind::Nop => (), - } - } - match &block.terminator { - Some(terminator) => match &terminator.kind { - TerminatorKind::SwitchInt { discr, .. } => for_operand(discr, terminator.span), - TerminatorKind::FalseEdge { .. } - | TerminatorKind::FalseUnwind { .. } - | TerminatorKind::Goto { .. } - | TerminatorKind::UnwindResume - | TerminatorKind::CoroutineDrop - | TerminatorKind::Abort - | TerminatorKind::Return - | TerminatorKind::Unreachable - | TerminatorKind::Drop { .. } => (), - TerminatorKind::DropAndReplace { value, .. } => { - for_operand(value, terminator.span); - } - TerminatorKind::Call { func, args, .. } => { - for_operand(func, terminator.span); - args.iter().for_each(|it| for_operand(it, terminator.span)); - } - TerminatorKind::Assert { cond, .. } => { - for_operand(cond, terminator.span); - } - TerminatorKind::Yield { value, .. } => { - for_operand(value, terminator.span); - } - }, - None => (), - } - } - result.shrink_to_fit(); - result -} - -fn borrow_regions<'db>(db: &'db dyn HirDatabase, body: &MirBody<'db>) -> Vec<BorrowRegion> { - let mut borrows = FxHashMap::default(); - for (_, block) in body.basic_blocks.iter() { - db.unwind_if_revision_cancelled(); - for statement in &block.statements { - if let StatementKind::Assign(_, Rvalue::Ref(kind, p)) = &statement.kind { - borrows - .entry(p.local) - .and_modify(|it: &mut BorrowRegion| { - it.places.push(statement.span); - }) - .or_insert_with(|| BorrowRegion { - local: p.local, - kind: *kind, - places: vec![statement.span], - }); - } - } - match &block.terminator { - Some(terminator) => match &terminator.kind { - TerminatorKind::FalseEdge { .. } - | TerminatorKind::FalseUnwind { .. } - | TerminatorKind::Goto { .. } - | TerminatorKind::UnwindResume - | TerminatorKind::CoroutineDrop - | TerminatorKind::Abort - | TerminatorKind::Return - | TerminatorKind::Unreachable - | TerminatorKind::Drop { .. } => (), - TerminatorKind::DropAndReplace { .. } => {} - TerminatorKind::Call { .. } => {} - _ => (), - }, - None => (), - } - } - - borrows.into_values().collect() -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -enum ProjectionCase { - /// Projection is a local - Direct, - /// Projection is some field or slice of a local - DirectPart, - /// Projection is deref of something - Indirect, -} - -fn place_case<'db>( - infcx: &InferCtxt<'db>, - env: ParamEnv<'db>, - body: &MirBody<'db>, - lvalue: &Place, -) -> ProjectionCase { - let mut is_part_of = false; - let mut ty = PlaceTy::from_ty(body.locals[lvalue.local].ty.as_ref()); - for proj in lvalue.projection.lookup().iter() { - match proj { - ProjectionElem::Deref if ty.ty.as_adt().is_none() => return ProjectionCase::Indirect, // It's indirect in case of reference and raw - ProjectionElem::Deref // It's direct in case of `Box<T>` - | ProjectionElem::ConstantIndex { .. } - | ProjectionElem::Subslice { .. } - | ProjectionElem::Field(_) - | ProjectionElem::Index(_) => { - is_part_of = true; - } - ProjectionElem::Downcast(_) => (), - } - ty = ty.projection_ty(infcx, proj, env); - } - if is_part_of { ProjectionCase::DirectPart } else { ProjectionCase::Direct } -} - -/// Returns a map from basic blocks to the set of locals that might be ever initialized before -/// the start of the block. Only `StorageDead` can remove something from this map, and we ignore -/// `Uninit` and `drop` and similar after initialization. -fn ever_initialized_map( - db: &dyn HirDatabase, - body: &MirBody<'_>, -) -> ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> { - let mut result: ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>> = - body.basic_blocks.iter().map(|it| (it.0, ArenaMap::default())).collect(); - fn dfs( - db: &dyn HirDatabase, - body: &MirBody<'_>, - l: LocalId, - stack: &mut Vec<BasicBlockId>, - result: &mut ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>>, - ) { - while let Some(b) = stack.pop() { - let mut is_ever_initialized = result[b][l]; // It must be filled, as we use it as mark for dfs - let block = &body.basic_blocks[b]; - for statement in &block.statements { - match &statement.kind { - StatementKind::Assign(p, _) => { - if p.projection.is_empty() && p.local == l { - is_ever_initialized = true; - } - } - StatementKind::StorageDead(p) => { - if *p == l { - is_ever_initialized = false; - } - } - StatementKind::Deinit(_) - | StatementKind::FakeRead(_) - | StatementKind::Nop - | StatementKind::StorageLive(_) => (), - } - } - let Some(terminator) = &block.terminator else { - never!( - "Terminator should be none only in construction.\nThe body:\n{}", - body.pretty_print(db, DisplayTarget::from_crate(db, body.owner.krate(db))) - ); - return; - }; - let mut process = |target, is_ever_initialized| { - if !result[target].contains_idx(l) || !result[target][l] && is_ever_initialized { - result[target].insert(l, is_ever_initialized); - stack.push(target); - } - }; - match &terminator.kind { - TerminatorKind::Goto { target } => process(*target, is_ever_initialized), - TerminatorKind::SwitchInt { targets, .. } => { - targets.all_targets().iter().for_each(|&it| process(it, is_ever_initialized)); - } - TerminatorKind::UnwindResume - | TerminatorKind::Abort - | TerminatorKind::Return - | TerminatorKind::Unreachable => (), - TerminatorKind::Call { target, cleanup, destination, .. } => { - if destination.projection.is_empty() && destination.local == l { - is_ever_initialized = true; - } - target.iter().chain(cleanup).for_each(|&it| process(it, is_ever_initialized)); - } - TerminatorKind::Drop { target, unwind, place: _ } => { - iter::once(target) - .chain(unwind) - .for_each(|&it| process(it, is_ever_initialized)); - } - TerminatorKind::DropAndReplace { .. } - | TerminatorKind::Assert { .. } - | TerminatorKind::Yield { .. } - | TerminatorKind::CoroutineDrop - | TerminatorKind::FalseEdge { .. } - | TerminatorKind::FalseUnwind { .. } => { - never!("We don't emit these MIR terminators yet"); - } - } - } - } - let mut stack = Vec::new(); - for &l in &body.param_locals { - result[body.start_block].insert(l, true); - stack.clear(); - stack.push(body.start_block); - dfs(db, body, l, &mut stack, &mut result); - } - for l in body.locals.iter().map(|it| it.0) { - db.unwind_if_revision_cancelled(); - if !result[body.start_block].contains_idx(l) { - result[body.start_block].insert(l, false); - stack.clear(); - stack.push(body.start_block); - dfs(db, body, l, &mut stack, &mut result); - } - } - result -} - -fn push_mut_span(local: LocalId, span: MirSpan, result: &mut ArenaMap<LocalId, MutabilityReason>) { - match &mut result[local] { - MutabilityReason::Mut { spans } => spans.push(span), - it @ (MutabilityReason::Not | MutabilityReason::Unused) => { - *it = MutabilityReason::Mut { spans: vec![span] } - } - }; -} - -fn record_usage(local: LocalId, result: &mut ArenaMap<LocalId, MutabilityReason>) { - if let it @ MutabilityReason::Unused = &mut result[local] { - *it = MutabilityReason::Not; - }; -} - -fn record_usage_for_operand(arg: &Operand, result: &mut ArenaMap<LocalId, MutabilityReason>) { - if let OperandKind::Copy(p) | OperandKind::Move(p) = &arg.kind { - record_usage(p.local, result); - } -} - -fn mutability_of_locals<'db>( - infcx: &InferCtxt<'db>, - env: ParamEnv<'db>, - body: &MirBody<'db>, -) -> ArenaMap<LocalId, MutabilityReason> { - let db = infcx.interner.db; - let mut result: ArenaMap<LocalId, MutabilityReason> = - body.locals.iter().map(|it| (it.0, MutabilityReason::Unused)).collect(); - - let ever_init_maps = ever_initialized_map(db, body); - for (block_id, mut ever_init_map) in ever_init_maps.into_iter() { - let block = &body.basic_blocks[block_id]; - for statement in &block.statements { - match &statement.kind { - StatementKind::Assign(place, value) => { - match place_case(infcx, env, body, place) { - ProjectionCase::Direct => { - if ever_init_map.get(place.local).copied().unwrap_or_default() { - push_mut_span(place.local, statement.span, &mut result); - } else { - ever_init_map.insert(place.local, true); - } - } - ProjectionCase::DirectPart => { - // Partial initialization is not supported, so it is definitely `mut` - push_mut_span(place.local, statement.span, &mut result); - } - ProjectionCase::Indirect => { - record_usage(place.local, &mut result); - } - } - match value { - Rvalue::CopyForDeref(p) - | Rvalue::Discriminant(p) - | Rvalue::Len(p) - | Rvalue::Ref(_, p) => { - record_usage(p.local, &mut result); - } - Rvalue::Use(o) - | Rvalue::Repeat(o, _) - | Rvalue::Cast(_, o, _) - | Rvalue::UnaryOp(_, o) => record_usage_for_operand(o, &mut result), - Rvalue::CheckedBinaryOp(_, o1, o2) => { - for o in [o1, o2] { - record_usage_for_operand(o, &mut result); - } - } - Rvalue::Aggregate(_, args) => { - for arg in args.iter() { - record_usage_for_operand(arg, &mut result); - } - } - Rvalue::ThreadLocalRef(n) - | Rvalue::AddressOf(n) - | Rvalue::BinaryOp(n) - | Rvalue::NullaryOp(n) => match *n {}, - } - if let Rvalue::Ref( - BorrowKind::Mut { - kind: MutBorrowKind::Default | MutBorrowKind::TwoPhasedBorrow, - }, - p, - ) = value - && place_case(infcx, env, body, p) != ProjectionCase::Indirect - { - push_mut_span(p.local, statement.span, &mut result); - } - } - StatementKind::FakeRead(p) => { - record_usage(p.local, &mut result); - } - StatementKind::StorageDead(p) => { - ever_init_map.insert(*p, false); - } - StatementKind::Deinit(_) | StatementKind::StorageLive(_) | StatementKind::Nop => (), - } - } - let Some(terminator) = &block.terminator else { - never!("Terminator should be none only in construction"); - continue; - }; - match &terminator.kind { - TerminatorKind::Goto { .. } - | TerminatorKind::UnwindResume - | TerminatorKind::Abort - | TerminatorKind::Return - | TerminatorKind::Unreachable - | TerminatorKind::FalseEdge { .. } - | TerminatorKind::FalseUnwind { .. } - | TerminatorKind::CoroutineDrop - | TerminatorKind::Drop { .. } - | TerminatorKind::DropAndReplace { .. } - | TerminatorKind::Assert { .. } - | TerminatorKind::Yield { .. } => (), - TerminatorKind::SwitchInt { discr, targets: _ } => { - record_usage_for_operand(discr, &mut result); - } - TerminatorKind::Call { destination, args, func, .. } => { - record_usage_for_operand(func, &mut result); - for arg in args.iter() { - record_usage_for_operand(arg, &mut result); - } - if destination.projection.is_empty() { - if ever_init_map.get(destination.local).copied().unwrap_or_default() { - push_mut_span(destination.local, terminator.span, &mut result); - } else { - ever_init_map.insert(destination.local, true); - } - } - } - } - } - result -}
diff --git a/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index e968da5..f849571 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/crates/hir-ty/src/mir/eval.rs
@@ -2,7 +2,7 @@ use std::{borrow::Cow, cell::RefCell, fmt::Write, iter, mem, ops::Range}; -use base_db::{Crate, salsa::update_fallback_db, target::TargetLoadError}; +use base_db::{Crate, target::TargetLoadError}; use either::Either; use hir_def::{ AdtId, DefWithBodyId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, StaticId, @@ -31,7 +31,7 @@ AliasTyKind, inherent::{GenericArgs as _, IntoKind, Region as _, SliceLike, Ty as _}, }; -use salsa::Update; +use salsa::SalsaValue; use span::FileId; use stdx::never; use syntax::{SyntaxNodePtr, TextRange}; @@ -349,7 +349,7 @@ } } -#[derive(Clone, PartialEq, Eq, Update)] +#[derive(Clone, PartialEq, Eq, SalsaValue)] pub enum MirEvalError<'db> { ConstEvalError(String, Box<ConstEvalError<'db>>), LayoutError(LayoutError, StoredTy), @@ -366,8 +366,7 @@ InvalidConst, InFunction( Box<MirEvalError<'db>>, - #[update(bounds(InternedClosureId<'db>: Update), unsafe(with(update_fallback_db::<'db, _>)))] - Vec<(Either<FunctionId, InternedClosureId<'db>>, MirSpan, InferBodyId<'db>)>, + Vec<(Either<FunctionId, InternedClosureId<'db>>, MirSpan, InferBodyId<'db>)>, ), ExecutionLimitExceeded, StackOverflow, @@ -2548,7 +2547,6 @@ ty: Ty<'db>, locals: &Locals<'a, 'db>, ) -> Result<'db, ()> { - // FIXME: support indirect references let layout = self.layout(ty)?; let my_size = self.size_of_sized(ty, locals, "value to patch address")?; use rustc_type_ir::TyKind; @@ -2574,9 +2572,30 @@ )?; } None => { - let current = from_bytes!(usize, self.read_memory(addr, my_size / 2)?); - if let Some(it) = patch_map.get(¤t) { - self.write_memory(addr, &it.to_le_bytes())?; + let bytes = self.read_memory(addr, my_size)?; + let (current, metadata) = bytes.split_at(my_size / 2); + let metadata = metadata.to_vec(); + let current = from_bytes!(usize, current); + let patched = match patch_map.get(¤t) { + Some(it) => { + self.write_memory(addr, &it.to_le_bytes())?; + *it + } + None => current, + }; + let patched = Address::from_usize(patched); + if let TyKind::Slice(inner) = t.kind() { + let len = from_bytes!(usize, metadata); + let size = self.size_of_sized(inner, locals, "slice item to patch")?; + for i in 0..len { + self.patch_addresses( + patch_map, + ty_of_bytes, + patched.offset(i * size), + inner, + locals, + )?; + } } } }
diff --git a/crates/hir-ty/src/mir/eval/shim.rs b/crates/hir-ty/src/mir/eval/shim.rs index e569b32..177b2c8 100644 --- a/crates/hir-ty/src/mir/eval/shim.rs +++ b/crates/hir-ty/src/mir/eval/shim.rs
@@ -1012,6 +1012,43 @@ let dst = Interval { addr: dst, size }; dst.write_from_interval(self, src) } + "slice_get_unchecked" => { + let [slice_ptr, index] = args else { + return Err(MirEvalError::InternalError( + "slice_get_unchecked args are not provided".into(), + )); + }; + let Some(ty) = generic_args.as_slice().get(2).and_then(|it| it.ty()) else { + return Err(MirEvalError::InternalError( + "slice_get_unchecked item type is not provided".into(), + )); + }; + let slice_ptr = slice_ptr.get(self)?; + let ptr_size = self.ptr_size(); + let Some(data) = slice_ptr.get(..ptr_size) else { + return Err(MirEvalError::InternalError( + "slice_get_unchecked slice pointer is too small".into(), + )); + }; + let Some(len) = slice_ptr.get(ptr_size..2 * ptr_size) else { + return Err(MirEvalError::InternalError( + "slice_get_unchecked slice metadata is missing".into(), + )); + }; + let slice_ptr = Address::from_bytes(data)?; + let len = from_bytes!(usize, len); + let index = from_bytes!(usize, index.get(self)?); + if index >= len { + return Err(MirEvalError::UndefinedBehavior(format!( + "slice_get_unchecked index {index} is out of bounds for slice of length {len}" + ))); + } + let size = self.size_of_sized(ty, locals, "slice_get_unchecked item type")?; + let offset = index* size; + let addr = slice_ptr.to_usize() + offset; + let addr = Address::from_usize(addr); + destination.write_from_bytes(self, &addr.to_bytes()[..destination.size]) + } "offset" | "arith_offset" => { let [ptr, offset] = args else { return Err(MirEvalError::InternalError("offset args are not provided".into()));
diff --git a/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs index 68d1976..7431ac8 100644 --- a/crates/hir-ty/src/mir/eval/tests.rs +++ b/crates/hir-ty/src/mir/eval/tests.rs
@@ -1115,6 +1115,109 @@ } #[test] +fn slice_get_unchecked_intrinsic() { + check_pass( + r#" +//- minicore: panic +#[rustc_intrinsic] +unsafe fn slice_get_unchecked<ItemPtr, SlicePtr, T>( + slice_ptr: SlicePtr, + index: usize, +) -> ItemPtr; + +fn should_not_reach() { panic!() } + +fn main() { + let values = [10, 20, 30]; + let slice_ptr = &values as *const [i32]; + let item_ptr = unsafe { + slice_get_unchecked::<*const i32, *const [i32], i32>(slice_ptr, 1) + }; + if unsafe { *item_ptr } != 20 { + should_not_reach(); + } +} +"#, + ); +} + +#[test] +fn slice_get_unchecked_out_of_bounds() { + check_error_with( + r#" +#[rustc_intrinsic] +unsafe fn slice_get_unchecked<ItemPtr, SlicePtr, T>( + slice_ptr: SlicePtr, + index: usize, +) -> ItemPtr; + +fn main() { + let values = [()]; + let slice_ptr = &values as *const [()]; + let _item = unsafe { + slice_get_unchecked::<*const (), *const [()], ()>(slice_ptr, 1) + }; +} +"#, + |e| { + let mut err = &e; + while let MirEvalError::InFunction(inner, _) = err { + err = inner; + } + matches!(err, MirEvalError::UndefinedBehavior(_)) + }, + ); +} + +#[test] +fn slice_get_unchecked_const_slice() { + check_pass( + r#" +//- minicore: panic +#[rustc_intrinsic] +unsafe fn slice_get_unchecked<ItemPtr, SlicePtr, T>( + slice_ptr: SlicePtr, + index: usize, +) -> ItemPtr; + +struct Flag { + name: &'static str, + value: u16, +} + +const PURE: &str = "PURE"; +const NOMEM: &str = "NOMEM"; +const READONLY: &str = "READONLY"; +const PRESERVES_FLAGS: &str = "PRESERVES_FLAGS"; +const NORETURN: &str = "NORETURN"; +const NOSTACK: &str = "NOSTACK"; +const ATT_SYNTAX: &str = "ATT_SYNTAX"; +const FLAGS: &[Flag] = &[ + Flag { name: PURE, value: 1 }, + Flag { name: NOMEM, value: 2 }, + Flag { name: READONLY, value: 4 }, + Flag { name: PRESERVES_FLAGS, value: 8 }, + Flag { name: NORETURN, value: 16 }, + Flag { name: NOSTACK, value: 32 }, + Flag { name: ATT_SYNTAX, value: 64 }, +]; + +fn should_not_reach() { panic!() } + +fn main() { + let flag = unsafe { + slice_get_unchecked::<&Flag, &[Flag], Flag>(FLAGS, 6) + }; + let name = flag.name as *const str as *const u8; + if unsafe { *name } != b'A' || flag.value != 64 { + should_not_reach(); + } +} +"#, + ); +} + +#[test] fn unreachable_intrinsic() { check_error_with( r#"
diff --git a/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 620d768..2537f64 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/crates/hir-ty/src/mir/lower.rs
@@ -23,7 +23,7 @@ use rustc_apfloat::Float; use rustc_hash::FxHashMap; use rustc_type_ir::inherent::{Const as _, GenericArgs as _, IntoKind, Ty as _}; -use salsa::Update; +use salsa::SalsaValue; use span::{Edition, FileId}; use syntax::TextRange; @@ -97,7 +97,7 @@ } // FIXME: Make this smaller, its stored in database queries -#[derive(Debug, Clone, PartialEq, Eq, Update)] +#[derive(Debug, Clone, PartialEq, Eq, SalsaValue)] pub enum MirLowerError<'db> { ConstEvalError(Box<str>, Box<ConstEvalError<'db>>), LayoutError(LayoutError),
diff --git a/crates/hir-ty/src/mir/lower/tests.rs b/crates/hir-ty/src/mir/lower/tests.rs index fdd67fc..1f6aa5c 100644 --- a/crates/hir-ty/src/mir/lower/tests.rs +++ b/crates/hir-ty/src/mir/lower/tests.rs
@@ -1,7 +1,6 @@ -use hir_def::DefWithBodyId; use test_fixture::WithFixture; -use crate::{InferBodyId, db::HirDatabase, setup_tracing, test_db::TestDB}; +use crate::{db::HirDatabase, setup_tracing, test_db::TestDB}; fn lower_mir(#[rust_analyzer::rust_fixture] ra_fixture: &str) { let _tracing = setup_tracing(); @@ -50,131 +49,3 @@ "#, ); } - -fn check_borrowck(#[rust_analyzer::rust_fixture] ra_fixture: &str) { - let _tracing = setup_tracing(); - let (db, file_ids) = TestDB::with_many_files(ra_fixture); - crate::attach_db(db.as_dyn(), || { - let file_id = *file_ids.last().unwrap(); - let module_id = db.module_for_file(file_id.file_id(&db)); - let def_map = module_id.def_map(&db); - let scope = &def_map[module_id].scope; - - let mut bodies: Vec<DefWithBodyId> = Vec::new(); - - for decl in scope.declarations() { - if let hir_def::ModuleDefId::FunctionId(f) = decl { - bodies.push(f.into()); - } - } - - for impl_id in scope.impls() { - let impl_items = impl_id.impl_items(&db); - for (_, item) in impl_items.items.iter() { - if let hir_def::AssocItemId::FunctionId(f) = item { - bodies.push((*f).into()); - } - } - } - - for body in bodies { - let _ = InferBodyId::from(body).borrowck(db.as_dyn()); - } - }) -} - -#[test] -fn regression_21173_const_generic_impl_with_assoc_type() { - check_borrowck( - r#" -pub trait Tr { - type Assoc; - fn f(&self, handle: Self::Assoc) -> i32; -} - -pub struct ConstGeneric<const N: usize>; - -impl<const N: usize> Tr for &ConstGeneric<N> { - type Assoc = AssocTy; - - fn f(&self, a: Self::Assoc) -> i32 { - a.x - } -} - -pub struct AssocTy { - x: i32, -} - "#, - ); -} - -#[test] -fn borrowck_tuple_field_projection_recovery_does_not_panic() { - check_borrowck( - r#" -//- minicore: sized -fn tuple_field() { - let t = (1,); - let x = t.1; -} - "#, - ); -} - -#[test] -fn borrowck_alias_projection_recovery_does_not_panic() { - check_borrowck( - r#" -//- minicore: sized -trait Tr { type A; } -fn alias<T: Tr>(x: T::A) { - let (a, b) = x; -} - "#, - ); -} - -#[test] -fn borrowck_opaque_downcast_recovery_does_not_panic() { - check_borrowck( - r#" -//- minicore: option, sized -struct PathBuf; -fn opaque<T>(path: T) -> impl Sized { - Some(path) -} -fn caller(path: &PathBuf) { - let Some(value) = opaque(path) else { return }; -} - "#, - ); -} - -#[test] -fn borrowck_hrtb_closure_argument_does_not_panic() { - check_borrowck( - r#" -//- minicore: fn, copy -enum Res<T, E> { - Ok(T), - Err(E), -} - -struct S; - -impl S { - fn set<F>(&mut self, _: F) - where - F: for<'a> Fn(&mut (), &'a [u8]) -> Res<(), ()>, - { - } -} - -fn main() { - let mut s = S; - s.set(|_, _| Res::Err(())); -} - "#, - ); -}
diff --git a/crates/hir-ty/src/next_solver/binder.rs b/crates/hir-ty/src/next_solver/binder.rs index 351d0c4..bc2f155 100644 --- a/crates/hir-ty/src/next_solver/binder.rs +++ b/crates/hir-ty/src/next_solver/binder.rs
@@ -1,6 +1,6 @@ use hir_def::TraitId; use macros::{TypeFoldable, TypeVisitable}; -use salsa::Update; +use salsa::SalsaValue; use crate::next_solver::{ Binder, Clauses, DbInterner, EarlyBinder, FnSig, FnSigKind, GenericArg, PolyFnSig, @@ -8,7 +8,7 @@ TraitRef, Ty, }; -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Update)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, SalsaValue)] pub struct StoredEarlyBinder<T>(T); impl<T> StoredEarlyBinder<T> { @@ -68,7 +68,7 @@ } } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, SalsaValue)] pub struct StoredPolyFnSig { bound_vars: StoredBoundVarKinds, sig: StoredFnSig, @@ -113,7 +113,7 @@ } } -#[derive(Debug, Clone, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable, Update)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, TypeVisitable, TypeFoldable, SalsaValue)] pub struct StoredTraitRef { #[type_visitable(ignore)] def_id: TraitId,
diff --git a/crates/hir-ty/src/next_solver/def_id.rs b/crates/hir-ty/src/next_solver/def_id.rs index b122a8c..c45387b 100644 --- a/crates/hir-ty/src/next_solver/def_id.rs +++ b/crates/hir-ty/src/next_solver/def_id.rs
@@ -10,7 +10,7 @@ }, }; use rustc_type_ir::inherent; -use salsa::Update; +use salsa::SalsaValue; use stdx::impl_from; use crate::{ @@ -29,7 +29,7 @@ Enum(EnumVariantId), } -#[derive(PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, Update)] +#[derive(PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, SalsaValue)] pub enum SolverDefId<'db> { AdtId(AdtId), ConstId(ConstId),
diff --git a/crates/hir-ty/src/next_solver/interner.rs b/crates/hir-ty/src/next_solver/interner.rs index 7554ca6..05e1559 100644 --- a/crates/hir-ty/src/next_solver/interner.rs +++ b/crates/hir-ty/src/next_solver/interner.rs
@@ -36,6 +36,7 @@ inherent::{self, Const as _, GenericsOf, IntoKind, SliceLike as _, Span as _, Ty as _}, lang_items::{SolverAdtLangItem, SolverProjectionLangItem, SolverTraitLangItem}, solve::{AdtDestructorKind, SizedTraitKind}, + try_visit, }; use crate::{ @@ -54,7 +55,6 @@ TraitAssocTyId, TraitIdWrapper, TypeAliasIdWrapper, UnevaluatedConst, Unnormalized, util::{explicit_item_bounds, explicit_item_self_bounds}, }, - ret, }; use super::{ @@ -211,12 +211,7 @@ } // SAFETY: It is safe to store this type in queries (but not `$name`). - unsafe impl salsa::Update for $stored_name { - unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool { - // SAFETY: Comparing by (pointer) equality is safe. - unsafe { salsa::update_fallback(old_pointer, new_value) } - } - } + unsafe impl salsa::SalsaValue for $stored_name {} impl std::fmt::Debug for $stored_name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -296,7 +291,7 @@ macro_rules! impl_stored_interned { ( $storage:ident, $name:ident, $stored_name:ident $(,)? ) => { - #[derive(Clone, PartialEq, Eq, Hash)] + #[derive(Clone, PartialEq, Eq, Hash, ::salsa::SalsaValue)] pub struct $stored_name { interned: ::intern::Interned<$storage>, } @@ -314,12 +309,6 @@ } } - unsafe impl salsa::Update for $stored_name { - unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool { - unsafe { salsa::update_fallback(old_pointer, new_value) } - } - } - impl std::fmt::Debug for $stored_name { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.as_ref().fmt(f) @@ -1671,10 +1660,10 @@ let (regular_impls, builtin_derive_impls) = impls.for_trait_and_self_ty(trait_def_id.0, &simp); for &impl_ in regular_impls { - ret!(f(impl_.into())); + try_visit!(f(impl_.into())); } for &impl_ in builtin_derive_impls { - ret!(f(impl_.into())); + try_visit!(f(impl_.into())); } R::output() }, @@ -1707,7 +1696,7 @@ let simp = fast_reject::simplify_type(self, self_ty, fast_reject::TreatParams::AsRigid) .unwrap(); - ret!(consider_impls_for_simplified_type(simp)); + try_visit!(consider_impls_for_simplified_type(simp)); } // HACK: For integer and float variables we have to manually look at all impls @@ -1735,7 +1724,7 @@ SimplifiedType::Uint(Usize), ]; for simp in possible_integers { - ret!(consider_impls_for_simplified_type(simp)); + try_visit!(consider_impls_for_simplified_type(simp)); } } @@ -1750,7 +1739,7 @@ ]; for simp in possible_floats { - ret!(consider_impls_for_simplified_type(simp)); + try_visit!(consider_impls_for_simplified_type(simp)); } } @@ -1791,7 +1780,7 @@ TraitImpls::for_each_crate_and_block(self.db, krate, block, &mut |impls| { for &impl_ in impls.blanket_impls(trait_def_id.0) { - ret!(f(impl_.into())); + try_visit!(f(impl_.into())); } R::output() }) @@ -2085,13 +2074,19 @@ opaque: Self::LocalOpaqueTyId, ) -> EarlyBinder<Self, Self::Ty> { let impl_trait_id = opaque.0.loc(self.db); - match impl_trait_id { + // The entry is missing when this call cycles back into the still-running inference + // of the defining body, as the cycle fallback is an empty result. + let hidden_type = match impl_trait_id { crate::ImplTraitId::ReturnTypeImplTrait(func, idx) => { - crate::opaques::rpit_hidden_types(self.db, func)[idx].get() + crate::opaques::rpit_hidden_types(self.db, func).get(idx) } crate::ImplTraitId::TypeAliasImplTrait(type_alias, idx) => { - crate::opaques::tait_hidden_types(self.db, type_alias)[idx].get() + crate::opaques::tait_hidden_types(self.db, type_alias).get(idx) } + }; + match hidden_type { + Some(hidden_type) => hidden_type.get(), + None => EarlyBinder::bind(Ty::new_error(self, ErrorGuaranteed)), } }
diff --git a/crates/hir-ty/src/next_solver/util.rs b/crates/hir-ty/src/next_solver/util.rs index e384ac4..7e40e3c 100644 --- a/crates/hir-ty/src/next_solver/util.rs +++ b/crates/hir-ty/src/next_solver/util.rs
@@ -723,15 +723,3 @@ recursion_depth: 0, }) } - -/// Copied from -/// <https://github.com/jdonszelmann/rust/blob/180725cff61d00dd1b9c35fc720a8befaec5d46b/compiler/rustc_middle/src/ty/context/impl_interner.rs#L534-L541> -#[macro_export] -macro_rules! ret { - ($e: expr) => { - match $e.branch() { - ::std::ops::ControlFlow::Break(b) => return R::from_residual(b), - ::std::ops::ControlFlow::Continue(()) => {} - } - }; -}
diff --git a/crates/hir-ty/src/opaques.rs b/crates/hir-ty/src/opaques.rs index 9cb0022..194c866 100644 --- a/crates/hir-ty/src/opaques.rs +++ b/crates/hir-ty/src/opaques.rs
@@ -5,14 +5,14 @@ signatures::ImplSignature, }; use hir_expand::name::Name; -use la_arena::ArenaMap; +use la_arena::{Arena, ArenaMap}; use rustc_type_ir::inherent::Ty as _; use syntax::ast; use crate::{ ImplTraitId, InferBodyId, InferenceResult, db::{HirDatabase, InternedOpaqueTyId}, - lower::{ImplTraitIdx, ImplTraits}, + lower::{ImplTrait, ImplTraitIdx}, next_solver::{ DbInterner, ErrorGuaranteed, SolverDefId, StoredEarlyBinder, StoredTy, Ty, TypingMode, infer::{DbInternerInferExt, traits::ObligationCause}, @@ -29,7 +29,7 @@ // A function may define its own RPITs. extend_with_opaques( db, - ImplTraits::return_type_impl_traits(db, func), + ImplTrait::return_type_impl_traits(db, func), |opaque_idx| ImplTraitId::ReturnTypeImplTrait(func, opaque_idx), result, ); @@ -38,7 +38,7 @@ let extend_with_taits = |type_alias| { extend_with_opaques( db, - ImplTraits::type_alias_impl_traits(db, type_alias), + ImplTrait::type_alias_impl_traits(db, type_alias), |opaque_idx| ImplTraitId::TypeAliasImplTrait(type_alias, opaque_idx), result, ); @@ -81,15 +81,13 @@ fn extend_with_opaques<'db>( db: &'db dyn HirDatabase, - opaques: &Option<Box<StoredEarlyBinder<ImplTraits>>>, + opaques: &Arena<ImplTrait>, mut make_impl_trait: impl FnMut(ImplTraitIdx) -> ImplTraitId, result: &mut Vec<SolverDefId<'db>>, ) { - if let Some(opaques) = opaques { - for (opaque_idx, _) in (**opaques).as_ref().skip_binder().impl_traits.iter() { - let opaque_id = InternedOpaqueTyId::new(db, make_impl_trait(opaque_idx)); - result.push(opaque_id.into()); - } + for (opaque_idx, _) in opaques.iter() { + let opaque_id = InternedOpaqueTyId::new(db, make_impl_trait(opaque_idx)); + result.push(opaque_id.into()); } } } @@ -116,12 +114,7 @@ type_alias: TypeAliasId, ) -> ArenaMap<ImplTraitIdx, StoredEarlyBinder<StoredTy>> { // Call this first, to not perform redundant work if there are no TAITs. - let Some(taits_count) = ImplTraits::type_alias_impl_traits(db, type_alias) - .as_deref() - .map(|taits| taits.as_ref().skip_binder().impl_traits.len()) - else { - return ArenaMap::new(); - }; + let taits_count = ImplTrait::type_alias_impl_traits(db, type_alias).len(); let loc = type_alias.loc(db); let module = loc.module(db);
diff --git a/crates/hir-ty/src/representability.rs b/crates/hir-ty/src/representability.rs index 828af20..9a31443 100644 --- a/crates/hir-ty/src/representability.rs +++ b/crates/hir-ty/src/representability.rs
@@ -23,7 +23,7 @@ }; } -#[salsa::tracked(cycle_result = representability_cycle)] +#[salsa::tracked(cycle_result = representability_cycle, returns(copy))] pub(crate) fn representability(db: &dyn HirDatabase, id: AdtId) -> Representability { match id { AdtId::StructId(id) => variant_representability(db, id.into()),
diff --git a/crates/hir-ty/src/specialization.rs b/crates/hir-ty/src/specialization.rs index 2d206fe..c3543cd 100644 --- a/crates/hir-ty/src/specialization.rs +++ b/crates/hir-ty/src/specialization.rs
@@ -39,7 +39,7 @@ /// `parent_impl_def_id` is a const impl (conditionally based off of some `[const]` /// bounds), then `specializing_impl_def_id` must also be const for the same /// set of types. -#[salsa::tracked(cycle_result = specializes_query_cycle)] +#[salsa::tracked(cycle_result = specializes_query_cycle, returns(copy))] fn specializes_query( db: &dyn HirDatabase, specializing_impl_def_id: ImplId,
diff --git a/crates/hir-ty/src/tests/incremental.rs b/crates/hir-ty/src/tests/incremental.rs index 08efef1..4574e09 100644 --- a/crates/hir-ty/src/tests/incremental.rs +++ b/crates/hir-ty/src/tests/incremental.rs
@@ -47,7 +47,7 @@ "lang_items", "crate_lang_items", "GenericPredicates::query_with_diagnostics_", - "ImplTraits::return_type_impl_traits_", + "fn_sig_for_fn", "ExprScopes::body_expr_scopes_", "body_upvars_mentioned", ] @@ -136,7 +136,7 @@ "lang_items", "crate_lang_items", "GenericPredicates::query_with_diagnostics_", - "ImplTraits::return_type_impl_traits_", + "fn_sig_for_fn", "ExprScopes::body_expr_scopes_", "body_upvars_mentioned", "InferenceResult < 'db >::for_body_", @@ -147,7 +147,7 @@ "Body::with_source_map_", "trait_environment_query", "GenericPredicates::query_with_diagnostics_", - "ImplTraits::return_type_impl_traits_", + "fn_sig_for_fn", "ExprScopes::body_expr_scopes_", "body_upvars_mentioned", "InferenceResult < 'db >::for_body_", @@ -158,7 +158,7 @@ "Body::with_source_map_", "trait_environment_query", "GenericPredicates::query_with_diagnostics_", - "ImplTraits::return_type_impl_traits_", + "fn_sig_for_fn", "ExprScopes::body_expr_scopes_", "body_upvars_mentioned", ] @@ -599,21 +599,20 @@ "crate_lang_items", "GenericPredicates::query_with_diagnostics_", "GenericPredicates::query_with_diagnostics_", - "ImplTraits::return_type_impl_traits_", + "fn_sig_for_fn", "body_upvars_mentioned", "InferenceResult < 'db >::for_body_", "FunctionSignature::of_", "FunctionSignature::with_source_map_", "trait_environment_query", "GenericPredicates::query_with_diagnostics_", - "ImplTraits::return_type_impl_traits_", + "fn_sig_for_fn", "ExprScopes::body_expr_scopes_", "StructSignature::of_", "StructSignature::with_source_map_", "AttrFlags::query_", "GenericPredicates::query_with_diagnostics_", "InherentImpls < 'db >::for_crate_", - "callable_item_signature_with_diagnostics", "TraitImpls < 'db >::for_crate_and_deps_", "TraitImpls < 'db >::for_crate_", "impl_trait_with_diagnostics", @@ -692,18 +691,17 @@ "crate_lang_items", "GenericPredicates::query_with_diagnostics_", "GenericPredicates::query_with_diagnostics_", - "ImplTraits::return_type_impl_traits_", + "fn_sig_for_fn", "body_upvars_mentioned", "InferenceResult < 'db >::for_body_", "FunctionSignature::with_source_map_", "GenericPredicates::query_with_diagnostics_", - "ImplTraits::return_type_impl_traits_", + "fn_sig_for_fn", "ExprScopes::body_expr_scopes_", "StructSignature::with_source_map_", "AttrFlags::query_", "GenericPredicates::query_with_diagnostics_", "InherentImpls < 'db >::for_crate_", - "callable_item_signature_with_diagnostics", "TraitImpls < 'db >::for_crate_", "ImplSignature::with_source_map_", "ImplSignature::of_",
diff --git a/crates/hir-ty/src/tests/never_type.rs b/crates/hir-ty/src/tests/never_type.rs index 9d98218..5cc4156 100644 --- a/crates/hir-ty/src/tests/never_type.rs +++ b/crates/hir-ty/src/tests/never_type.rs
@@ -888,6 +888,17 @@ } #[test] +fn diverging_destructuring_assignment_coerces_rhs() { + check_no_mismatches( + r#" +fn main() { + if (() = return) {} +} +"#, + ); +} + +#[test] fn never_coercion_in_struct_update_syntax() { check_no_mismatches( r#"
diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs index 2836f97..6724243 100644 --- a/crates/hir-ty/src/tests/regression.rs +++ b/crates/hir-ty/src/tests/regression.rs
@@ -3028,3 +3028,97 @@ "#, ); } + +#[test] +fn braced_const_path() { + check_types( + r#" +//- minicore: default, builtin_impls +trait ToNum { + type Num; +} +trait Bar { + type Ty; +} +struct Gen<const B: bool>; +struct Int<const B: bool>; + +impl<const B: bool> ToNum for Gen<{ B }> { + type Num = Int<B>; +} + +impl Bar for Int<true> { + type Ty = i32; +} +impl Bar for Int<false> { + type Ty = f32; +} + +type A = <<Gen<true> as ToNum>::Num as Bar>::Ty; + +fn main() { + let x = A::default(); + // ^ i32 +} + "#, + ); +} + +#[test] +fn regression_22820() { + check_no_mismatches( + r#" +//- minicore: copy +trait MyTrait: Copy { + const ASSOC: usize; +} + +const fn output<T: MyTrait>(_: T) -> usize { + <T as MyTrait>::ASSOC +} + +const fn yeet() -> impl Clone { + let x = [0u8; output(yeet())]; +} + "#, + ); +} + +#[test] +fn rpit_function_with_non_trivial_anon_const() { + check_no_mismatches( + r#" +fn f() -> impl Sized { + let x = [0u8; 1 + 2]; +} + "#, + ); +} + +#[test] +fn regression_22836() { + check( + r#" +fn main() { + match () { + const { + async | v | () + // ^^^^^^^^^^^^^^ expected (), got impl AsyncFn({unknown}) + } + } +} + "#, + ); +} + +#[test] +fn regression_22986() { + check_no_mismatches( + r#" +fn main() { + let _: &[u8; 0] = b"\ + "; +} + "#, + ); +}
diff --git a/crates/hir-ty/src/tests/regression/new_solver.rs b/crates/hir-ty/src/tests/regression/new_solver.rs index 97f83ca..59563bd 100644 --- a/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/crates/hir-ty/src/tests/regression/new_solver.rs
@@ -1,5 +1,5 @@ use expect_test::expect; -use hir_def::ModuleDefId; +use hir_def::{AdtId, ModuleDefId}; use rustc_type_ir::inherent::IntoKind as _; use test_fixture::WithFixture; @@ -424,6 +424,41 @@ } #[test] +fn invalid_string_array_len_is_error() { + let (db, file_id) = TestDB::with_single_file( + r#" +pub union U { + foo: [usize; ""], +} +"#, + ); + + crate::attach_db(&db, || { + let module_id = db.module_for_file(file_id.file_id(&db)); + let def_map = module_id.def_map(&db); + let union_id = def_map[module_id] + .scope + .declarations() + .find_map(|decl| match decl { + ModuleDefId::AdtId(AdtId::UnionId(id)) => Some(id), + _ => None, + }) + .unwrap(); + let field_ty = db + .field_types(union_id.into()) + .iter() + .next() + .unwrap() + .1 + .ty() + .instantiate_identity() + .skip_norm_wip(); + let TyKind::Array(_, len) = field_ty.kind() else { unreachable!() }; + assert!(len.is_error()); + }); +} + +#[test] fn another_20654_case() { check_no_mismatches( r#" @@ -914,3 +949,31 @@ "#]], ); } + +#[test] +fn infer_method_call_recovery_hrtb_does_not_panic() { + check_no_mismatches( + r#" +//- minicore: coerce_unsized, dispatch_from_dyn, fn, option, phantom_data, sized +use core::marker::PhantomData; + +trait Any {} + +impl<T: 'static> Any for T {} + +struct Bar<'a>(PhantomData<&'a ()>); + +type FooFn = for<'a> fn(&'a dyn Any) -> Option<Bar<'a>>; + +struct Foo { + bar: FooFn, +} + +impl Foo { + fn baz<'a, T: 'static>(&self, x: &'a T) -> Option<Bar<'a>> { + self.bar(x) + } +} +"#, + ); +}
diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs index e8f378d..3cdfe4e 100644 --- a/crates/hir-ty/src/tests/simple.rs +++ b/crates/hir-ty/src/tests/simple.rs
@@ -4201,8 +4201,6 @@ 248..282 'LazyLo..._LOCK)': &'? [u32; 0] 264..281 '&VALUE...Y_LOCK': &'? LazyLock<[u32; 0]> 265..281 'VALUES...Y_LOCK': LazyLock<[u32; 0]> - 197..202 '{ 0 }': usize - 199..200 '0': usize "#]], ); } @@ -4308,9 +4306,8 @@ } "#, expect![[r#" - 29..34 '{ 2 }': usize - 31..32 '2': usize - "#]], + +"#]], ); }
diff --git a/crates/hir-ty/src/traits.rs b/crates/hir-ty/src/traits.rs index 108e3e0..c485af5 100644 --- a/crates/hir-ty/src/traits.rs +++ b/crates/hir-ty/src/traits.rs
@@ -83,7 +83,7 @@ ty.replace_infer_with_error(infcx.interner) } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, salsa::Update)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, salsa::SalsaValue)] pub enum FnTrait { // Warning: Order is important. If something implements `x` it should also implement // `y` if `y <= x`.
diff --git a/crates/hir-ty/src/upvars.rs b/crates/hir-ty/src/upvars.rs index 6dcd8b5..5cc7d81 100644 --- a/crates/hir-ty/src/upvars.rs +++ b/crates/hir-ty/src/upvars.rs
@@ -2,9 +2,10 @@ use hir_def::{ DefWithBodyId, ExpressionStoreOwnerId, GenericDefId, VariantId, - expr_store::{ExpressionStore, path::Path}, - hir::{BindingId, Expr, ExprId, ExprOrPatId}, + expr_store::{ExpressionStore, StoreVisitor, StoreVisitorExt, path::Path}, + hir::{BindingId, Expr, ExprId, PatId}, resolver::{HasResolver, Resolver, ValueNs}, + type_ref::TypeRefId, }; use hir_expand::mod_path::PathKind; use rustc_hash::{FxHashMap, FxHashSet}; @@ -111,140 +112,114 @@ ) -> Option<Box<FxHashMap<ExprId, Upvars>>> { let store = ExpressionStore::of(db, owner); store.expr_roots().next()?; - let mut resolver = owner.resolver(db); - let mut result = FxHashMap::default(); - for root_expr in store.expr_roots() { - handle_expr_outside_closure(db, &mut resolver, owner, store, root_expr, &mut result); - } - return if result.is_empty() { + let resolver = owner.resolver(db); + let mut visitor = UpvarsMentionedVisitor { + db, + resolver, + owner, + store, + closures_map: FxHashMap::default(), + current_closure: None, + }; + visitor.on_exprs(store.expr_roots()); + let mut result = visitor.closures_map; + if result.is_empty() { None } else { result.shrink_to_fit(); Some(Box::new(result)) - }; - - fn handle_expr_outside_closure<'db>( - db: &'db dyn HirDatabase, - resolver: &mut Resolver<'db>, - owner: ExpressionStoreOwnerId, - body: &ExpressionStore, - expr: ExprId, - closures_map: &mut FxHashMap<ExprId, Upvars>, - ) { - match &body[expr] { - &Expr::Closure { body: body_expr, .. } => { - let mut upvars = FxHashSet::default(); - handle_expr_inside_closure( - db, - resolver, - owner, - body, - expr, - body_expr, - &mut upvars, - closures_map, - ); - if !upvars.is_empty() { - closures_map.insert(expr, Upvars::new(&upvars)); - } - } - _ => body.walk_child_exprs(expr, |expr| { - handle_expr_outside_closure(db, resolver, owner, body, expr, closures_map) - }), - } - } - - fn handle_expr_inside_closure<'db>( - db: &'db dyn HirDatabase, - resolver: &mut Resolver<'db>, - owner: ExpressionStoreOwnerId, - body: &ExpressionStore, - current_closure: ExprId, - expr: ExprId, - upvars: &mut FxHashSet<BindingId>, - closures_map: &mut FxHashMap<ExprId, Upvars>, - ) { - match &body[expr] { - Expr::Path(path) => { - resolve_maybe_upvar( - db, - resolver, - owner, - body, - current_closure, - expr, - expr.into(), - upvars, - path, - ); - } - &Expr::Closure { body: body_expr, .. } => { - let mut closure_upvars = FxHashSet::default(); - handle_expr_inside_closure( - db, - resolver, - owner, - body, - expr, - body_expr, - &mut closure_upvars, - closures_map, - ); - if !closure_upvars.is_empty() { - closures_map.insert(expr, Upvars::new(&closure_upvars)); - // All nested closure's upvars are also upvars of the parent closure. - upvars.extend( - closure_upvars - .iter() - .copied() - .filter(|local| body.binding_owner(*local) != Some(current_closure)), - ); - } - return; - } - _ => {} - } - body.walk_child_exprs(expr, |expr| { - handle_expr_inside_closure( - db, - resolver, - owner, - body, - current_closure, - expr, - upvars, - closures_map, - ) - }); } } -fn resolve_maybe_upvar<'db>( +struct UpvarsMentionedVisitor<'db> { db: &'db dyn HirDatabase, - resolver: &mut Resolver<'db>, + resolver: Resolver<'db>, owner: ExpressionStoreOwnerId, - body: &ExpressionStore, - current_closure: ExprId, - expr: ExprId, - id: ExprOrPatId, - upvars: &mut FxHashSet<BindingId>, - path: &Path, -) { - if let Path::BarePath(mod_path) = path + store: &'db ExpressionStore, + closures_map: FxHashMap<ExprId, Upvars>, + current_closure: Option<(ExprId, FxHashSet<BindingId>)>, +} + +impl UpvarsMentionedVisitor<'_> { + fn resolve_maybe_upvar(&mut self, expr: ExprId, path: &Path) { + let Some((current_closure, upvars)) = &mut self.current_closure else { return }; + + if let Path::BarePath(mod_path) = path && matches!(mod_path.kind, PathKind::Plain | PathKind::SELF) // `self` is length zero. && mod_path.segments().len() <= 1 - { - // Could be a variable. - let guard = resolver.update_to_inner_scope(db, owner, expr); - let resolution = - resolver.resolve_path_in_value_ns_fully(db, path, body.expr_or_pat_path_hygiene(id)); - if let Some(ValueNs::LocalBinding(local)) = resolution - && body.binding_owner(local) != Some(current_closure) { - upvars.insert(local); + // Could be a variable. + let guard = self.resolver.update_to_inner_scope(self.db, self.owner, expr); + let resolution = self.resolver.resolve_path_in_value_ns_fully( + self.db, + path, + self.store.expr_or_pat_path_hygiene(expr.into()), + ); + if let Some(ValueNs::LocalBinding(local)) = resolution + && self.store.binding_owner(local) != Some(*current_closure) + { + upvars.insert(local); + } + self.resolver.reset_to_guard(guard); } - resolver.reset_to_guard(guard); + } +} + +impl StoreVisitor for UpvarsMentionedVisitor<'_> { + fn on_expr(&mut self, expr: ExprId) { + match &self.store[expr] { + Expr::Path(path) => { + self.resolve_maybe_upvar(expr, path); + self.on_path(path); + } + Expr::Closure { + body: body_expr, + args, + arg_types, + ret_type, + capture_by: _, + closure_kind: _, + } => { + self.on_pats(args); + arg_types.iter().for_each(|arg_ty| self.on_type_opt(*arg_ty)); + self.on_type_opt(*ret_type); + + let mut old_current_closure = + self.current_closure.replace((expr, FxHashSet::default())); + self.on_expr(*body_expr); + let closure_upvars = self.current_closure.take().unwrap().1; + if !closure_upvars.is_empty() { + self.closures_map.insert(expr, Upvars::new(&closure_upvars)); + // All nested closure's upvars are also upvars of the parent closure. + if let Some((current_closure, upvars)) = &mut old_current_closure { + upvars.extend(closure_upvars.iter().copied().filter(|local| { + self.store.binding_owner(*local) != Some(*current_closure) + })); + } + } + self.current_closure = old_current_closure; + } + _ => self.store.visit_expr_children(expr, self), + } + } + + fn on_anon_const_expr(&mut self, expr: ExprId) { + // Anon consts don't contribute upvars. + let old_current_closure = self.current_closure.take(); + self.on_expr(expr); + self.current_closure = old_current_closure; + } + + fn on_pat(&mut self, pat: PatId) { + self.store.visit_pat_children(pat, self); + } + + fn on_type(&mut self, ty: TypeRefId) { + // Anon consts don't contribute upvars. + let old_current_closure = self.current_closure.take(); + self.store.visit_type_ref_children(ty, &mut *self); + self.current_closure = old_current_closure; } } @@ -394,4 +369,21 @@ expect!["56..65: self"], ); } + + #[test] + fn const_block() { + check( + r#" +fn main() { + || { + const { + let v = (); + v + } + }; +} + "#, + expect![""], + ); + } }
diff --git a/crates/hir/src/attrs.rs b/crates/hir/src/attrs.rs index 9a61885..210d6bd 100644 --- a/crates/hir/src/attrs.rs +++ b/crates/hir/src/attrs.rs
@@ -100,6 +100,8 @@ } } + /// **Do not** use this to detect visibility of the macro, this does not account for macros 2.0. + /// Use this only if you're interested in `#[macro_export]` literally (for example, because it exports under the crate root). #[inline] pub fn is_macro_export(&self) -> bool { self.attrs.contains(AttrFlags::IS_MACRO_EXPORT)
diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 0c4191c..f6df18a 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs
@@ -32,7 +32,7 @@ }; use triomphe::Arc; -use crate::{AssocItem, Field, Function, GenericDef, Local, Trait, Type, TypeOwnerId, Variant}; +use crate::{AssocItem, Field, Function, GenericDef, Trait, Type, TypeOwnerId, Variant}; pub use hir_def::VariantId; pub use hir_ty::{ @@ -134,10 +134,8 @@ MissingFields, MissingMatchArms, MissingUnsafe, - MovedOutOfRef<'db>, MutRefInImmRefPat, MutableRefBinding, - NeedMut<'db>, NonExhaustiveLet, NonExhaustiveRecordExpr, NonExhaustiveRecordPat, @@ -168,8 +166,6 @@ UnresolvedMethodCall<'db>, UnresolvedModule, UnresolvedIdent, - UnusedMut<'db>, - UnusedVariable<'db>, GenericArgsProhibited, ParenthesizedGenericArgsWithoutFnTrait, BadRtn, @@ -476,28 +472,6 @@ pub actual: Type<'db>, } -#[derive(Debug)] -pub struct NeedMut<'db> { - pub local: Local<'db>, - pub span: InFile<SyntaxNodePtr>, -} - -#[derive(Debug)] -pub struct UnusedMut<'db> { - pub local: Local<'db>, -} - -#[derive(Debug)] -pub struct UnusedVariable<'db> { - pub local: Local<'db>, -} - -#[derive(Debug)] -pub struct MovedOutOfRef<'db> { - pub ty: Type<'db>, - pub span: InFile<SyntaxNodePtr>, -} - #[derive(Debug, PartialEq, Eq)] pub struct IncoherentImpl { pub file_id: HirFileId,
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 70e2d3f..474f4bb 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs
@@ -2098,121 +2098,6 @@ } } - if let Ok(borrowck_results) = InferBodyId::from(id).borrowck(db) { - for borrowck_result in borrowck_results { - let mir_body = borrowck_result.mir_body(db); - for moof in &borrowck_result.moved_out_of_ref { - let span: InFile<SyntaxNodePtr> = match moof.span { - mir::MirSpan::ExprId(e) => match source_map.expr_syntax(e) { - Ok(s) => s.map(|it| it.into()), - Err(_) => continue, - }, - mir::MirSpan::PatId(p) => match source_map.pat_syntax(p) { - Ok(s) => s.map(|it| it.into()), - Err(_) => continue, - }, - mir::MirSpan::SelfParam => match source_map.self_param_syntax() { - Some(s) => s.map(|it| it.into()), - None => continue, - }, - mir::MirSpan::BindingId(b) => { - match source_map - .patterns_for_binding(b) - .iter() - .find_map(|p| source_map.pat_syntax(*p).ok()) - { - Some(s) => s.map(|it| it.into()), - None => continue, - } - } - mir::MirSpan::Unknown => continue, - }; - acc.push( - MovedOutOfRef { - ty: Type { owner: type_owner, ty: EarlyBinder::bind(moof.ty.as_ref()) }, - span, - } - .into(), - ) - } - let mol = &borrowck_result.mutability_of_locals; - for (binding_id, binding_data) in body.bindings() { - if binding_data.problems.is_some() { - // We should report specific diagnostics for these problems, not `need-mut` and `unused-mut`. - continue; - } - let Some(&local) = mir_body.binding_locals.get(binding_id) else { - continue; - }; - if source_map - .patterns_for_binding(binding_id) - .iter() - .any(|&pat| source_map.pat_syntax(pat).is_err()) - { - // Skip synthetic bindings - continue; - } - let mut need_mut = &mol[local]; - if body[binding_id].name == sym::self_ - && need_mut == &mir::MutabilityReason::Unused - { - need_mut = &mir::MutabilityReason::Not; - } - let local = - Local { parent: id.into(), parent_infer: mir_body.owner, binding_id }; - let is_mut = body[binding_id].mode == BindingAnnotation::Mutable; - - match (need_mut, is_mut) { - (mir::MutabilityReason::Unused, _) => { - let should_ignore = body[binding_id].name.as_str().starts_with('_'); - if !should_ignore { - acc.push(UnusedVariable { local }.into()) - } - } - (mir::MutabilityReason::Mut { .. }, true) - | (mir::MutabilityReason::Not, false) => (), - (mir::MutabilityReason::Mut { spans }, false) => { - for span in spans { - let span: InFile<SyntaxNodePtr> = match span { - mir::MirSpan::ExprId(e) => match source_map.expr_syntax(*e) { - Ok(s) => s.map(|it| it.into()), - Err(_) => continue, - }, - mir::MirSpan::PatId(p) => match source_map.pat_syntax(*p) { - Ok(s) => s.map(|it| it.into()), - Err(_) => continue, - }, - mir::MirSpan::BindingId(b) => { - match source_map - .patterns_for_binding(*b) - .iter() - .find_map(|p| source_map.pat_syntax(*p).ok()) - { - Some(s) => s.map(|it| it.into()), - None => continue, - } - } - mir::MirSpan::SelfParam => match source_map.self_param_syntax() - { - Some(s) => s.map(|it| it.into()), - None => continue, - }, - mir::MirSpan::Unknown => continue, - }; - acc.push(NeedMut { local, span }.into()); - } - } - (mir::MutabilityReason::Not, true) => { - let should_ignore = body[binding_id].name.as_str().starts_with('_'); - if !should_ignore { - acc.push(UnusedMut { local }.into()) - } - } - } - } - } - } - for diagnostic in BodyValidationDiagnostic::collect(db, id, style_lints) { acc.extend(AnyDiagnostic::body_validation_diagnostic(db, diagnostic, source_map)); } @@ -3310,10 +3195,6 @@ } } - pub fn is_macro_export(self, db: &dyn HirDatabase) -> bool { - matches!(self.id, MacroId::MacroRulesId(_) if AttrFlags::query(db, self.id.into()).contains(AttrFlags::IS_MACRO_EXPORT)) - } - pub fn is_proc_macro(self) -> bool { matches!(self.id, MacroId::ProcMacroId(_)) } @@ -3454,7 +3335,13 @@ let source = loc.source(db); visibility_from_ast(db, id, source.map(|src| src.visibility())) } - MacroId::MacroRulesId(_) => Visibility::Public, + MacroId::MacroRulesId(id) => { + if AttrFlags::query(db, id.into()).contains(AttrFlags::IS_MACRO_EXPORT) { + Visibility::Public + } else { + Visibility::PubCrate(self.krate(db).id) + } + } MacroId::ProcMacroId(_) => Visibility::Public, } }
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index e805676..2090916 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs
@@ -1320,7 +1320,7 @@ .first_segment() .and_then(|it| it.name_ref()) .and_then(|name_ref| { - ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text()) + ToolModule::by_name(db, self.resolver.krate().into(), name_ref.text()) .map(PathResolution::ToolModule) }) .map(|it| (it, None)), @@ -1361,7 +1361,7 @@ // in this case we have to check for inert/builtin attributes and tools and prioritize // resolution of attributes over other namespaces if let Some(name_ref) = path.as_single_name_ref() { - let builtin = BuiltinAttr::builtin(&name_ref.text()); + let builtin = BuiltinAttr::builtin(name_ref.text()); if builtin.is_some() { return builtin.map(|it| (PathResolution::BuiltinAttr(it), None)); } @@ -1411,7 +1411,7 @@ .first_segment() .and_then(|it| it.name_ref()) .and_then(|name_ref| { - ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text()) + ToolModule::by_name(db, self.resolver.krate().into(), name_ref.text()) .map(PathResolution::ToolModule) }) .map(|it| (it, None)),
diff --git a/crates/hir/src/symbols.rs b/crates/hir/src/symbols.rs index 79a075e..b458dc0 100644 --- a/crates/hir/src/symbols.rs +++ b/crates/hir/src/symbols.rs
@@ -2,7 +2,7 @@ use std::marker::PhantomData; -use base_db::{FxIndexSet, salsa::Update}; +use base_db::{FxIndexSet, salsa::SalsaValue}; use either::Either; use hir_def::{ AdtId, AssocItemId, AstIdLoc, Complete, DefWithBodyId, ExternCrateId, HasModule, ImplId, @@ -28,7 +28,7 @@ /// The actual data that is stored in the index. It should be as compact as /// possible. -#[derive(Clone, PartialEq, Eq, Hash, Update)] +#[derive(Clone, PartialEq, Eq, Hash, SalsaValue)] pub struct FileSymbol<'db> { pub name: Symbol, pub def: ModuleDef,
diff --git a/crates/hir/src/term_search.rs b/crates/hir/src/term_search.rs index 1cc6766..0a5d95d 100644 --- a/crates/hir/src/term_search.rs +++ b/crates/hir/src/term_search.rs
@@ -228,8 +228,6 @@ /// Configuration options for the term search #[derive(Debug, Clone, Copy)] pub struct TermSearchConfig { - /// Enable borrow checking, this guarantees the outputs of the `term_search` to borrow-check - pub enable_borrowcheck: bool, /// Indicate when to squash multiple trees to `Many` as there are too many to keep track pub many_alternatives_threshold: usize, /// Fuel for term search in "units of work" @@ -238,7 +236,7 @@ impl Default for TermSearchConfig { fn default() -> Self { - Self { enable_borrowcheck: true, many_alternatives_threshold: 1, fuel: 1200 } + Self { many_alternatives_threshold: 1, fuel: 1200 } } }
diff --git a/crates/hir/src/term_search/tactics.rs b/crates/hir/src/term_search/tactics.rs index c0cfe18..b89921d 100644 --- a/crates/hir/src/term_search/tactics.rs +++ b/crates/hir/src/term_search/tactics.rs
@@ -10,7 +10,7 @@ use std::iter; -use hir_ty::{db::HirDatabase, mir::BorrowKind}; +use hir_ty::db::HirDatabase; use itertools::Itertools; use rustc_hash::FxHashSet; @@ -46,42 +46,17 @@ ScopeDef::ModuleDef(ModuleDef::Const(it)) => Some(Expr::Const(*it)), ScopeDef::ModuleDef(ModuleDef::Static(it)) => Some(Expr::Static(*it)), ScopeDef::GenericParam(GenericParam::ConstParam(it)) => Some(Expr::ConstParam(*it)), - ScopeDef::Local(it) => { - if ctx.config.enable_borrowcheck { - let borrowck = it.parent_infer.borrowck(db).ok()?; - - let invalid = borrowck.iter().any(|b| { - let mir_body = b.mir_body(ctx.sema.db); - b.partially_moved.iter().any(|moved| { - Some(&moved.local) == mir_body.binding_locals.get(it.binding_id) - }) || b.borrow_regions.iter().any(|region| { - // Shared borrows are fine - Some(®ion.local) == mir_body.binding_locals.get(it.binding_id) - && region.kind != BorrowKind::Shared - }) - }); - - if invalid { - return None; - } - } - - Some(Expr::Local(*it)) - } + ScopeDef::Local(it) => Some(Expr::Local(*it)), _ => None, }?; let ty = expr.ty(db); - lookup.insert(ty.clone(), std::iter::once(expr.clone())); - - // Don't suggest local references as they are not valid for return - if matches!(expr, Expr::Local(_)) - && ty.contains_reference(db) - && ctx.config.enable_borrowcheck - { + if ty.contains_unknown() { return None; } + lookup.insert(ty.clone(), std::iter::once(expr.clone())); + ty.instantiate_with_errors().could_unify_with_deeply(db, &ctx.goal).then_some(expr) }) } @@ -182,10 +157,6 @@ return None; } - // Ignore types that have something to do with lifetimes - if ctx.config.enable_borrowcheck && ty.contains_reference(db) { - return None; - } let fields = strukt.fields(db); // Check if all fields are visible, otherwise we cannot fill them if fields.iter().any(|it| !it.is_visible_from(db, module)) { @@ -234,11 +205,6 @@ return None; } - // Ignore types that have something to do with lifetimes - if ctx.config.enable_borrowcheck && ty.contains_reference(db) { - return None; - } - let generics: Vec<_> = ty.type_arguments().collect(); let exprs = enum_ .variants(db) @@ -372,7 +338,6 @@ crate::Crate::from(ctx.scope.resolver().krate()).edition(db), ) || it.is_unstable(db) - || ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr() { return None; @@ -495,11 +460,6 @@ } let ret_ty = it.ret_type(db).instantiate(ty.type_arguments()); - // Filter out functions that return references - if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr() - { - return None; - } // Ignore functions that do not change the type if ty.instantiate_with_errors().could_unify_with_deeply(db, &ret_ty) { @@ -695,11 +655,6 @@ } let ret_ty = it.ret_type(db).instantiate(ty.type_arguments()); - // Filter out functions that return references - if ctx.config.enable_borrowcheck && ret_ty.contains_reference(db) || ret_ty.is_raw_ptr() - { - return None; - } // Early exit if some param cannot be filled from lookup let param_exprs: Vec<Vec<Expr<'_>>> = it @@ -763,11 +718,6 @@ return None; } - // Ignore types that have something to do with lifetimes - if ctx.config.enable_borrowcheck && ty.contains_reference(db) { - return None; - } - // Early exit if some param cannot be filled from lookup let param_exprs: Vec<Vec<Expr<'db>>> = ty.type_arguments().map(|field| lookup.find(db, &field)).collect::<Option<_>>()?;
diff --git a/crates/ide-assists/src/assist_config.rs b/crates/ide-assists/src/assist_config.rs index 636acf3..c7d69d8 100644 --- a/crates/ide-assists/src/assist_config.rs +++ b/crates/ide-assists/src/assist_config.rs
@@ -24,7 +24,6 @@ pub prefer_absolute: bool, pub assist_emit_must_use: bool, pub term_search_fuel: u64, - pub term_search_borrowck: bool, pub code_action_grouping: bool, pub expr_fill_default: ExprFillDefaultMode, pub prefer_self_ty: bool,
diff --git a/crates/ide-assists/src/handlers/apply_demorgan.rs b/crates/ide-assists/src/handlers/apply_demorgan.rs index 1026244..e2c1048 100644 --- a/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/crates/ide-assists/src/handlers/apply_demorgan.rs
@@ -206,7 +206,7 @@ let closure_body = closure_expr.body()?; let op_range = method_call.syntax().text_range(); - let label = format!("Apply De Morgan's law to `Iterator::{}`", name.text().as_str()); + let label = format!("Apply De Morgan's law to `Iterator::{}`", name.text()); acc.add_group( &GroupLabel("Apply De Morgan's law".to_owned()), AssistId::refactor_rewrite("apply_demorgan_iterator"), @@ -216,7 +216,7 @@ let editor = builder.make_editor(method_call.syntax()); let make = editor.make(); // replace the method name - let new_name = match name.text().as_str() { + let new_name = match name.text() { "all" => make.name_ref("any"), "any" => make.name_ref("all"), "is_some_and" => make.name_ref("is_none_or"),
diff --git a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs index c9f5e0a..83effa1 100644 --- a/crates/ide-assists/src/handlers/convert_closure_to_fn.rs +++ b/crates/ide-assists/src/handlers/convert_closure_to_fn.rs
@@ -507,7 +507,7 @@ capture_kind: CaptureKind, is_ref: bool, ) -> ast::Expr { - let capture_name = make.expr_path(make.path_from_text(&capture_name.text())); + let capture_name = make.expr_path(make.path_from_text(capture_name.text())); if capture_kind == CaptureKind::Move || is_ref { return capture_name; }
diff --git a/crates/ide-assists/src/handlers/convert_into_to_from.rs b/crates/ide-assists/src/handlers/convert_into_to_from.rs index a01a66e..c8de14b 100644 --- a/crates/ide-assists/src/handlers/convert_into_to_from.rs +++ b/crates/ide-assists/src/handlers/convert_into_to_from.rs
@@ -109,7 +109,7 @@ editor.replace(into_fn_name.syntax(), make.name("from").syntax()); for s in selfs { - match s.text().as_ref() { + match s.text() { "self" => editor.replace(s.syntax(), make.name_ref("val").syntax()), "Self" => { if let Some(path_segment) =
diff --git a/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/crates/ide-assists/src/handlers/convert_match_to_let_else.rs index 9dffdf3..db084c6 100644 --- a/crates/ide-assists/src/handlers/convert_match_to_let_else.rs +++ b/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
@@ -148,8 +148,7 @@ if let Some(name_ref) = record_pat_field.field_name() { editor.replace( record_pat_field.syntax(), - make.record_pat_field(make.name_ref(&name_ref.text()), binding.clone()) - .syntax(), + make.record_pat_field(make.name_ref(name_ref.text()), binding.clone()).syntax(), ); } } else {
diff --git a/crates/ide-assists/src/handlers/convert_range_for_to_while.rs b/crates/ide-assists/src/handlers/convert_range_for_to_while.rs index 7026b5b..ae8f626 100644 --- a/crates/ide-assists/src/handlers/convert_range_for_to_while.rs +++ b/crates/ide-assists/src/handlers/convert_range_for_to_while.rs
@@ -74,7 +74,7 @@ let mut elements = vec![]; - let var_expr = make.expr_path(make.ident_path(&name.text())); + let var_expr = make.expr_path(make.ident_path(name.text())); let op = ast::BinaryOp::CmpOp(ast::CmpOp::Ord { ordering: ast::Ordering::Less, strict: !inclusive,
diff --git a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs index eb74e91..0bb9bf1 100644 --- a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs
@@ -194,7 +194,7 @@ let range = ctx.sema.original_range_opt(pat.syntax())?.range; let place = cover_edit_range(source.syntax(), range); let elements = vec![ - make.name_ref(&name.text()).syntax().clone().into(), + make.name_ref(name.text()).syntax().clone().into(), make.token(T![:]).into(), make.whitespace(" ").into(), ]; @@ -237,7 +237,7 @@ let range = ctx.sema.original_range_opt(expr.syntax())?.range; let place = cover_edit_range(source.syntax(), range); let elements = vec![ - make.name_ref(&name.text()).syntax().clone().into(), + make.name_ref(name.text()).syntax().clone().into(), make.token(T![:]).into(), make.whitespace(" ").into(), ];
diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index c2eb49d..46333ed 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs
@@ -934,7 +934,7 @@ }; // FIXME: make trait arguments - let trait_name = trait_name.map(|name| make.ty_path(make.ident_path(&name.text())).into()); + let trait_name = trait_name.map(|name| make.ty_path(make.ident_path(name.text())).into()); let parent = self.parent()?; let parents = generic_parents(&parent); @@ -1561,7 +1561,7 @@ old_indent: IndentLevel, make: &SyntaxFactory, ) -> ast::Fn { - let fun_name = make.name(&fun.name.text()); + let fun_name = make.name(fun.name.text()); let params = fun.make_param_list(make, ctx, module, fun.mods.edition); let ret_ty = fun.make_ret_ty(make, ctx, module); let body = make_body(make, ctx, old_indent, fun);
diff --git a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 5e6e74b..c1ac4f1 100644 --- a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs
@@ -241,7 +241,7 @@ if matches!(token.kind(), T![lifetime_ident]) => { if let Some(lt) = lt.lifetime() - && lt.text().as_str() == token.text() + && lt.text() == token.text() { *tag = true; tagged_one = true; @@ -250,18 +250,15 @@ } param if matches!(token.kind(), T![ident]) => { if match param { - ast::GenericParam::ConstParam(konst) => konst - .name() - .map(|name| name.text().as_str() == token.text()) - .unwrap_or_default(), - ast::GenericParam::TypeParam(ty) => ty - .name() - .map(|name| name.text().as_str() == token.text()) - .unwrap_or_default(), - ast::GenericParam::LifetimeParam(lt) => lt - .lifetime() - .map(|lt| lt.text().as_str() == token.text()) - .unwrap_or_default(), + ast::GenericParam::ConstParam(konst) => { + konst.name().map(|name| name.text() == token.text()).unwrap_or_default() + } + ast::GenericParam::TypeParam(ty) => { + ty.name().map(|name| name.text() == token.text()).unwrap_or_default() + } + ast::GenericParam::LifetimeParam(lt) => { + lt.lifetime().map(|lt| lt.text() == token.text()).unwrap_or_default() + } } { *tag = true; tagged_one = true; @@ -338,7 +335,7 @@ // FIXME: replace with a `ast::make` constructor let ty = match generic_args { Some(generic_args) => make.ty(&format!("{name}{generic_args}")), - None => make.ty(&name.text()), + None => make.ty(name.text()), }; // change from a record to a tuple field list
diff --git a/crates/ide-assists/src/handlers/extract_type_alias.rs b/crates/ide-assists/src/handlers/extract_type_alias.rs index 329f832..a378256 100644 --- a/crates/ide-assists/src/handlers/extract_type_alias.rs +++ b/crates/ide-assists/src/handlers/extract_type_alias.rs
@@ -145,7 +145,7 @@ .filter_map(|it| match it { ast::GenericArg::LifetimeArg(lt) => { let lt = lt.lifetime()?; - known_generics.iter().find(find_lifetime(<.text())) + known_generics.iter().find(find_lifetime(lt.text())) } _ => None, }), @@ -157,7 +157,7 @@ generics.extend( it.bounds() .filter_map(|it| it.lifetime()) - .filter_map(|lt| known_generics.iter().find(find_lifetime(<.text()))), + .filter_map(|lt| known_generics.iter().find(find_lifetime(lt.text()))), ); } } @@ -166,13 +166,12 @@ generics.extend( it.bounds() .filter_map(|it| it.lifetime()) - .filter_map(|lt| known_generics.iter().find(find_lifetime(<.text()))), + .filter_map(|lt| known_generics.iter().find(find_lifetime(lt.text()))), ); } } ast::Type::RefType(ref_) => generics.extend( - ref_.lifetime() - .and_then(|lt| known_generics.iter().find(find_lifetime(<.text()))), + ref_.lifetime().and_then(|lt| known_generics.iter().find(find_lifetime(lt.text()))), ), ast::Type::ArrayType(ar) => { if let Some(ast::Expr::PathExpr(p)) = ar.const_arg().and_then(|x| x.expr())
diff --git a/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs b/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs index acd98ae..738f461 100644 --- a/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs +++ b/crates/ide-assists/src/handlers/generate_blanket_trait_impl.rs
@@ -75,12 +75,12 @@ |builder| { let editor = builder.make_editor(traitd.syntax()); let make = editor.make(); - let namety = make.ty_path(make.path_from_text(&name.text())); + let namety = make.ty_path(make.path_from_text(name.text())); let trait_where_clause = traitd.where_clause().map(|it| it.reset_indent()); let bounds = traitd.type_bound_list().and_then(|list| exclude_sized(make, list)); let is_unsafe = traitd.unsafe_token().is_some(); let thisname = this_name(make, &traitd); - let thisty = make.ty_path(make.path_from_text(&thisname.text())); + let thisty = make.ty_path(make.path_from_text(thisname.text())); let indent = traitd.indent_level(); let gendecl = make.generic_param_list([GenericParam::TypeParam(make.type_param(
diff --git a/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs b/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs index 713d6a3..07c191b 100644 --- a/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs +++ b/crates/ide-assists/src/handlers/generate_default_from_enum_variant.rs
@@ -76,7 +76,7 @@ fn default_impl(variant_name: ast::Name, adt: &ast::Adt, make: &SyntaxFactory) -> ast::Impl { let impl_ = utils::generate_trait_impl_intransitive(make, adt, make.ty("Default")); - let fn_ = default_fn(&variant_name.text(), make); + let fn_ = default_fn(variant_name.text(), make); let (impl_editor, impl_) = SyntaxEditor::with_ast_node(&impl_); impl_
diff --git a/crates/ide-assists/src/handlers/generate_enum_is_method.rs b/crates/ide-assists/src/handlers/generate_enum_is_method.rs index 53e77b4..5e2ee77 100644 --- a/crates/ide-assists/src/handlers/generate_enum_is_method.rs +++ b/crates/ide-assists/src/handlers/generate_enum_is_method.rs
@@ -141,7 +141,7 @@ }; let variant_name = variant.name()?; - let fn_name = format!("is_{}", to_lower_snake_case(&variant_name.text())); + let fn_name = format!("is_{}", to_lower_snake_case(variant_name.text())); Some(Method { pattern_suffix, fn_name, variant_name }) } }
diff --git a/crates/ide-assists/src/handlers/generate_enum_projection_method.rs b/crates/ide-assists/src/handlers/generate_enum_projection_method.rs index 8a194ae..479143c 100644 --- a/crates/ide-assists/src/handlers/generate_enum_projection_method.rs +++ b/crates/ide-assists/src/handlers/generate_enum_projection_method.rs
@@ -219,7 +219,7 @@ fn new(variant: &ast::Variant, fn_name_prefix: &str) -> Option<Self> { use itertools::Itertools as _; let variant_name = variant.name()?; - let fn_name = format!("{fn_name_prefix}_{}", to_lower_snake_case(&variant_name.text())); + let fn_name = format!("{fn_name_prefix}_{}", to_lower_snake_case(variant_name.text())); match variant.kind() { ast::StructKind::Record(record) => {
diff --git a/crates/ide-assists/src/handlers/generate_enum_variant.rs b/crates/ide-assists/src/handlers/generate_enum_variant.rs index fb43e3e..73837f4 100644 --- a/crates/ide-assists/src/handlers/generate_enum_variant.rs +++ b/crates/ide-assists/src/handlers/generate_enum_variant.rs
@@ -62,7 +62,7 @@ let editor = builder.make_editor(enum_node.syntax()); let make = editor.make(); let field_list = parent.make_field_list(ctx, make); - let variant = make.variant(None, make.name(&name_ref.text()), field_list, None); + let variant = make.variant(None, make.name(name_ref.text()), field_list, None); if let Some(it) = enum_node.variant_list() { it.add_variant(&editor, &variant); }
diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index 3c3fde8..13096c6 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs
@@ -67,7 +67,7 @@ return None; } - let fn_name = &*name_ref.text(); + let fn_name = name_ref.text(); let TargetInfo { target_module, adt_info, target, file } = fn_target_info(ctx, path, &call, fn_name)?; @@ -159,7 +159,7 @@ let (impl_, file) = if let Some(impl_) = cursor_impl { (Some(impl_), ctx.vfs_file_id()) } else { - get_adt_source(ctx, &adt, fn_name.text().as_str())? + get_adt_source(ctx, &adt, fn_name.text())? }; let target = get_method_target(ctx, &impl_, &adt)?; @@ -269,7 +269,7 @@ // If generated function has the name "new" and is an associated function, we generate fn body // as a constructor and assume a "Self" return type. if let Some(body) = - make_fn_body_as_new_function(make, ctx, &fn_name.text(), adt_info, target_edition) + make_fn_body_as_new_function(make, ctx, fn_name.text(), adt_info, target_edition) { ret_type = Some(make.ret_type(make.ty_path(make.ident_path("Self")).into())); should_focus_return_type = false;
diff --git a/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs index 7e5d5ce..b21e608 100644 --- a/crates/ide-assists/src/handlers/generate_getter_or_setter.rs +++ b/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
@@ -225,7 +225,7 @@ ( make.ty_ref(record_field_info.field_ty.clone(), true), make.expr_ref( - make.expr_field(self_expr, &record_field_info.field_name.text()).into(), + make.expr_field(self_expr, record_field_info.field_name.text()).into(), true, ), ) @@ -250,7 +250,7 @@ make.expr_ref( make.expr_field( make.expr_path(make.ident_path("self")), - &record_field_info.field_name.text(), + record_field_info.field_name.text(), ) .into(), false,
diff --git a/crates/ide-assists/src/handlers/generate_impl.rs b/crates/ide-assists/src/handlers/generate_impl.rs index ab0eb56..ecff626 100644 --- a/crates/ide-assists/src/handlers/generate_impl.rs +++ b/crates/ide-assists/src/handlers/generate_impl.rs
@@ -185,7 +185,7 @@ None, None, false, - make.ty(&name.text()), + make.ty(name.text()), make.ty_placeholder(), None, None,
diff --git a/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs index fd095dd..6858b62 100644 --- a/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs +++ b/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs
@@ -210,7 +210,7 @@ } fn process_method_name(name: ast::Name) -> Option<(ast::Name, &'static str)> { - let new_name = match &*name.text() { + let new_name = match name.text() { "index" => "index_mut", "as_ref" => "as_mut", "borrow" => "borrow_mut",
diff --git a/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs index d5629e2..242712f 100644 --- a/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs +++ b/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs
@@ -4,14 +4,11 @@ RootDatabase, famous_defs::FamousDefs, helpers::mod_path_to_ast_with_factory, imports::import_assets::item_for_path_search, }; -use syntax::syntax_editor::{Position, SyntaxEditor}; -use syntax::{ - TokenText, - ast::{ - self, AstNode, HasAttrs, HasGenericParams, HasName, edit::AstNodeEdit, - syntax_factory::SyntaxFactory, - }, +use syntax::ast::{ + self, AstNode, HasAttrs, HasGenericParams, HasName, edit::AstNodeEdit, + syntax_factory::SyntaxFactory, }; +use syntax::syntax_editor::{Position, SyntaxEditor}; use crate::{ AssistId, @@ -71,8 +68,7 @@ return None; } - let main_field_name = - names.as_ref().map_or(TokenText::borrowed("value"), |names| names[main_field_i].text()); + let main_field_name = names.as_ref().map_or("value", |names| names[main_field_i].text()); let main_field_ty = types[main_field_i].clone(); acc.add( @@ -92,10 +88,10 @@ false, )); - let ty = make.ty(&strukt_name.text()); + let ty = make.ty(strukt_name.text()); let constructor = - make_adt_constructor(names.as_deref(), constructors, &main_field_name, make); + make_adt_constructor(names.as_deref(), constructors, main_field_name, make); let body = make.block_expr([], Some(constructor)); let fn_ = make @@ -108,7 +104,7 @@ make.param_list( None, [make.param( - make.path_pat(make.path_from_text(&main_field_name)), + make.path_pat(make.path_from_text(main_field_name)), main_field_ty, )], ), @@ -161,12 +157,12 @@ fn make_adt_constructor( names: Option<&[ast::Name]>, constructors: Vec<Option<ast::Expr>>, - main_field_name: &TokenText<'_>, + main_field_name: &str, make: &SyntaxFactory, ) -> ast::Expr { if let Some(names) = names { let fields = make.record_expr_field_list(names.iter().zip(constructors).map( - |(name, initializer)| make.record_expr_field(make.name_ref(&name.text()), initializer), + |(name, initializer)| make.record_expr_field(make.name_ref(name.text()), initializer), )); make.record_expr(make.path_from_text("Self"), fields).into() } else {
diff --git a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs index 12afd9a..354447c 100644 --- a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs +++ b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs
@@ -117,7 +117,7 @@ let params = used_params(&impl_ast, make, ctx); let trait_ast = make.trait_( false, - &trait_name(&impl_assoc_items, make).text(), + trait_name(&impl_assoc_items, make).text(), params.clone(), impl_ast.where_clause(), trait_items, @@ -204,7 +204,7 @@ fn_names .next() .and_then(|name| { - fn_names.next().is_none().then(|| make.name(&stdx::to_camel_case(&name.text()))) + fn_names.next().is_none().then(|| make.name(&stdx::to_camel_case(name.text()))) }) .unwrap_or_else(|| make.name("NewTrait")) }
diff --git a/crates/ide-assists/src/handlers/inline_type_alias.rs b/crates/ide-assists/src/handlers/inline_type_alias.rs index bb76e27..f5d5400 100644 --- a/crates/ide-assists/src/handlers/inline_type_alias.rs +++ b/crates/ide-assists/src/handlers/inline_type_alias.rs
@@ -140,7 +140,7 @@ let src = adt.source(ctx.db())?.value; let name = src.name()?; let generic_params = src.generic_param_list(); - let name_ref = make.name_ref(&name.text()); + let name_ref = make.name_ref(name.text()); let segment = match generic_params { Some(params) => { make.path_segment_generics(name_ref, params.to_generic_args(&make))
diff --git a/crates/ide-assists/src/handlers/merge_match_arms.rs b/crates/ide-assists/src/handlers/merge_match_arms.rs index f417691..5060886 100644 --- a/crates/ide-assists/src/handlers/merge_match_arms.rs +++ b/crates/ide-assists/src/handlers/merge_match_arms.rs
@@ -165,7 +165,7 @@ { let pat_type = ctx.sema.type_of_binding_in_pat(ident_pat); - map.insert(name.text().to_string(), pat_type); + map.insert(name.text().to_owned(), pat_type); } } _ => (),
diff --git a/crates/ide-assists/src/handlers/remove_unused_imports.rs b/crates/ide-assists/src/handlers/remove_unused_imports.rs index d6582a9..aeffb8a 100644 --- a/crates/ide-assists/src/handlers/remove_unused_imports.rs +++ b/crates/ide-assists/src/handlers/remove_unused_imports.rs
@@ -8,7 +8,7 @@ use ide_db::{ FxHashMap, RootDatabase, defs::Definition, - search::{FileReference, ReferenceCategory, SearchScope}, + search::{ReferenceCategory, SearchScope}, }; use syntax::{ AstNode, @@ -139,7 +139,7 @@ fn is_path_per_ns_unused_in_scope( ctx: &AssistContext<'_, '_>, u: &ast::UseTree, - scope: &mut Vec<SearchScope>, + scope: &[SearchScope], path: &PathResolutionPerNs<'_>, ) -> bool { if let Some(PathResolution::Def(ModuleDef::Trait(ref t))) = path.type_ns { @@ -158,7 +158,7 @@ fn is_path_unused_in_scope( ctx: &AssistContext<'_, '_>, u: &ast::UseTree, - scope: &mut Vec<SearchScope>, + scope: &[SearchScope], path: &[Option<PathResolution<'_>>], ) -> bool { !path @@ -174,7 +174,7 @@ fn is_trait_unused_in_scope( ctx: &AssistContext<'_, '_>, u: &ast::UseTree, - scope: &mut Vec<SearchScope>, + scope: &[SearchScope], t: &hir::Trait, ) -> bool { !std::iter::once((Definition::Trait(*t), u.rename())) @@ -186,30 +186,16 @@ ctx: &AssistContext<'_, 'db>, def: Definition<'db>, rename: Option<Rename>, - scopes: &Vec<SearchScope>, + scopes: &[SearchScope], ) -> bool { - let mut found = false; - - for scope in scopes { - let mut search_non_import = |_, r: FileReference| { - // The import itself is a use; we must skip that. - if !r.category.contains(ReferenceCategory::IMPORT) { - found = true; - true - } else { - false - } - }; + scopes.iter().any(|scope| { + // The import itself is a use; we must skip that. def.usages(&ctx.sema) + .set_included_categories(ReferenceCategory::IMPORT.complement()) .in_scope(scope) .with_rename(rename.as_ref()) - .search(&mut search_non_import); - if found { - break; - } - } - - found + .at_least_one() + }) } /// Build a search scope spanning the given module but none of its submodules.
diff --git a/crates/ide-assists/src/handlers/reorder_impl_items.rs b/crates/ide-assists/src/handlers/reorder_impl_items.rs index 658947a..ed5a372 100644 --- a/crates/ide-assists/src/handlers/reorder_impl_items.rs +++ b/crates/ide-assists/src/handlers/reorder_impl_items.rs
@@ -82,7 +82,7 @@ ast::AssocItem::MacroCall(_) => None, }; - name.and_then(|n| ranks.get(n.text().as_str().trim_start_matches("r#")).copied()) + name.and_then(|n| ranks.get(n.text().trim_start_matches("r#")).copied()) .unwrap_or(usize::MAX) }) .collect();
diff --git a/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs b/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs index 17ee859..a414db0 100644 --- a/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs +++ b/crates/ide-assists/src/handlers/replace_method_eager_lazy.rs
@@ -39,7 +39,7 @@ let (_, receiver_ty) = callable.receiver_param(ctx.sema.db)?; let n_params = callable.n_params() + 1; - let method_name_lazy = lazy_method_name(&method_name.text()); + let method_name_lazy = lazy_method_name(method_name.text()); receiver_ty.iterate_method_candidates_with_traits( ctx.sema.db, @@ -156,7 +156,7 @@ } let method_name_text = method_name.text(); - let method_name_eager = eager_method_name(&method_name_text)?; + let method_name_eager = eager_method_name(method_name_text)?; receiver_ty.iterate_method_candidates_with_traits( ctx.sema.db,
diff --git a/crates/ide-assists/src/handlers/term_search.rs b/crates/ide-assists/src/handlers/term_search.rs index 9036f195..5025f60 100644 --- a/crates/ide-assists/src/handlers/term_search.rs +++ b/crates/ide-assists/src/handlers/term_search.rs
@@ -39,11 +39,7 @@ sema: &ctx.sema, scope: &scope, goal: target_ty, - config: TermSearchConfig { - fuel: ctx.config.term_search_fuel, - enable_borrowcheck: ctx.config.term_search_borrowck, - ..Default::default() - }, + config: TermSearchConfig { fuel: ctx.config.term_search_fuel, ..Default::default() }, }; let paths = hir::term_search::term_search(&term_search_ctx);
diff --git a/crates/ide-assists/src/tests.rs b/crates/ide-assists/src/tests.rs index 3624099..220b0c3 100644 --- a/crates/ide-assists/src/tests.rs +++ b/crates/ide-assists/src/tests.rs
@@ -34,7 +34,6 @@ prefer_absolute: false, assist_emit_must_use: false, term_search_fuel: 400, - term_search_borrowck: true, code_action_grouping: true, expr_fill_default: ExprFillDefaultMode::Todo, prefer_self_ty: false, @@ -56,7 +55,6 @@ prefer_absolute: false, assist_emit_must_use: false, term_search_fuel: 400, - term_search_borrowck: true, code_action_grouping: false, expr_fill_default: ExprFillDefaultMode::Todo, prefer_self_ty: false, @@ -78,7 +76,6 @@ prefer_absolute: false, assist_emit_must_use: false, term_search_fuel: 400, - term_search_borrowck: true, code_action_grouping: true, expr_fill_default: ExprFillDefaultMode::Todo, prefer_self_ty: false, @@ -100,7 +97,6 @@ prefer_absolute: false, assist_emit_must_use: false, term_search_fuel: 400, - term_search_borrowck: true, code_action_grouping: true, expr_fill_default: ExprFillDefaultMode::Todo, prefer_self_ty: false, @@ -500,6 +496,35 @@ } #[test] +fn assist_order_term_search_unknown_type() { + let (db, frange) = RootDatabase::with_range( + r#" +//- minicore: todo, unimplemented +fn foo(_: u32) -> i32 { 2 } +fn main() { + let unknown; + let _: i32 = $0todo!()$0; +} +"#, + ); + + let assists = assists( + &db, + &TEST_CONFIG, + AssistResolveStrategy::None, + FileRange { file_id: frange.file_id.file_id(&db), range: frange.range }, + ); + let expected = labels(&assists); + + expect![[r#" + Inline macro + Extract into... + Extract Module + "#]] + .assert_eq(&expected); +} + +#[test] fn assist_filter_works() { let (db, frange) = RootDatabase::with_range( r#"
diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs index 670a030..388aac1 100644 --- a/crates/ide-assists/src/utils.rs +++ b/crates/ide-assists/src/utils.rs
@@ -344,7 +344,7 @@ let method = mce.name_ref()?; let arg_list = mce.arg_list()?; - let method = match method.text().as_str() { + let method = match method.text() { "is_some" => "is_none", "is_none" => "is_some", "is_ok" => "is_err", @@ -550,7 +550,7 @@ for item in il.assoc_items() { if let ast::AssocItem::Fn(f) = item && let Some(name) = f.name() - && names.iter().any(|n| n.eq_ignore_ascii_case(&name.text())) + && names.iter().any(|n| n.eq_ignore_ascii_case(name.text())) { return true; } @@ -664,7 +664,7 @@ .zip(generic_params.as_ref()) .and_then(|(trait_, params)| generic_param_associated_bounds(make, adt, trait_, params)); - let ty: ast::Type = make.ty_path(make.ident_path(&adt.name().unwrap().text())).into(); + let ty: ast::Type = make.ty_path(make.ident_path(adt.name().unwrap().text())).into(); let cfg_attrs = adt.attrs().filter(|attr| matches!(attr.meta(), Some(ast::Meta::CfgMeta(_)))); match trait_ {
diff --git a/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/crates/ide-assists/src/utils/gen_trait_fn_body.rs index c0ddcb9..277b5bd 100644 --- a/crates/ide-assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide-assists/src/utils/gen_trait_fn_body.rs
@@ -21,7 +21,7 @@ trait_ref: Option<TraitRef<'_>>, ) -> Option<ast::BlockExpr> { let _ = func.body()?; - match trait_path.segment()?.name_ref()?.text().as_str() { + match trait_path.segment()?.name_ref()?.text() { "Clone" => { stdx::always!(func.name().is_some_and(|name| name.text() == "clone")); gen_clone_impl(make, adt)
diff --git a/crates/ide-completion/src/completions/attribute.rs b/crates/ide-completion/src/completions/attribute.rs index 109ebce..c2d7cb9 100644 --- a/crates/ide-completion/src/completions/attribute.rs +++ b/crates/ide-completion/src/completions/attribute.rs
@@ -40,7 +40,6 @@ let path = attribute.path()?; let segments = path.segments().map(|s| s.name_ref()).collect::<Option<Vec<_>>>()?; let segments = segments.iter().map(|n| n.text()).collect::<Vec<_>>(); - let segments = segments.iter().map(|t| t.as_str()).collect::<Vec<_>>(); let tt = attribute.token_tree()?; match segments.as_slice() {
diff --git a/crates/ide-completion/src/completions/attribute/repr.rs b/crates/ide-completion/src/completions/attribute/repr.rs index 63cddb3..73ba847 100644 --- a/crates/ide-completion/src/completions/attribute/repr.rs +++ b/crates/ide-completion/src/completions/attribute/repr.rs
@@ -23,7 +23,7 @@ }) .any(|it| { let text = it.text(); - lookup.unwrap_or(label) == text || collides.contains(&text.as_str()) + lookup.unwrap_or(label) == text || collides.contains(&text) }); if repr_already_annotated { continue;
diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index 889beea..3e95128 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs
@@ -484,7 +484,6 @@ scope: &ctx.scope, goal: ty.clone(), config: hir::term_search::TermSearchConfig { - enable_borrowcheck: false, many_alternatives_threshold: 1, fuel: 200, },
diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs index 5a3a3ac..34b53e5 100644 --- a/crates/ide-completion/src/completions/postfix.rs +++ b/crates/ide-completion/src/completions/postfix.rs
@@ -365,7 +365,7 @@ match receiver { ast::Expr::PathExpr(path) => { if let Some(name) = path.path().and_then(|it| it.as_single_name_ref()) { - return placeholder(name.text().as_str()); + return placeholder(name.text()); } } ast::Expr::RefExpr(it) => {
diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 9f8a2f7..43b2a53 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs
@@ -3226,9 +3226,7 @@ foo($0); } "#, - // FIXME: term_search exclude ssss.0 (field.ty().is_unknown()) expect![[r#" - ex ssss.0 [type_could_unify] lc ssss S<{unknown}> [local] st S S<T> [] md core:: []
diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs index 4f70a90..2fa8a9d 100644 --- a/crates/ide-completion/src/render/function.rs +++ b/crates/ide-completion/src/render/function.rs
@@ -233,7 +233,8 @@ Some(n) => { let smol_str = n.display_no_db(ctx.edition).to_smolstr(); let text = smol_str.as_str().trim_start_matches('_'); - let ref_ = ref_of_param(ctx, text, param.ty()); + let ref_ = + ref_of_param(ctx, text, ¶m.ty().instantiate_with_errors()); f(&format_args!("${{{}:{ref_}{text}}}", index + offset)) } None => { @@ -288,14 +289,16 @@ } fn ref_of_param(ctx: &CompletionContext<'_, '_>, arg: &str, ty: &hir::Type<'_>) -> &'static str { - if let Some(derefed_ty) = ty.as_reference_inner() { + if let Some((_, mutability)) = ty.as_reference() { + let ref_prefix = if mutability.is_mut() { "&mut " } else { "&" }; + for (name, local) in ctx.locals.iter().sorted_by_key(|&(k, _)| k.clone()) { if name.as_str() == arg { - return if local.ty(ctx.db) == derefed_ty { - if ty.is_mutable_reference() { "&mut " } else { "&" } - } else { - "" - }; + let local_ty = local.ty(ctx.db); + let added_ref = local_ty.add_reference(ctx.db, mutability); + let needs_ref = + !local_ty.could_coerce_to(ctx.db, ty) && added_ref.could_coerce_to(ctx.db, ty); + return if needs_ref { ref_prefix } else { "" }; } } } @@ -333,7 +336,7 @@ let mut detail = String::with_capacity(signature.len()); for segment in signature.split_whitespace() { - if !detail.is_empty() { + if !detail.is_empty() && !detail.ends_with('(') && !segment.starts_with(')') { detail.push(' '); } @@ -473,7 +476,7 @@ r#" struct S {} impl S { - fn foo(&self, x: i32) { + fn foo(&self, x: i32, y: &i32) { $0 } } @@ -481,8 +484,8 @@ r#" struct S {} impl S { - fn foo(&self, x: i32) { - self.foo(${1:x});$0 + fn foo(&self, x: i32, y: &i32) { + self.foo(${1:x}, ${2:y});$0 } } "#, @@ -563,6 +566,24 @@ } "#, ); + check_edit( + "ref_arg", + r#" +//- minicore: coerce_unsized +fn ref_arg(x: &[i32]) {} +fn main() { + let x = [2]; + ref_ar$0 +} +"#, + r#" +fn ref_arg(x: &[i32]) {} +fn main() { + let x = [2]; + ref_arg(${1:&x});$0 +} +"#, + ); } #[test]
diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs index bc7b727..6c2502a4 100644 --- a/crates/ide-completion/src/tests/special.rs +++ b/crates/ide-completion/src/tests/special.rs
@@ -1617,6 +1617,19 @@ expect!("const fn(&'foo mut self, &Foo) -> !"), expect!("pub const fn baz<'foo>(&'foo mut self, x: &'foo Foo) -> !"), ); + + check_signatures( + r#" +struct Foo; +impl Foo { + pub fn method(&self, first: bool, second: bool, third: bool, fourth: bool) {} +} +fn main() { Foo.m$0 } +"#, + CompletionItemKind::SymbolKind(SymbolKind::Method), + expect!("fn(&self, bool, bool, bool, bool)"), + expect!("pub fn method(&self, first: bool, second: bool, third: bool, fourth: bool)"), + ); } #[test]
diff --git a/crates/ide-db/src/helpers.rs b/crates/ide-db/src/helpers.rs index 00e81b3..08e4b12 100644 --- a/crates/ide-db/src/helpers.rs +++ b/crates/ide-db/src/helpers.rs
@@ -3,7 +3,7 @@ use std::collections::VecDeque; use base_db::SourceDatabase; -use hir::{Crate, ItemInNs, ModuleDef, Name, Semantics}; +use hir::{Crate, HasAttrs, ItemInNs, ModuleDef, Name, Semantics}; use span::{Edition, FileId}; use syntax::{ AstToken, SyntaxKind, SyntaxToken, ToSmolStr, TokenAtOffset, @@ -110,7 +110,7 @@ .legacy_macros(db) .into_iter() // don't show legacy macros declared in the crate-root that were already covered in declarations earlier - .filter(|it| !(is_root && it.is_macro_export(db))) + .filter(|it| !(is_root && it.attrs(db).is_macro_export())) .for_each(|mac| cb(mac.into())); }
diff --git a/crates/ide-db/src/imports/import_assets.rs b/crates/ide-db/src/imports/import_assets.rs index f5dff47..422648c 100644 --- a/crates/ide-db/src/imports/import_assets.rs +++ b/crates/ide-db/src/imports/import_assets.rs
@@ -979,7 +979,7 @@ return None; } let after = std::iter::successors(path.parent_path(), |it| it.parent_path()) - .map(|seg| seg.segment()?.name_ref().map(|name| Name::new_root(&name.text()))) + .map(|seg| seg.segment()?.name_ref().map(|name| Name::new_root(name.text()))) .collect::<Option<_>>()?; path_import_candidate( sema, @@ -993,7 +993,7 @@ fn for_name(sema: &Semantics<'db, RootDatabase>, name: &ast::Name) -> Option<Self> { if sema .scope(name.syntax())? - .speculative_resolve(&make::ext::ident_path(&name.text())) + .speculative_resolve(&make::ext::ident_path(name.text())) .is_some() { return None; @@ -1033,7 +1033,7 @@ if qualifier.first_qualifier().is_none_or(|it| sema.resolve_path(&it).is_none()) { let qualifier = qualifier .segments() - .map(|seg| seg.name_ref().map(|name| Name::new_root(&name.text()))) + .map(|seg| seg.name_ref().map(|name| Name::new_root(name.text()))) .collect::<Option<Vec<_>>>()?; ImportCandidate::Path(PathImportCandidate { qualifier,
diff --git a/crates/ide-db/src/imports/insert_use.rs b/crates/ide-db/src/imports/insert_use.rs index 27e3ed6..0235389 100644 --- a/crates/ide-db/src/imports/insert_use.rs +++ b/crates/ide-db/src/imports/insert_use.rs
@@ -314,7 +314,7 @@ PathSegmentKind::SelfKw => ImportGroup::ThisModule, PathSegmentKind::SuperKw => ImportGroup::SuperModule, PathSegmentKind::CrateKw => ImportGroup::ThisCrate, - PathSegmentKind::Name(name) => match name.text().as_str() { + PathSegmentKind::Name(name) => match name.text() { "std" => ImportGroup::Std, "core" => ImportGroup::Std, _ => ImportGroup::ExternCrate,
diff --git a/crates/ide-db/src/imports/merge_imports.rs b/crates/ide-db/src/imports/merge_imports.rs index 5909905..f925145 100644 --- a/crates/ide-db/src/imports/merge_imports.rs +++ b/crates/ide-db/src/imports/merge_imports.rs
@@ -582,8 +582,8 @@ (Some(_), None) => Ordering::Greater, (None, Some(_)) => Ordering::Less, (Some(a_name), Some(b_name)) => { - let a_text = a_name.as_str().trim_start_matches("r#"); - let b_text = b_name.as_str().trim_start_matches("r#"); + let a_text = a_name.trim_start_matches("r#"); + let b_text = b_name.trim_start_matches("r#"); version_sort::version_sort(a_text, b_text) } } @@ -614,15 +614,13 @@ .name() .as_ref() .map(ast::Name::text) - .as_ref() - .map_or("_", |a_name| a_name.as_str().trim_start_matches("r#")) + .map_or("_", |a_name| a_name.trim_start_matches("r#")) .cmp( b_rename .name() .as_ref() .map(ast::Name::text) - .as_ref() - .map_or("_", |b_name| b_name.as_str().trim_start_matches("r#")), + .map_or("_", |b_name| b_name.trim_start_matches("r#")), ), }, };
diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs index 930cb99..e37c2f0 100644 --- a/crates/ide-db/src/lib.rs +++ b/crates/ide-db/src/lib.rs
@@ -256,6 +256,7 @@ pub fn line_index(db: &dyn SourceDatabase, file_id: FileId) -> &Arc<LineIndex> { #[salsa::interned] pub struct InternedFileId { + #[returns(copy)] id: FileId, } #[salsa::tracked(returns(ref))]
diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs index 101046c..ff32bad 100644 --- a/crates/ide-db/src/path_transform.rs +++ b/crates/ide-db/src/path_transform.rs
@@ -536,7 +536,7 @@ let name = ident_pat.name()?; let make = editor.make(); - let temp_path = make.path_from_text(&name.text()); + let temp_path = make.path_from_text(name.text()); let resolution = self.source_scope.speculative_resolve(&temp_path)?; @@ -651,7 +651,7 @@ }); for name in names { - if assoc_item_name.as_str() == name.as_str() { + if assoc_item_name == name.as_str() { // It is fine to return the first match because in case of // multiple possibilities, the exact trait must be disambiguated // in the definition of trait being implemented, so this search
diff --git a/crates/ide-db/src/ra_fixture.rs b/crates/ide-db/src/ra_fixture.rs index c8607a8..09a270c 100644 --- a/crates/ide-db/src/ra_fixture.rs +++ b/crates/ide-db/src/ra_fixture.rs
@@ -102,8 +102,7 @@ else { return false; }; - segment1.text_non_mutable() == "rust_analyzer" - && segment2.text_non_mutable() == "rust_fixture" + segment1.text() == "rust_analyzer" && segment2.text() == "rust_fixture" }) }); if !has_rust_fixture_attr {
diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index b89c2fd..16224ae 100644 --- a/crates/ide-db/src/rename.rs +++ b/crates/ide-db/src/rename.rs
@@ -459,7 +459,7 @@ }; expr.record_expr_field_list()?.fields().find_map(|record_field| { if record_field.name_ref().is_none() - && Name::new_root(&record_field.field_name()?.text()) == old_name + && Name::new_root(record_field.field_name()?.text()) == old_name && let ast::Expr::PathExpr(field_name) = record_field.expr()? { field_name.path() @@ -734,7 +734,7 @@ // special cases required for renaming fields/locals in Record patterns if let Some(pat_field) = pat.syntax().parent().and_then(ast::RecordPatField::cast) { if let Some(name_ref) = pat_field.name_ref() { - if new_name.as_str() == name_ref.text().as_str().trim_start_matches("r#") + if new_name.as_str() == name_ref.text().trim_start_matches("r#") && pat.at_token().is_none() { // Foo { field: ref mut local } -> Foo { ref mut field } @@ -747,7 +747,7 @@ .text_range() .cover_offset(pat.syntax().text_range().start()), ); - edit.replace(name_range, name_ref.text().to_string()); + edit.replace(name_range, name_ref.text().to_owned()); } else { // Foo { field: ref mut local @ local 2} -> Foo { field: ref mut new_name @ local2 } // Foo { field: ref mut local } -> Foo { field: ref mut new_name }
diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index b688cb1..c2cc9d7 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs
@@ -11,8 +11,8 @@ use either::Either; use hir::{ Adt, AsAssocItem, DefWithBody, EditionedFileId, ExpressionStoreOwner, FileRange, - FileRangeWrapper, HasAttrs, HasContainer, HasSource, InFile, InFileWrapper, InRealFile, - InlineAsmOperand, ItemContainer, ModuleSource, PathResolution, Semantics, Visibility, + FileRangeWrapper, HasContainer, HasSource, InFile, InFileWrapper, InRealFile, InlineAsmOperand, + ItemContainer, ModuleSource, PathResolution, Semantics, Visibility, }; use memchr::memmem::Finder; use parser::SyntaxKind; @@ -119,13 +119,13 @@ _ => None, } } - pub fn text(&self) -> syntax::TokenText<'_> { + pub fn text(&self) -> &str { match self { FileReferenceNode::NameRef(name_ref) => name_ref.text(), FileReferenceNode::Name(name) => name.text(), FileReferenceNode::Lifetime(lifetime) => lifetime.text(), FileReferenceNode::FormatStringEntry(it, range) => { - syntax::TokenText::borrowed(&it.text()[*range - it.syntax().text_range().start()]) + &it.text()[*range - it.syntax().text_range().start()] } } } @@ -397,24 +397,6 @@ }; } - if let Definition::Macro(macro_def) = self { - return match macro_def.kind(db) { - hir::MacroKind::Declarative => { - if macro_def.attrs(db).is_macro_export() { - SearchScope::reverse_dependencies(db, module.krate(db)) - } else { - SearchScope::krate(db, module.krate(db)) - } - } - hir::MacroKind::AttrBuiltIn - | hir::MacroKind::DeriveBuiltIn - | hir::MacroKind::DeclarativeBuiltIn => SearchScope::crate_graph(db), - hir::MacroKind::Derive | hir::MacroKind::Attr | hir::MacroKind::ProcMacro => { - SearchScope::reverse_dependencies(db, module.krate(db)) - } - }; - } - if let Definition::DeriveHelper(_) = self { return SearchScope::reverse_dependencies(db, module.krate(db)); } @@ -751,7 +733,7 @@ insert_type_alias( sema.db, &mut to_process, - name.text().as_str(), + name.text(), def.into(), ); } else { @@ -814,7 +796,7 @@ insert_type_alias( sema.db, &mut to_process, - name.text().as_str(), + name.text(), def.into(), ); } else {
diff --git a/crates/ide-db/src/symbol_index.rs b/crates/ide-db/src/symbol_index.rs index edec862..9de7cd9 100644 --- a/crates/ide-db/src/symbol_index.rs +++ b/crates/ide-db/src/symbol_index.rs
@@ -29,7 +29,7 @@ use base_db::{ CrateOrigin, InternedSourceRootId, LangCrateOrigin, LibraryRoots, LocalRoots, SourceRootId, - salsa::Update, source_root_crates, + salsa::SalsaValue, source_root_crates, }; use fst::{Automaton, Streamer, raw::IndexedValue}; use hir::{ @@ -359,31 +359,12 @@ candidate_modules.into_iter().map(|(module, _)| module).collect() } -#[derive(Default)] +#[derive(Default, SalsaValue)] pub struct SymbolIndex<'db> { symbols: Box<[FileSymbol<'db>]>, map: fst::Map<Vec<u8>>, } -// SAFETY: -// - It is safe to compare a `SymbolIndex` from a previous revision to a new one. -// - FileSymbol<'db>: Update -unsafe impl<'db> Update for SymbolIndex<'db> -where - FileSymbol<'db>: Update, -{ - unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool { - // SAFETY: Safe to dereference as per `salsa::Update` contract. - let this = unsafe { &mut *old_pointer }; - if *this != new_value { - *this = new_value; - true - } else { - false - } - } -} - impl<'db> SymbolIndex<'db> { /// The symbol index for a given source root within library_roots. pub fn library_symbols(
diff --git a/crates/ide-db/src/test_data/test_doc_alias.txt b/crates/ide-db/src/test_data/test_doc_alias.txt index 7a59abf..9b97b28 100644 --- a/crates/ide-db/src/test_data/test_doc_alias.txt +++ b/crates/ide-db/src/test_data/test_doc_alias.txt
@@ -2,7 +2,7 @@ ( Module { id: ModuleIdLt { - [salsa id]: Id(2800), + [salsa id]: Id(500), }, }, [ @@ -12,7 +12,7 @@ Struct( Struct { id: StructId( - 3001, + 0601, ), }, ), @@ -20,7 +20,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -48,7 +48,7 @@ Struct( Struct { id: StructId( - 3000, + 0600, ), }, ), @@ -56,7 +56,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -84,7 +84,7 @@ Struct( Struct { id: StructId( - 3000, + 0600, ), }, ), @@ -92,7 +92,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -120,7 +120,7 @@ Struct( Struct { id: StructId( - 3000, + 0600, ), }, ), @@ -128,7 +128,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -156,7 +156,7 @@ Struct( Struct { id: StructId( - 3000, + 0600, ), }, ), @@ -164,7 +164,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -192,7 +192,7 @@ Struct( Struct { id: StructId( - 3001, + 0601, ), }, ), @@ -200,7 +200,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -228,7 +228,7 @@ Struct( Struct { id: StructId( - 3000, + 0600, ), }, ), @@ -236,7 +236,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr {
diff --git a/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/crates/ide-db/src/test_data/test_symbol_index_collection.txt index d061976..1cf6042 100644 --- a/crates/ide-db/src/test_data/test_symbol_index_collection.txt +++ b/crates/ide-db/src/test_data/test_symbol_index_collection.txt
@@ -2,7 +2,7 @@ ( Module { id: ModuleIdLt { - [salsa id]: Id(2800), + [salsa id]: Id(500), }, }, [ @@ -11,14 +11,14 @@ def: EnumVariant( EnumVariant { id: EnumVariantId( - 7000, + 0e00, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -47,14 +47,14 @@ def: TypeAlias( TypeAlias { id: TypeAliasId( - 6400, + 0c80, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -81,14 +81,14 @@ def: EnumVariant( EnumVariant { id: EnumVariantId( - 7001, + 0e01, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -117,14 +117,14 @@ def: Const( Const { id: ConstId( - 5c00, + 0b80, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -151,14 +151,14 @@ def: Const( Const { id: ConstId( - 5c02, + 0b82, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -186,7 +186,7 @@ Enum( Enum { id: EnumId( - 4800, + 0900, ), }, ), @@ -194,7 +194,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -222,7 +222,7 @@ Macro { id: Macro2Id( Macro2Id( - 4400, + 0880, ), ), }, @@ -230,7 +230,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -258,7 +258,7 @@ Macro { id: Macro2Id( Macro2Id( - 4400, + 0880, ), ), }, @@ -266,7 +266,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -293,14 +293,14 @@ def: Static( Static { id: StaticId( - 6000, + 0c00, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -328,7 +328,7 @@ Struct( Struct { id: StructId( - 4001, + 0801, ), }, ), @@ -336,7 +336,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -364,7 +364,7 @@ Struct( Struct { id: StructId( - 4000, + 0800, ), }, ), @@ -372,7 +372,7 @@ loc: DeclarationLocation { hir_file_id: MacroFile( MacroCallId( - Id(3800), + Id(700), ), ), ptr: SyntaxNodePtr { @@ -400,7 +400,7 @@ Struct( Struct { id: StructId( - 4005, + 0805, ), }, ), @@ -408,7 +408,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -438,7 +438,7 @@ Struct( Struct { id: StructId( - 4006, + 0806, ), }, ), @@ -446,7 +446,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -476,7 +476,7 @@ Struct( Struct { id: StructId( - 4007, + 0807, ), }, ), @@ -484,7 +484,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -512,7 +512,7 @@ Struct( Struct { id: StructId( - 4002, + 0802, ), }, ), @@ -520,7 +520,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -547,14 +547,14 @@ def: Trait( Trait { id: TraitId( - 5400, + 0a80, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -582,7 +582,7 @@ Macro { id: Macro2Id( Macro2Id( - 4400, + 0880, ), ), }, @@ -590,7 +590,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -618,7 +618,7 @@ Union( Union { id: UnionId( - 4c00, + 0980, ), }, ), @@ -626,7 +626,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -653,14 +653,14 @@ def: Module( Module { id: ModuleIdLt { - [salsa id]: Id(2801), + [salsa id]: Id(501), }, }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -687,14 +687,14 @@ def: Module( Module { id: ModuleIdLt { - [salsa id]: Id(2802), + [salsa id]: Id(502), }, }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -722,7 +722,7 @@ Macro { id: MacroRulesId( MacroRulesId( - 3401, + 0681, ), ), }, @@ -730,7 +730,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -757,14 +757,14 @@ def: Function( FunctionId( FunctionId( - 5802, + 0b02, ), ), ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -793,14 +793,14 @@ def: Function( FunctionId( FunctionId( - 5801, + 0b01, ), ), ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -830,7 +830,7 @@ Macro { id: MacroRulesId( MacroRulesId( - 3400, + 0680, ), ), }, @@ -838,7 +838,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -865,14 +865,14 @@ def: Function( FunctionId( FunctionId( - 5800, + 0b00, ), ), ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -900,7 +900,7 @@ Macro { id: MacroRulesId( MacroRulesId( - 3401, + 0681, ), ), }, @@ -908,7 +908,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -935,14 +935,14 @@ def: Function( FunctionId( FunctionId( - 5803, + 0b03, ), ), ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -971,7 +971,7 @@ ( Module { id: ModuleIdLt { - [salsa id]: Id(2801), + [salsa id]: Id(501), }, }, [ @@ -981,7 +981,7 @@ Struct( Struct { id: StructId( - 4003, + 0803, ), }, ), @@ -989,7 +989,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr { @@ -1016,7 +1016,7 @@ ( Module { id: ModuleIdLt { - [salsa id]: Id(2802), + [salsa id]: Id(502), }, }, [ @@ -1025,14 +1025,14 @@ def: Trait( Trait { id: TraitId( - 5400, + 0a80, ), }, ), loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2401), + Id(481), ), ), ptr: SyntaxNodePtr { @@ -1060,7 +1060,7 @@ Macro { id: Macro2Id( Macro2Id( - 4400, + 0880, ), ), }, @@ -1068,7 +1068,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2401), + Id(481), ), ), ptr: SyntaxNodePtr { @@ -1096,7 +1096,7 @@ Struct( Struct { id: StructId( - 4004, + 0804, ), }, ), @@ -1104,7 +1104,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2401), + Id(481), ), ), ptr: SyntaxNodePtr { @@ -1132,7 +1132,7 @@ Macro { id: Macro2Id( Macro2Id( - 4400, + 0880, ), ), }, @@ -1140,7 +1140,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2401), + Id(481), ), ), ptr: SyntaxNodePtr { @@ -1168,7 +1168,7 @@ Struct( Struct { id: StructId( - 4004, + 0804, ), }, ), @@ -1176,7 +1176,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2401), + Id(481), ), ), ptr: SyntaxNodePtr {
diff --git a/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt b/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt index 20b162b..c70e63c 100644 --- a/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt +++ b/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt
@@ -5,7 +5,7 @@ Struct( Struct { id: StructId( - 3400, + 0680, ), }, ), @@ -13,7 +13,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2401), + Id(481), ), ), ptr: SyntaxNodePtr {
diff --git a/crates/ide-db/src/test_data/test_symbols_with_imports.txt b/crates/ide-db/src/test_data/test_symbols_with_imports.txt index 1d475ab..ac29c72 100644 --- a/crates/ide-db/src/test_data/test_symbols_with_imports.txt +++ b/crates/ide-db/src/test_data/test_symbols_with_imports.txt
@@ -5,7 +5,7 @@ Struct( Struct { id: StructId( - 3400, + 0680, ), }, ), @@ -13,7 +13,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2401), + Id(481), ), ), ptr: SyntaxNodePtr { @@ -41,7 +41,7 @@ Struct( Struct { id: StructId( - 3400, + 0680, ), }, ), @@ -49,7 +49,7 @@ loc: DeclarationLocation { hir_file_id: FileId( EditionedFileId( - Id(2400), + Id(480), ), ), ptr: SyntaxNodePtr {
diff --git a/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs b/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs index 225d3e0..6f0981b 100644 --- a/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs +++ b/crates/ide-diagnostics/src/handlers/invalid_lhs_of_assignment.rs
@@ -38,6 +38,19 @@ } #[test] + fn local_binding_shadows_function() { + check_diagnostics( + r#" +//- minicore: add +fn f() { + let mut f = 1; + f += 1; +} + "#, + ); + } + + #[test] fn struct_literal() { check_diagnostics( r#"
diff --git a/crates/ide-diagnostics/src/handlers/macro_error.rs b/crates/ide-diagnostics/src/handlers/macro_error.rs index 7acbcad..3c7d9f9 100644 --- a/crates/ide-diagnostics/src/handlers/macro_error.rs +++ b/crates/ide-diagnostics/src/handlers/macro_error.rs
@@ -329,6 +329,47 @@ } #[test] + fn no_stack_overflow_for_recursive_macro() { + check_diagnostics( + r#" +#![recursion_limit = "4"] +macro_rules! test { + () => { + fn my_test() { + test!(); + } + }; +} +mod tests { + test!(); +} +"#, + ); + } + + #[test] + fn no_stack_overflow_for_recursive_exported_macro() { + check_diagnostics( + r#" +#![recursion_limit = "4"] +#[macro_export] +macro_rules! test { + () => { + fn my_test() { + $crate::test!(); + } + }; +} + +mod tests { + use super::*; + test!(); +} +"#, + ); + } + + #[test] fn unimplemented_builtin_macro() { check_diagnostics( r#"
diff --git a/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs deleted file mode 100644 index e61719a..0000000 --- a/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs +++ /dev/null
@@ -1,260 +0,0 @@ -use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; -use hir::HirDisplay; - -// Diagnostic: moved-out-of-ref -// -// This diagnostic is triggered on moving non copy things out of references. -pub(crate) fn moved_out_of_ref( - ctx: &DiagnosticsContext<'_, '_>, - d: &hir::MovedOutOfRef<'_>, -) -> Diagnostic { - Diagnostic::new_with_syntax_node_ptr( - ctx, - DiagnosticCode::RustcHardError("E0507"), - format!("cannot move `{}` out of reference", d.ty.display(ctx.sema.db, ctx.display_target)), - d.span, - ) - // spans are broken, and I'm not sure how precise we can detect copy types -} - -#[cfg(test)] -mod tests { - use crate::tests::check_diagnostics; - - #[test] - fn operand_field_span_respected() { - check_diagnostics( - r#" -struct NotCopy; -struct S { - field: NotCopy, -} - -fn f(s: &S) -> S { - S { field: s.field } - //^^^^^^^ error: cannot move `NotCopy` out of reference -} - "#, - ); - } - - #[test] - fn move_by_explicit_deref() { - check_diagnostics( - r#" -struct X; -fn main() { - let a = &X; - let b = *a; - //^ error: cannot move `X` out of reference - _ = b; -} -"#, - ); - } - - #[test] - fn move_out_of_field() { - check_diagnostics( - r#" -//- minicore: copy -struct X; -struct Y(X, i32); -fn main() { - let a = &Y(X, 5); - let b = a.0; - //^ error: cannot move `X` out of reference - let y = a.1; - _ = (b, y); -} -"#, - ); - } - - #[test] - fn move_out_of_static() { - check_diagnostics( - r#" -//- minicore: copy -struct X; -fn main() { - static S: X = X; - let _s = S; - //^^ error: cannot move `X` out of reference -} -"#, - ); - } - - #[test] - fn generic_types() { - check_diagnostics( - r#" -//- minicore: derive, copy - -#[derive(Copy)] -struct X<T>(T); -struct Y; - -fn consume<T>(_: X<T>) { - -} - -fn main() { - let a = &X(Y); - consume(*a); - //^^ error: cannot move `X<Y>` out of reference - let a = &X(5); - consume(*a); -} -"#, - ); - } - - #[test] - fn no_false_positive_simple() { - check_diagnostics( - r#" -//- minicore: copy -fn f(_: i32) {} -fn main() { - let x = &2; - f(*x); -} -"#, - ); - } - - #[test] - fn no_false_positive_unknown_type() { - check_diagnostics( - r#" -//- minicore: derive, copy -fn f(x: &Unknown) -> Unknown { - *x -} - -#[derive(Copy)] -struct X<T>(T); - -struct Y<T>(T); - -fn g(x: &X<Unknown>) -> X<Unknown> { - *x -} - -fn h(x: &Y<Unknown>) -> Y<Unknown> { - // FIXME: we should show error for this, as `Y` is not copy - // regardless of its generic parameter. - *x -} - -"#, - ); - } - - #[test] - fn no_false_positive_dyn_fn() { - check_diagnostics( - r#" -//- minicore: copy, fn, dispatch_from_dyn -fn f(x: &mut &mut dyn Fn()) { - x(); -} - -struct X<'a> { - field: &'a mut dyn Fn(), -} - -fn g(x: &mut X<'_>) { - (x.field)(); -} -"#, - ); - } - - #[test] - fn no_false_positive_match_and_closure_capture() { - check_diagnostics( - r#" -//- minicore: copy, fn -enum X { - Foo(u16), - Bar, -} - -fn main() { - let x = &X::Bar; - let _c = || match *x { - X::Foo(t) => t, - _ => 5, - }; -} - "#, - ); - } - - #[test] - fn regression_15787() { - check_diagnostics( - r#" -//- minicore: coerce_unsized, slice, copy -fn foo(mut slice: &[u32]) -> usize { - slice = match slice { - [0, rest @ ..] | rest => rest, - }; - slice.len() -} -"#, - ); - } - - #[test] - fn regression_16564() { - check_diagnostics( - r#" -//- minicore: copy -fn test() { - let _x = (&(&mut (),)).0 as *const (); -} - "#, - ) - } - - #[test] - fn regression_18201() { - check_diagnostics( - r#" -//- minicore: copy -struct NotCopy; -struct S(NotCopy); -impl S { - fn f(&mut self) { - || { - if let ref mut _cb = self.0 { - } - }; - } -} -"#, - ) - } - - #[test] - fn regression_20155() { - check_diagnostics( - r#" -//- minicore: copy, option -struct Box(i32); -fn test() { - let b = Some(Box(0)); - || { - if let Some(b) = b { - let _move = b; - } - }; -} -"#, - ) - } -}
diff --git a/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs deleted file mode 100644 index fa9a69c..0000000 --- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ /dev/null
@@ -1,1332 +0,0 @@ -use ide_db::source_change::SourceChange; -use ide_db::text_edit::TextEdit; -use syntax::{AstNode, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, T, ast}; - -use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext, fix}; - -// Diagnostic: need-mut -// -// This diagnostic is triggered on mutating an immutable variable. -pub(crate) fn need_mut( - ctx: &DiagnosticsContext<'_, '_>, - d: &hir::NeedMut<'_>, -) -> Option<Diagnostic> { - let root = d.span.file_id.parse_or_expand(ctx.sema.db); - let node = d.span.value.to_node(&root); - let mut span = d.span; - if let Some(parent) = node.parent() - && ast::BinExpr::can_cast(parent.kind()) - { - // In case of an assignment, the diagnostic is provided on the variable name. - // We want to expand it to include the whole assignment, but only when this - // is an ordinary assignment, not a destructuring assignment. So, the direct - // parent is an assignment expression. - span = d.span.with_value(SyntaxNodePtr::new(&parent)); - }; - - let fixes = (|| { - if d.local.is_ref(ctx.sema.db) { - // There is no simple way to add `mut` to `ref x` and `ref mut x` - return None; - } - let file_id = span.file_id.file_id()?; - let mut edit_builder = TextEdit::builder(); - let use_range = span.value.text_range(); - for source in d.local.sources(ctx.sema.db) { - let Some(ast) = source.name() else { continue }; - // FIXME: macros - edit_builder.insert(ast.value.syntax().text_range().start(), "mut ".to_owned()); - } - let edit = edit_builder.finish(); - Some(vec![fix( - "add_mut", - "Change it to be mutable", - SourceChange::from_text_edit(file_id.file_id(ctx.sema.db), edit), - use_range, - )]) - })(); - - Some( - Diagnostic::new_with_syntax_node_ptr( - ctx, - // FIXME: `E0384` is not the only error that this diagnostic handles - DiagnosticCode::RustcHardError("E0384"), - format!( - "cannot mutate immutable variable `{}`", - d.local.name(ctx.sema.db).display(ctx.sema.db, ctx.edition) - ), - span, - ) - .stable() - .with_fixes(fixes), - ) -} - -// Diagnostic: unused-mut -// -// This diagnostic is triggered when a mutable variable isn't actually mutated. -pub(crate) fn unused_mut( - ctx: &DiagnosticsContext<'_, '_>, - d: &hir::UnusedMut<'_>, -) -> Option<Diagnostic> { - let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); - let fixes = (|| { - let file_id = ast.file_id.file_id()?; - let mut edit_builder = TextEdit::builder(); - let use_range = ast.value.text_range(); - for source in d.local.sources(ctx.sema.db) { - let ast = source.syntax(); - let Some(mut_token) = token(ast, T![mut]) else { continue }; - edit_builder.delete(mut_token.text_range()); - if let Some(token) = mut_token.next_token() - && token.kind() == SyntaxKind::WHITESPACE - { - edit_builder.delete(token.text_range()); - } - } - let edit = edit_builder.finish(); - Some(vec![fix( - "remove_mut", - "Remove unnecessary `mut`", - SourceChange::from_text_edit(file_id.file_id(ctx.sema.db), edit), - use_range, - )]) - })(); - Some( - Diagnostic::new_with_syntax_node_ptr( - ctx, - DiagnosticCode::RustcLint("unused_mut"), - "variable does not need to be mutable", - ast, - ) - // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive, hence not stable. - .with_fixes(fixes), - ) -} - -pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option<SyntaxToken> { - parent.children_with_tokens().filter_map(|it| it.into_token()).find(|it| it.kind() == kind) -} - -#[cfg(test)] -mod tests { - use crate::tests::{check_diagnostics, check_diagnostics_with_disabled, check_fix}; - - #[test] - fn unused_mut_simple() { - check_diagnostics( - r#" -fn f(_: i32) {} -fn main() { - let mut x = 2; - //^^^^^ 💡 warn: variable does not need to be mutable - f(x); -} -"#, - ); - } - - #[test] - fn no_false_positive_simple() { - check_diagnostics( - r#" -fn f(_: i32) {} -fn main() { - let x = 2; - f(x); -} -"#, - ); - check_diagnostics( - r#" -fn f(_: i32) {} -fn main() { - let mut x = 2; - x = 5; - f(x); -} -"#, - ); - } - - #[test] - fn multiple_errors_for_single_variable() { - check_diagnostics( - r#" -fn f(_: i32) {} -fn main() { - let x = 2; - x = 10; - //^^^^^^ 💡 error: cannot mutate immutable variable `x` - x = 5; - //^^^^^ 💡 error: cannot mutate immutable variable `x` - &mut x; - //^^^^^^ 💡 error: cannot mutate immutable variable `x` - f(x); -} -"#, - ); - } - - #[test] - fn unused_mut_fix() { - check_fix( - r#" -fn f(_: i32) {} -fn main() { - let mu$0t x = 2; - f(x); -} -"#, - r#" -fn f(_: i32) {} -fn main() { - let x = 2; - f(x); -} -"#, - ); - check_fix( - r#" -fn f(_: i32) {} -fn main() { - let ((mu$0t x, _) | (_, mut x)) = (2, 3); - f(x); -} -"#, - r#" -fn f(_: i32) {} -fn main() { - let ((x, _) | (_, x)) = (2, 3); - f(x); -} -"#, - ); - } - - #[test] - fn need_mut_fix() { - check_fix( - r#" -fn f(_: i32) {} -fn main() { - let x = 2; - x$0 = 5; - f(x); -} -"#, - r#" -fn f(_: i32) {} -fn main() { - let mut x = 2; - x = 5; - f(x); -} -"#, - ); - check_fix( - r#" -fn f(_: i32) {} -fn main() { - let ((x, _) | (_, x)) = (2, 3); - x =$0 4; - f(x); -} -"#, - r#" -fn f(_: i32) {} -fn main() { - let ((mut x, _) | (_, mut x)) = (2, 3); - x = 4; - f(x); -} -"#, - ); - - check_fix( - r#" -struct Foo(i32); - -impl Foo { - fn foo(self) { - self = Fo$0o(5); - } -} -"#, - r#" -struct Foo(i32); - -impl Foo { - fn foo(mut self) { - self = Foo(5); - } -} -"#, - ); - } - - #[test] - fn need_mut_fix_not_applicable_on_ref() { - check_diagnostics( - r#" -fn main() { - let ref x = 2; - x = &5; - //^^^^^^ error: cannot mutate immutable variable `x` -} -"#, - ); - check_diagnostics( - r#" -fn main() { - let ref mut x = 2; - x = &mut 5; - //^^^^^^^^^^ error: cannot mutate immutable variable `x` -} -"#, - ); - } - - #[test] - fn field_mutate() { - check_diagnostics( - r#" -fn f(_: i32) {} -fn main() { - let mut x = (2, 7); - //^^^^^ 💡 warn: variable does not need to be mutable - f(x.1); -} -"#, - ); - check_diagnostics( - r#" -fn f(_: i32) {} -fn main() { - let mut x = (2, 7); - x.0 = 5; - f(x.1); -} -"#, - ); - check_diagnostics( - r#" -fn f(_: i32) {} -fn main() { - let x = (2, 7); - x.0 = 5; - //^^^^^^^ 💡 error: cannot mutate immutable variable `x` - f(x.1); -} -"#, - ); - } - - #[test] - fn mutable_reference() { - check_diagnostics( - r#" -fn main() { - let mut x = &mut 2; - //^^^^^ 💡 warn: variable does not need to be mutable - *x = 5; -} -"#, - ); - check_diagnostics( - r#" -fn main() { - let x = 2; - &mut x; - //^^^^^^ 💡 error: cannot mutate immutable variable `x` -} -"#, - ); - check_diagnostics( - r#" -fn main() { - let x_own = 2; - let ref mut x_ref = x_own; - //^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x_own` - _ = x_ref; -} -"#, - ); - check_diagnostics( - r#" -struct Foo; -impl Foo { - fn method(&mut self, _x: i32) {} -} -fn main() { - let x = Foo; - x.method(2); - //^ 💡 error: cannot mutate immutable variable `x` -} -"#, - ); - } - - #[test] - fn regression_14310() { - check_diagnostics( - r#" - //- minicore: copy, builtin_impls - fn clone(mut i: &!) -> ! { - //^^^^^ 💡 warn: variable does not need to be mutable - *i - } - "#, - ); - } - - #[test] - fn match_closure_capture() { - check_diagnostics( - r#" -//- minicore: option -fn main() { - let mut v = &mut Some(2); - //^^^^^ 💡 warn: variable does not need to be mutable - let _ = || match v { - Some(k) => { - *k = 5; - } - None => {} - }; - let v = &mut Some(2); - let _ = || match v { - //^ 💡 error: cannot mutate immutable variable `v` - ref mut k => { - *k = &mut Some(5); - } - }; -} -"#, - ); - } - - #[test] - fn match_bindings() { - check_diagnostics( - r#" -fn main() { - match (2, 3) { - (x, mut y) => { - //^^^^^ 💡 warn: variable does not need to be mutable - x = 7; - //^^^^^ 💡 error: cannot mutate immutable variable `x` - _ = y; - } - } -} -"#, - ); - } - - #[test] - fn mutation_in_dead_code() { - // This one is interesting. Dead code is not represented at all in the MIR, so - // there would be no mutability error for locals in dead code. Rustc tries to - // not emit `unused_mut` in this case, but since it works without `mut`, and - // special casing it is not trivial, we emit it. - - // Update: now MIR based `unused-variable` is taking over `unused-mut` for the same reason. - check_diagnostics( - r#" -fn main() { - return; - let mut x = 2; - //^^^^^ 💡 warn: unused variable - &mut x; -} -"#, - ); - check_diagnostics( - r#" -fn main() { - loop {} - let mut x = 2; - //^^^^^ 💡 warn: unused variable - &mut x; -} -"#, - ); - check_diagnostics_with_disabled( - r#" -enum X {} -fn g() -> X { - loop {} -} -fn f() -> ! { - loop {} -} -fn main(b: bool) { - if b { - f(); - } else { - g(); - } - let mut x = 2; - //^^^^^ 💡 warn: unused variable - &mut x; -} -"#, - &["remove-unnecessary-else"], - ); - check_diagnostics_with_disabled( - r#" -fn main(b: bool) { - if b { - loop {} - } else { - return; - } - let mut x = 2; - //^^^^^ 💡 warn: unused variable - &mut x; -} -"#, - &["remove-unnecessary-else"], - ); - } - - #[test] - fn initialization_is_not_mutation() { - check_diagnostics( - r#" -fn f(_: i32) {} -fn main() { - let mut x; - //^^^^^ 💡 warn: variable does not need to be mutable - x = 5; - f(x); -} -"#, - ); - check_diagnostics( - r#" -fn f(_: i32) {} -fn main(b: bool) { - let mut x; - //^^^^^ 💡 warn: variable does not need to be mutable - if b { - x = 1; - } else { - x = 3; - } - f(x); -} -"#, - ); - check_diagnostics( - r#" -fn f(_: i32) {} -fn main(b: bool) { - let x; - if b { - x = 1; - } - x = 3; - //^^^^^ 💡 error: cannot mutate immutable variable `x` - f(x); -} -"#, - ); - check_diagnostics( - r#" -fn f(_: i32) {} -fn main() { - let x; - loop { - x = 1; - //^^^^^ 💡 error: cannot mutate immutable variable `x` - f(x); - } -} -"#, - ); - check_diagnostics( - r#" -fn check(_: i32) -> bool { - false -} -fn main() { - loop { - let x = 1; - if check(x) { - break; - } - let y = (1, 2); - if check(y.1) { - return; - } - let z = (1, 2); - match z { - (k @ 5, ref mut t) if { continue; } => { - //^^^^^^^^^ 💡 error: cannot mutate immutable variable `z` - *t = 5; - _ = k; - } - _ => { - let y = (1, 2); - if check(y.1) { - return; - } - } - } - } -} -"#, - ); - check_diagnostics( - r#" -fn f(_: i32) {} -fn main() { - loop { - let mut x = 1; - //^^^^^ 💡 warn: variable does not need to be mutable - f(x); - if let mut y = 2 { - //^^^^^ 💡 warn: variable does not need to be mutable - f(y); - } - match 3 { - mut z => f(z), - //^^^^^ 💡 warn: variable does not need to be mutable - } - } -} -"#, - ); - } - - #[test] - fn initialization_is_not_mutation_in_loop() { - check_diagnostics( - r#" -fn main() { - let a; - loop { - let c @ ( - mut b, - //^^^^^ 💡 warn: variable does not need to be mutable - mut d - //^^^^^ 💡 warn: variable does not need to be mutable - ); - a = 1; - //^^^^^ 💡 error: cannot mutate immutable variable `a` - b = 1; - c = (2, 3); - d = 3; - _ = (c, b, d); - } -} -"#, - ); - } - - #[test] - fn function_arguments_are_initialized() { - check_diagnostics( - r#" -fn f(mut x: i32) { - //^^^^^ 💡 warn: variable does not need to be mutable - f(x + 2); -} -"#, - ); - check_diagnostics( - r#" -fn f(x: i32) { - x = 5; - //^^^^^ 💡 error: cannot mutate immutable variable `x` -} -"#, - ); - check_diagnostics( - r#" -fn f((x, y): (i32, i32)) { - let t = [0; 2]; - x = 5; - //^^^^^ 💡 error: cannot mutate immutable variable `x` - _ = x; - _ = y; - _ = t; -} -"#, - ); - } - - #[test] - fn no_diagnostics_in_case_of_multiple_bounds() { - check_diagnostics( - r#" -fn f() { - let (b, a, b) = (2, 3, 5); - a = 8; - //^^^^^ 💡 error: cannot mutate immutable variable `a` -} -"#, - ); - } - - #[test] - fn for_loop() { - check_diagnostics( - r#" -//- minicore: iterators, copy -fn f(x: [(i32, u8); 10]) { - for (a, mut b) in x { - //^^^^^ 💡 warn: variable does not need to be mutable - a = 2; - //^^^^^ 💡 error: cannot mutate immutable variable `a` - _ = b; - } -} -"#, - ); - } - - #[test] - fn while_let() { - check_diagnostics( - r#" -//- minicore: iterators, copy -fn f(x: [(i32, u8); 10]) { - let mut it = x.into_iter(); - while let Some((a, mut b)) = it.next() { - //^^^^^ 💡 warn: variable does not need to be mutable - while let Some((c, mut d)) = it.next() { - //^^^^^ 💡 warn: variable does not need to be mutable - a = 2; - //^^^^^ 💡 error: cannot mutate immutable variable `a` - c = 2; - //^^^^^ 💡 error: cannot mutate immutable variable `c` - _ = (b, d); - } - } -} -"#, - ); - } - - #[test] - fn index() { - check_diagnostics( - r#" -//- minicore: coerce_unsized, index, slice -fn f() { - let x = [1, 2, 3]; - x[2] = 5; - //^^^^^^^^ 💡 error: cannot mutate immutable variable `x` - let x = &mut x; - //^^^^^^ 💡 error: cannot mutate immutable variable `x` - let mut x = x; - //^^^^^ 💡 warn: variable does not need to be mutable - x[2] = 5; -} -"#, - ); - } - - #[test] - fn overloaded_index() { - check_diagnostics( - r#" -//- minicore: index, copy -use core::ops::{Index, IndexMut}; - -struct Foo; -impl Index<usize> for Foo { - type Output = (i32, u8); - fn index(&self, _index: usize) -> &(i32, u8) { - &(5, 2) - } -} -impl IndexMut<usize> for Foo { - fn index_mut(&mut self, _index: usize) -> &mut (i32, u8) { - &mut (5, 2) - } -} -fn f() { - let mut x = Foo; - //^^^^^ 💡 warn: variable does not need to be mutable - let y = &x[2]; - _ = (x, y); - let x = Foo; - let y = &mut x[2]; - //^💡 error: cannot mutate immutable variable `x` - _ = (x, y); - let mut x = &mut Foo; - //^^^^^ 💡 warn: variable does not need to be mutable - let y: &mut (i32, u8) = &mut x[2]; - _ = (x, y); - let x = Foo; - let ref mut y = x[7]; - //^ 💡 error: cannot mutate immutable variable `x` - _ = (x, y); - let (ref mut y, _) = x[3]; - //^ 💡 error: cannot mutate immutable variable `x` - _ = y; - match x[10] { - //^ 💡 error: cannot mutate immutable variable `x` - (ref y, 5) => _ = y, - (_, ref mut y) => _ = y, - } - let mut x = Foo; - let mut i = 5; - //^^^^^ 💡 warn: variable does not need to be mutable - let y = &mut x[i]; - _ = y; -} -"#, - ); - } - - #[test] - fn overloaded_deref() { - check_diagnostics( - r#" -//- minicore: deref_mut, copy -use core::ops::{Deref, DerefMut}; - -struct Foo; -impl Deref for Foo { - type Target = (i32, u8); - fn deref(&self) -> &(i32, u8) { - &(5, 2) - } -} -impl DerefMut for Foo { - fn deref_mut(&mut self) -> &mut (i32, u8) { - &mut (5, 2) - } -} -fn f() { - let mut x = Foo; - //^^^^^ 💡 warn: variable does not need to be mutable - let y = &*x; - _ = (x, y); - let x = Foo; - let y = &mut *x; - // ^ 💡 error: cannot mutate immutable variable `x` - _ = (x, y); - let x = Foo; - //^ 💡 warn: unused variable - let x = Foo; - let y: &mut (i32, u8) = &mut x; - //^^^^^^ 💡 error: cannot mutate immutable variable `x` - _ = (x, y); - let ref mut y = *x; - // ^ 💡 error: cannot mutate immutable variable `x` - _ = y; - let (ref mut y, _) = *x; - // ^ 💡 error: cannot mutate immutable variable `x` - _ = y; - match *x { - // ^ 💡 error: cannot mutate immutable variable `x` - (ref y, 5) => _ = y, - (_, ref mut y) => _ = y, - } -} -"#, - ); - } - - #[test] - fn or_pattern() { - check_diagnostics( - r#" -//- minicore: option -fn f(_: i32) {} -fn main() { - let ((Some(mut x), None) | (_, Some(mut x))) = (None, Some(7)) else { return }; - //^^^^^ 💡 warn: variable does not need to be mutable - - f(x); -} -"#, - ); - check_diagnostics( - r#" -struct Foo(i32); - -const X: Foo = Foo(5); -const Y: Foo = Foo(12); - -const fn f(mut a: Foo) -> bool { - //^^^^^ 💡 warn: variable does not need to be mutable - match a { - X | Y => true, - _ => false, - } -} -"#, - ); - } - - #[test] - fn or_pattern_no_terminator() { - check_diagnostics( - r#" -enum Foo { - A, B, C, D -} - -use Foo::*; - -fn f(inp: (Foo, Foo, Foo, Foo)) { - let ((A, B, _, x) | (B, C | D, x, _)) = inp else { - return; - }; - x = B; - //^^^^^ 💡 error: cannot mutate immutable variable `x` -} -"#, - ); - } - - #[test] - // FIXME: We should have tests for `is_ty_uninhabited_from` - fn regression_14421() { - check_diagnostics( - r#" -pub enum Tree { - Node(TreeNode), - Leaf(TreeLeaf), -} - -struct Box<T>(&T); - -pub struct TreeNode { - pub depth: usize, - pub children: [Box<Tree>; 8] -} - -pub struct TreeLeaf { - pub depth: usize, - pub data: u8 -} - -pub fn test() { - let mut tree = Tree::Leaf( - //^^^^^^^^ 💡 warn: variable does not need to be mutable - TreeLeaf { - depth: 0, - data: 0 - } - ); - _ = tree; -} -"#, - ); - } - - #[test] - fn fn_traits() { - check_diagnostics( - r#" -//- minicore: fn -fn fn_ref(mut x: impl Fn(u8) -> u8) -> u8 { - //^^^^^ 💡 warn: variable does not need to be mutable - x(2) -} -fn fn_mut(x: impl FnMut(u8) -> u8) -> u8 { - x(2) - //^ 💡 error: cannot mutate immutable variable `x` -} -fn fn_borrow_mut(mut x: &mut impl FnMut(u8) -> u8) -> u8 { - //^^^^^ 💡 warn: variable does not need to be mutable - x(2) -} -fn fn_once(mut x: impl FnOnce(u8) -> u8) -> u8 { - //^^^^^ 💡 warn: variable does not need to be mutable - x(2) -} -"#, - ); - } - - #[test] - fn closure() { - check_diagnostics( - r#" - //- minicore: copy, fn - struct X; - - impl X { - fn mutate(&mut self) {} - } - - fn f() { - let x = 5; - let closure1 = || { x = 2; }; - //^^^^^ 💡 error: cannot mutate immutable variable `x` - let _ = closure1(); - //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` - let closure2 = || { x = x; }; - //^^^^^ 💡 error: cannot mutate immutable variable `x` - let closure3 = || { - let x = 2; - x = 5; - //^^^^^ 💡 error: cannot mutate immutable variable `x` - x - }; - let x = X; - let closure4 = || { x.mutate(); }; - //^ 💡 error: cannot mutate immutable variable `x` - _ = (closure2, closure3, closure4); - } - "#, - ); - check_diagnostics( - r#" - //- minicore: copy, fn - fn f() { - let mut x = 5; - //^^^^^ 💡 warn: variable does not need to be mutable - let mut y = 2; - y = 7; - let closure = || { - let mut z = 8; - z = 3; - let mut k = z; - //^^^^^ 💡 warn: variable does not need to be mutable - _ = k; - }; - _ = (x, closure); - } - "#, - ); - check_diagnostics( - r#" -//- minicore: copy, fn -fn f() { - let closure = || { - || { - || { - let x = 2; - || { || { x = 5; } } - //^^^^^ 💡 error: cannot mutate immutable variable `x` - } - } - }; - _ = closure; -} - "#, - ); - check_diagnostics( - r#" -//- minicore: copy, fn -fn f() { - struct X; - let mut x = X; - //^^^^^ 💡 warn: variable does not need to be mutable - let c1 = || x; - let mut x = X; - let c2 = || { x = X; x }; - let mut x = X; - let c3 = move || { x = X; }; - _ = (c1, c2, c3); -} - "#, - ); - check_diagnostics( - r#" - //- minicore: copy, fn, deref_mut - struct X(i32, i64); - - fn f() { - let mut x = &mut 5; - //^^^^^ 💡 warn: variable does not need to be mutable - let closure1 = || { *x = 2; }; - let _ = closure1(); - //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` - let mut x = &mut 5; - //^^^^^ 💡 warn: variable does not need to be mutable - let closure1 = || { *x = 2; &x; }; - let _ = closure1(); - //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` - let mut x = &mut 5; - let closure1 = || { *x = 2; &x; x = &mut 3; }; - let _ = closure1(); - //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` - let mut x = &mut 5; - //^^^^^ 💡 warn: variable does not need to be mutable - let closure1 = move || { *x = 2; }; - let _ = closure1(); - //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` - let mut x = &mut X(1, 2); - //^^^^^ 💡 warn: variable does not need to be mutable - let closure1 = || { x.0 = 2; }; - let _ = closure1(); - //^^^^^^^^ 💡 error: cannot mutate immutable variable `closure1` - } - "#, - ); - } - - #[test] - fn slice_pattern() { - check_diagnostics( - r#" -//- minicore: coerce_unsized, deref_mut, slice, copy -fn x(t: &[u8]) { - match t { - &[a, mut b] | &[a, _, mut b] => { - //^^^^^ 💡 warn: variable does not need to be mutable - - a = 2; - //^^^^^ 💡 error: cannot mutate immutable variable `a` - _ = b; - } - _ => {} - } -} - "#, - ); - } - - #[test] - fn boxes() { - check_diagnostics( - r#" -//- minicore: coerce_unsized, deref_mut, slice -use core::ops::{Deref, DerefMut}; -use core::{marker::Unsize, ops::CoerceUnsized}; - -#[rustc_intrinsic] -#[rustc_intrinsic_must_be_overridden] -pub fn box_new<T>(_x: T) -> Box<T>; - -#[lang = "owned_box"] -pub struct Box<T: ?Sized> { - inner: *mut T, -} -impl<T> Box<T> { - fn new(t: T) -> Self { - box_new(t) - } -} - -impl<T: ?Sized> Deref for Box<T> { - type Target = T; - - fn deref(&self) -> &T { - &**self - } -} - -impl<T: ?Sized> DerefMut for Box<T> { - fn deref_mut(&mut self) -> &mut T { - &mut **self - } -} - -fn f() { - let x = Box::new(5); - x = Box::new(7); - //^^^^^^^^^^^^^^^ 💡 error: cannot mutate immutable variable `x` - let x = Box::new(5); - *x = 7; - //^^^^^^ 💡 error: cannot mutate immutable variable `x` - let mut y = Box::new(5); - //^^^^^ 💡 warn: variable does not need to be mutable - *x = *y; - //^^^^^^^ 💡 error: cannot mutate immutable variable `x` - let x = Box::new(5); - let closure = || *x = 2; - //^^^^^^ 💡 error: cannot mutate immutable variable `x` - _ = closure; -} -"#, - ); - } - - #[test] - fn regression_15143() { - check_diagnostics( - r#" - trait Tr { - type Ty; - } - - struct A; - - impl Tr for A { - type Ty = (u32, i64); - } - - struct B<T: Tr> { - f: <T as Tr>::Ty, - } - - fn main(b: B<A>) { - let f = b.f.0; - f = 5; - //^^^^^ 💡 error: cannot mutate immutable variable `f` - } - "#, - ); - } - - #[test] - fn allow_unused_mut_for_identifiers_starting_with_underline() { - check_diagnostics( - r#" -fn f(_: i32) {} -fn main() { - let mut _x = 2; - f(_x); -} -"#, - ); - } - - #[test] - fn respect_lint_attributes_for_unused_mut() { - check_diagnostics( - r#" -fn f(_: i32) {} -fn main() { - #[allow(unused_mut)] - let mut x = 2; - f(x); -} - -fn main2() { - #[deny(unused_mut)] - let mut x = 2; - //^^^^^ 💡 error: variable does not need to be mutable - f(x); -} -"#, - ); - check_diagnostics( - r#" -macro_rules! mac { - ($($x:expr),*$(,)*) => ({ - #[allow(unused_mut)] - let mut vec = 2; - vec - }); -} - -fn main2() { - let mut x = mac![]; - //^^^^^ 💡 warn: variable does not need to be mutable - _ = x; -} - "#, - ); - } - - #[test] - fn regression_15099() { - check_diagnostics( - r#" -//- minicore: iterator, range -fn f() { - loop {} - for _ in 0..2 {} -} -"#, - ); - } - - #[test] - fn regression_15623() { - check_diagnostics( - r#" -//- minicore: fn - -struct Foo; - -impl Foo { - fn needs_mut(&mut self) {} -} - -fn foo(mut foo: Foo) { - let mut call_me = || { - let 0 = 1 else { return }; - foo.needs_mut(); - }; - call_me(); -} -"#, - ); - } - - #[test] - fn regression_15670() { - check_diagnostics( - r#" -//- minicore: fn - -pub struct A {} -pub unsafe fn foo(a: *mut A) { - let mut b = || -> *mut A { unsafe { &mut *a } }; - //^^^^^ 💡 warn: variable does not need to be mutable - let _ = b(); -} -"#, - ); - } - - #[test] - fn regression_15799() { - check_diagnostics( - r#" -//- minicore: deref_mut -struct WrapPtr(*mut u32); - -impl core::ops::Deref for WrapPtr { - type Target = *mut u32; - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -fn main() { - let mut x = 0u32; - let wrap = WrapPtr(&mut x); - unsafe { - **wrap = 6; - } -} -"#, - ); - } - - #[test] - fn destructuring_assignment_needs_mut() { - check_diagnostics( - r#" -//- minicore: fn - -fn main() { - let mut var = 1; - let mut func = || (var,) = (2,); - func(); -} - "#, - ); - } - - #[test] - fn regression_20662() { - check_diagnostics( - r#" -//- minicore: index, slice -pub trait A: core::ops::IndexMut<usize> { - type T: A; -} - -fn func(a: &mut impl A, b: &mut [i32]) { - b[0] += 1; -} - "#, - ); - } -}
diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs index b16be50..df815d6 100644 --- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs
@@ -45,18 +45,31 @@ cov_mark::hit!(type_mismatch_range_adjustment); Some(salient_token_range) }); + + let expected = d + .expected + .display(ctx.db(), ctx.display_target) + .with_closure_style(ClosureStyle::ClosureWithId) + .to_string(); + let actual = d + .actual + .display(ctx.db(), ctx.display_target) + .with_closure_style(ClosureStyle::ClosureWithId) + .to_string(); + + // The types differ (that's why we're here), yet they render the same, e.g. `foo::S` and + // `bar::S` both render as `S`. Retry with qualified paths so the message isn't a useless + // "expected S, found S". + let (expected, actual) = if expected == actual { + qualified_display(ctx, d).unwrap_or((expected, actual)) + } else { + (expected, actual) + }; + Some( Diagnostic::new( DiagnosticCode::RustcHardError("E0308"), - format!( - "expected {}, found {}", - d.expected - .display(ctx.db(), ctx.display_target) - .with_closure_style(ClosureStyle::ClosureWithId), - d.actual - .display(ctx.db(), ctx.display_target) - .with_closure_style(ClosureStyle::ClosureWithId), - ), + format!("expected {expected}, found {actual}"), display_range, ) .stable() @@ -64,6 +77,20 @@ ) } +/// Renders both sides of the mismatch with qualified paths, for when their plain names collide. +/// Returns `None` if either side has no renderable path, in which case both keep their plain +/// names — mixing a qualified and an unqualified name would be more confusing, not less. +fn qualified_display( + ctx: &DiagnosticsContext<'_, '_>, + d: &hir::TypeMismatch<'_>, +) -> Option<(String, String)> { + let root = d.expr_or_pat.file_id.parse_or_expand(ctx.db()); + let module = ctx.sema.scope(d.expr_or_pat.value.to_node(&root).syntax())?.module(); + let expected = d.expected.display_source_code(ctx.db(), module.into(), true).ok()?; + let actual = d.actual.display_source_code(ctx.db(), module.into(), true).ok()?; + Some((expected, actual)) +} + fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>) -> Option<Vec<Assist>> { let mut fixes = Vec::new(); @@ -1838,4 +1865,88 @@ "#, ); } + + // Tests for qualified paths on name collision (issue #22331) + + #[test] + fn type_mismatch_collision_basic_structs() { + check_diagnostics( + r#" +mod foo { + pub struct S; +} +mod bar { + pub struct S; +} +fn test(_: foo::S) { + test(bar::S); + //^^^^^^ error: expected foo::S, found bar::S +} +"#, + ); + } + + #[test] + fn type_mismatch_collision_in_generic() { + check_diagnostics( + r#" +//- minicore: option +mod foo { + pub struct S; +} +mod bar { + pub struct S; +} +fn make() -> Option<bar::S> { loop {} } +fn test(_: Option<foo::S>) { + test(make()); + //^^^^^^ error: expected Option<foo::S>, found Option<bar::S> +} +"#, + ); + } + + #[test] + fn type_mismatch_no_collision_unchanged() { + check_diagnostics( + r#" +mod foo { + pub struct S; +} +mod bar { + pub struct T; +} +fn test(_: foo::S) { + test(bar::T); + //^^^^^^ error: expected S, found T +} +"#, + ); + } + + #[test] + fn type_mismatch_multiple_collisions() { + check_diagnostics( + r#" +//- minicore: result +mod foo { + pub struct T; +} +mod bar { + pub struct T; +} +mod baz { + pub struct E; +} +mod qux { + pub struct E; +} +fn make() -> Result<bar::T, qux::E> { loop {} } +fn test(_: Result<foo::T, baz::E>) { + test(make()); + //^^^^^^ error: expected Result<foo::T, baz::E>, found Result<bar::T, qux::E> +} +"#, + ); + } }
diff --git a/crates/ide-diagnostics/src/handlers/type_must_be_known.rs b/crates/ide-diagnostics/src/handlers/type_must_be_known.rs index ae4e6c6..5a338de 100644 --- a/crates/ide-diagnostics/src/handlers/type_must_be_known.rs +++ b/crates/ide-diagnostics/src/handlers/type_must_be_known.rs
@@ -64,11 +64,9 @@ r#" fn foo() { let var = loop {}; - // ^^^ 💡 warn: unused variable var(); // ^^^ error: type annotations needed; type must be known at this point let var = loop {}; - // ^^^ 💡 warn: unused variable var[0]; // ^^^ error: type annotations needed; type must be known at this point }
diff --git a/crates/ide-diagnostics/src/handlers/typed_hole.rs b/crates/ide-diagnostics/src/handlers/typed_hole.rs index 4c59482..e2d3150 100644 --- a/crates/ide-diagnostics/src/handlers/typed_hole.rs +++ b/crates/ide-diagnostics/src/handlers/typed_hole.rs
@@ -54,12 +54,7 @@ sema: &ctx.sema, scope: &scope, goal: d.expected.clone(), - config: TermSearchConfig { - fuel: ctx.config.term_search_fuel, - enable_borrowcheck: ctx.config.term_search_borrowck, - - ..Default::default() - }, + config: TermSearchConfig { fuel: ctx.config.term_search_fuel, ..Default::default() }, }; let paths = term_search(&term_search_ctx);
diff --git a/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/crates/ide-diagnostics/src/handlers/unlinked_file.rs index dc6ae6f..376fc4fb 100644 --- a/crates/ide-diagnostics/src/handlers/unlinked_file.rs +++ b/crates/ide-diagnostics/src/handlers/unlinked_file.rs
@@ -3,14 +3,15 @@ use std::iter; use hir::crate_def_map; -use hir::{InFile, ModuleSource}; +use hir::{EditionedFileId, InFile, ModuleSource}; use ide_db::text_edit::TextEdit; use ide_db::{FileId, FileRange, base_db::SourceDatabase, source_change::SourceChange}; use ide_db::{base_db, line_index}; use paths::Utf8Component; use syntax::{ - AstNode, TextRange, + AstNode, Edition, TextRange, ast::{self, HasModuleItem, HasName, edit::IndentLevel}, + utils::{is_identifier, is_raw_identifier}, }; use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, Severity, fix}; @@ -22,10 +23,11 @@ pub(crate) fn unlinked_file( ctx: &DiagnosticsContext<'_, '_>, acc: &mut Vec<Diagnostic>, - file_id: FileId, + editioned_file_id: EditionedFileId, ) { + let file_id = editioned_file_id.file_id(ctx.sema.db); let mut range = TextRange::up_to(line_index(ctx.sema.db, file_id).len()); - let fixes = fixes(ctx, file_id, range); + let fixes = fixes(ctx, editioned_file_id, range); // FIXME: This is a hack for the vscode extension to notice whether there is an autofix or not before having to resolve diagnostics. // This is to prevent project linking popups from appearing when there is an autofix. https://github.com/rust-lang/rust-analyzer/issues/14523 let message = if fixes.is_none() { @@ -74,13 +76,15 @@ fn fixes( ctx: &DiagnosticsContext<'_, '_>, - file_id: FileId, + editioned_file_id: EditionedFileId, trigger_range: TextRange, ) -> Option<Vec<Assist>> { // If there's an existing module that could add `mod` or `pub mod` items to include the unlinked file, // suggest that as a fix. let db = ctx.sema.db; + let file_id = editioned_file_id.file_id(db); + let edition = editioned_file_id.edition(db); let source_root = ctx.sema.db.file_source_root(file_id).source_root_id(db); let source_root = ctx.sema.db.source_root(source_root).source_root(db); @@ -136,6 +140,7 @@ return make_fixes( parent_file_id.file_id(ctx.sema.db), source, + edition, &module_name, trigger_range, ); @@ -169,6 +174,7 @@ return make_fixes( parent_id, module.definition_source(ctx.sema.db).value, + edition, &module_name, trigger_range, ); @@ -193,6 +199,7 @@ return make_fixes( parent_file_id.file_id(ctx.sema.db), source, + edition, &module_name, trigger_range, ); @@ -202,9 +209,26 @@ None } +/// Convert a module name along with its visibility to code. +/// In most cases, this just adds the visibility and keyword beforehand, +/// but the exceptions are non-identifiers and keywords. +fn format_mod_name(mod_name: &str, visibility_and_keyword: &str, edition: Edition) -> String { + if is_identifier(mod_name, edition) { + format!("{visibility_and_keyword} {mod_name};") + } else { + if is_raw_identifier(mod_name, edition) { + format!("{visibility_and_keyword} r#{mod_name};") + } else { + let file_name = format!("{mod_name}.rs"); + format!("#[path = {file_name:?}]\n{visibility_and_keyword} mod_name;") + } + } +} + fn make_fixes( parent_file_id: FileId, source: ModuleSource, + edition: Edition, new_mod_name: &str, trigger_range: TextRange, ) -> Option<Vec<Assist>> { @@ -212,9 +236,9 @@ matches!(item, ast::Item::Module(m) if m.item_list().is_none()) } - let mod_decl = format!("mod {new_mod_name};"); - let pub_mod_decl = format!("pub mod {new_mod_name};"); - let pub_crate_mod_decl = format!("pub(crate) mod {new_mod_name};"); + let mod_decl = format_mod_name(new_mod_name, "mod", edition); + let pub_mod_decl = format_mod_name(new_mod_name, "pub mod", edition); + let pub_crate_mod_decl = format_mod_name(new_mod_name, "pub(crate) mod", edition); let mut mod_decl_builder = TextEdit::builder(); let mut pub_mod_decl_builder = TextEdit::builder(); @@ -548,4 +572,88 @@ "#, ); } + + #[test] + fn unlinked_file_with_strict_keyword_move() { + check_fix( + r#" +//- /main.rs +//- /move.rs +$0 +"#, + r#" +mod r#move; +"#, + ); + } + + #[test] + fn unlinked_file_with_weak_keyword_safe() { + check_fix( + r#" +//- /main.rs +//- /safe.rs +$0 +"#, + r#" +mod safe; +"#, + ); + } + + #[test] + fn unlinked_file_with_reserved_keyword_abstract() { + check_fix( + r#" +//- /main.rs +//- /abstract.rs +$0 +"#, + r#" +mod r#abstract; +"#, + ); + } + + #[test] + fn unlinked_file_with_unescaped_keyword_crate() { + check_fix( + r#" +//- /main.rs +//- /crate.rs +$0 +"#, + r#"#[path = "crate.rs"] +mod mod_name; +"#, + ); + } + + #[test] + fn unlinked_invalid_symbol_in_module_name() { + check_fix( + r#" +//- /main.rs +//- /my-file.rs +$0 +"#, + r#"#[path = "my-file.rs"] +mod mod_name; +"#, + ); + } + + #[test] + fn unlinked_numeric_module_name() { + check_fix( + r#" +//- /main.rs +//- /0000.rs +$0 +"#, + r#"#[path = "0000.rs"] +mod mod_name; +"#, + ); + } }
diff --git a/crates/ide-diagnostics/src/handlers/unused_variables.rs b/crates/ide-diagnostics/src/handlers/unused_variables.rs deleted file mode 100644 index 9e5150c..0000000 --- a/crates/ide-diagnostics/src/handlers/unused_variables.rs +++ /dev/null
@@ -1,420 +0,0 @@ -use hir::Name; -use ide_db::text_edit::TextEdit; -use ide_db::{ - FileRange, RootDatabase, - assists::{Assist, AssistId}, - label::Label, - source_change::SourceChange, -}; -use syntax::{AstNode, Edition, TextRange, ToSmolStr}; - -use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; - -// Diagnostic: unused-variables -// -// This diagnostic is triggered when a local variable is not used. -pub(crate) fn unused_variables( - ctx: &DiagnosticsContext<'_, '_>, - d: &hir::UnusedVariable<'_>, -) -> Option<Diagnostic> { - let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); - if ast.file_id.macro_file().is_some() { - // FIXME: Our infra can't handle allow from within macro expansions rn - return None; - } - let diagnostic_range = ctx.sema.diagnostics_display_range(ast); - // The range for the Actual Name. We don't want to replace the entire declaration. Using the diagnostic range causes issues within in Array Destructuring. - let primary_source = d.local.primary_source(ctx.sema.db); - let name_range = primary_source - .name() - .map(|v| v.syntax().original_file_range_rooted(ctx.sema.db)) - .filter(|it| { - Some(it.file_id) == ast.file_id.file_id() - && diagnostic_range.range.contains_range(it.range) - }); - let is_shorthand_field = primary_source - .source - .value - .left() - .and_then(|name| name.syntax().parent()) - .and_then(syntax::ast::RecordPatField::cast) - .is_some_and(|field| field.colon_token().is_none()); - let var_name = d.local.name(ctx.sema.db); - Some( - Diagnostic::new_with_syntax_node_ptr( - ctx, - DiagnosticCode::RustcLint("unused_variables"), - "unused variable", - ast, - ) - .with_fixes(name_range.and_then(|it| { - fixes( - ctx.sema.db, - var_name, - it.range, - diagnostic_range, - ast.file_id.is_macro(), - is_shorthand_field, - ctx.edition, - ) - })), - ) -} - -fn fixes( - db: &RootDatabase, - var_name: Name, - name_range: TextRange, - diagnostic_range: FileRange, - is_in_marco: bool, - is_shorthand_field: bool, - edition: Edition, -) -> Option<Vec<Assist>> { - if is_in_marco { - return None; - } - let name = var_name.display(db, edition).to_smolstr(); - let name = name.strip_prefix("r#").unwrap_or(&name); - let new_name = if is_shorthand_field { format!("{name}: _{name}") } else { format!("_{name}") }; - - Some(vec![Assist { - id: AssistId::quick_fix("unscore_unused_variable_name"), - label: Label::new(format!("Rename unused {name} to {new_name}")), - group: None, - target: diagnostic_range.range, - source_change: Some(SourceChange::from_text_edit( - diagnostic_range.file_id, - TextEdit::replace(name_range, new_name), - )), - command: None, - }]) -} - -#[cfg(test)] -mod tests { - use crate::tests::{check_diagnostics, check_fix}; - - #[test] - fn unused_variables_simple() { - check_diagnostics( - r#" -//- minicore: fn -struct Foo { f1: i32, f2: i64 } - -fn f(kkk: i32) {} - //^^^ 💡 warn: unused variable -fn main() { - let a = 2; - //^ 💡 warn: unused variable - let b = 5; - // note: `unused variable` implies `unused mut`, so we should not emit both at the same time. - let mut c = f(b); - //^^^^^ 💡 warn: unused variable - let (d, e) = (3, 5); - //^ 💡 warn: unused variable - let _ = e; - let f1 = 2; - let f2 = 5; - let f = Foo { f1, f2 }; - match f { - Foo { f1, f2 } => { - //^^ 💡 warn: unused variable - _ = f2; - } - } - let g = false; - if g {} - let h: fn() -> i32 = || 2; - let i = h(); - //^ 💡 warn: unused variable -} -"#, - ); - } - - #[test] - fn unused_self() { - check_diagnostics( - r#" -struct S { -} -impl S { - fn owned_self(self, u: i32) {} - //^ 💡 warn: unused variable - fn ref_self(&self, u: i32) {} - //^ 💡 warn: unused variable - fn ref_mut_self(&mut self, u: i32) {} - //^ 💡 warn: unused variable - fn owned_mut_self(mut self) {} - //^^^^^^^^ 💡 warn: variable does not need to be mutable - -} -"#, - ); - } - - #[test] - fn allow_unused_variables_for_identifiers_starting_with_underline() { - check_diagnostics( - r#" -fn main() { - let _x = 2; -} -"#, - ); - } - - #[test] - fn respect_lint_attributes_for_unused_variables() { - check_diagnostics( - r#" -fn main() { - #[allow(unused_variables)] - let x = 2; -} - -#[deny(unused)] -fn main2() { - let x = 2; - //^ 💡 error: unused variable -} -"#, - ); - } - - #[test] - fn apply_last_lint_attribute_when_multiple_are_present() { - check_diagnostics( - r#" -#![allow(unused_variables)] -#![warn(unused_variables)] -#![deny(unused_variables)] - -fn main() { - let x = 2; - //^ 💡 error: unused variable - - #[deny(unused_variables)] - #[warn(unused_variables)] - #[allow(unused_variables)] - let y = 0; -} -"#, - ); - } - - #[test] - fn prefer_closest_ancestor_lint_attribute() { - check_diagnostics( - r#" -#![allow(unused_variables)] - -fn main() { - #![warn(unused_variables)] - - #[deny(unused_variables)] - let x = 2; - //^ 💡 error: unused variable -} - -#[warn(unused_variables)] -fn main2() { - #[deny(unused_variables)] - let x = 2; - //^ 💡 error: unused variable -} - -#[warn(unused_variables)] -fn main3() { - let x = 2; - //^ 💡 warn: unused variable -} - -fn main4() { - let x = 2; -} -"#, - ); - } - - #[test] - fn fix_unused_variable() { - check_fix( - r#" -fn main() { - let x$0 = 2; -} -"#, - r#" -fn main() { - let _x = 2; -} -"#, - ); - - check_fix( - r#" -fn main() { - let ($0d, _e) = (3, 5); -} -"#, - r#" -fn main() { - let (_d, _e) = (3, 5); -} -"#, - ); - - check_fix( - r#" -struct Foo { f1: i32, f2: i64 } -fn main() { - let f = Foo { f1: 0, f2: 0 }; - match f { - Foo { f1$0, f2 } => { - _ = f2; - } - } -} -"#, - r#" -struct Foo { f1: i32, f2: i64 } -fn main() { - let f = Foo { f1: 0, f2: 0 }; - match f { - Foo { f1: _f1, f2 } => { - _ = f2; - } - } -} -"#, - ); - - check_fix( - r#" -fn main() { - let $0r#type = 2; -} -"#, - r#" -fn main() { - let _type = 2; -} -"#, - ); - } - - #[test] - fn no_fix_for_marco() { - check_diagnostics( - r#" -macro_rules! my_macro { - () => { - let x = 3; - }; -} - -fn main() { - my_macro!(); -} -"#, - ); - } - #[test] - fn unused_variable_in_array_destructure() { - check_fix( - r#" -fn main() { - let arr = [1, 2, 3, 4, 5]; - let [_x, y$0 @ ..] = arr; -} -"#, - r#" -fn main() { - let arr = [1, 2, 3, 4, 5]; - let [_x, _y @ ..] = arr; -} -"#, - ); - } - - #[test] - fn unused_variable_in_record_field() { - check_fix( - r#" -struct S { field : u32 } -fn main() { - let s = S { field : 2 }; - let S { field: $0x } = s -} -"#, - r#" -struct S { field : u32 } -fn main() { - let s = S { field : 2 }; - let S { field: _x } = s -} -"#, - ); - } - - #[test] - fn unused_variable_in_shorthand_record_field() { - check_fix( - r#" -struct S { field : u32 } -fn main() { - let s = S { field : 2 }; - let S { $0field } = s -} -"#, - r#" -struct S { field : u32 } -fn main() { - let s = S { field : 2 }; - let S { field: _field } = s -} -"#, - ); - } - - #[test] - fn crate_attrs_lint_smoke_test() { - check_diagnostics( - r#" -//- /lib.rs crate:foo crate-attr:deny(unused_variables) -fn main() { - let x = 2; - //^ 💡 error: unused variable -} -"#, - ); - } - - #[test] - fn crate_attrs_should_not_override_lints_in_source() { - check_diagnostics( - r#" -//- /lib.rs crate:foo crate-attr:allow(unused_variables) -#![deny(unused_variables)] -fn main() { - let x = 2; - //^ 💡 error: unused variable -} -"#, - ); - } - - #[test] - fn crate_attrs_should_preserve_lint_order() { - check_diagnostics( - r#" -//- /lib.rs crate:foo crate-attr:allow(unused_variables) crate-attr:warn(unused_variables) -fn main() { - let x = 2; - //^ 💡 warn: unused variable -} -"#, - ); - } -}
diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 26e4a84..8ba59ed 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs
@@ -64,9 +64,7 @@ pub(crate) mod missing_lifetime; pub(crate) mod missing_match_arms; pub(crate) mod missing_unsafe; - pub(crate) mod moved_out_of_ref; pub(crate) mod mut_ref_in_imm_ref_pat; - pub(crate) mod mutability_errors; pub(crate) mod mutable_ref; pub(crate) mod no_such_field; pub(crate) mod non_exhaustive_let; @@ -103,7 +101,6 @@ pub(crate) mod unresolved_method; pub(crate) mod unresolved_module; pub(crate) mod unused_must_use; - pub(crate) mod unused_variables; pub(crate) mod yield_outside_coroutine; // The handlers below are unusual, the implement the diagnostics as well. @@ -286,7 +283,6 @@ pub prefer_prelude: bool, pub prefer_absolute: bool, pub term_search_fuel: u64, - pub term_search_borrowck: bool, pub show_rename_conflicts: bool, } @@ -315,7 +311,6 @@ prefer_prelude: true, prefer_absolute: false, term_search_fuel: 400, - term_search_borrowck: true, show_rename_conflicts: true, } } @@ -435,9 +430,7 @@ m.diagnostics(db, &mut diags, config.style_lints); } } - None => { - handlers::unlinked_file::unlinked_file(&ctx, &mut res, editioned_file_id.file_id(db)) - } + None => handlers::unlinked_file::unlinked_file(&ctx, &mut res, editioned_file_id), } for diag in diags { @@ -484,13 +477,8 @@ AnyDiagnostic::MissingFields(d) => handlers::missing_fields::missing_fields(&ctx, &d), AnyDiagnostic::MissingMatchArms(d) => handlers::missing_match_arms::missing_match_arms(&ctx, &d), AnyDiagnostic::MissingUnsafe(d) => handlers::missing_unsafe::missing_unsafe(&ctx, &d), - AnyDiagnostic::MovedOutOfRef(d) => handlers::moved_out_of_ref::moved_out_of_ref(&ctx, &d), AnyDiagnostic::MutRefInImmRefPat(d) => handlers::mut_ref_in_imm_ref_pat::mut_ref_in_imm_ref_pat(&ctx, &d), AnyDiagnostic::MutableRefBinding(d) => handlers::mutable_ref::mutable_ref_binding(&ctx, &d), - AnyDiagnostic::NeedMut(d) => match handlers::mutability_errors::need_mut(&ctx, &d) { - Some(it) => it, - None => continue, - }, AnyDiagnostic::NonExhaustiveLet(d) => handlers::non_exhaustive_let::non_exhaustive_let(&ctx, &d), AnyDiagnostic::NonExhaustiveRecordExpr(d) => { handlers::non_exhaustive_record_expr::non_exhaustive_record_expr(&ctx, &d) @@ -524,14 +512,6 @@ AnyDiagnostic::UnresolvedMethodCall(d) => handlers::unresolved_method::unresolved_method(&ctx, &d), AnyDiagnostic::UnresolvedModule(d) => handlers::unresolved_module::unresolved_module(&ctx, &d), AnyDiagnostic::UnusedMustUse(d) => handlers::unused_must_use::unused_must_use(&ctx, &d), - AnyDiagnostic::UnusedMut(d) => match handlers::mutability_errors::unused_mut(&ctx, &d) { - Some(it) => it, - None => continue, - }, - AnyDiagnostic::UnusedVariable(d) => match handlers::unused_variables::unused_variables(&ctx, &d) { - Some(it) => it, - None => continue, - }, AnyDiagnostic::BreakOutsideOfLoop(d) => handlers::break_outside_of_loop::break_outside_of_loop(&ctx, &d), AnyDiagnostic::MismatchedTupleStructPatArgCount(d) => handlers::mismatched_arg_count::mismatched_tuple_struct_pat_arg_count(&ctx, &d), AnyDiagnostic::RemoveTrailingReturn(d) => match handlers::remove_trailing_return::remove_trailing_return(&ctx, &d) {
diff --git a/crates/ide-ssr/src/resolving.rs b/crates/ide-ssr/src/resolving.rs index 3dbba0f..9d60792 100644 --- a/crates/ide-ssr/src/resolving.rs +++ b/crates/ide-ssr/src/resolving.rs
@@ -155,7 +155,7 @@ fn path_contains_placeholder(&self, path: &ast::Path) -> bool { if let Some(segment) = path.segment() && let Some(name_ref) = segment.name_ref() - && self.placeholders_by_stand_in.contains_key(name_ref.text().as_str()) + && self.placeholders_by_stand_in.contains_key(name_ref.text()) { return true; }
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index 70d05cd..2f29fc3 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs
@@ -282,7 +282,7 @@ ast::Comment(comment) => TextSize::try_from(comment.prefix().len()).ok(), ast::String(string) => { doc_token.parent_ancestors().find_map(ast::Attr::cast).filter(|attr| attr.simple_name().as_deref() == Some("doc"))?; - if doc_token.parent_ancestors().find_map(ast::MacroCall::cast).filter(|mac| mac.path().and_then(|p| p.segment()?.name_ref()).as_ref().map(|n| n.text()).as_deref() == Some("include_str")).is_some() { + if doc_token.parent_ancestors().find_map(ast::MacroCall::cast).filter(|mac| mac.path().and_then(|p| p.segment()?.name_ref()).as_ref().map(|n| n.text()) == Some("include_str")).is_some() { return None; } string.open_quote_text_range().map(|it| it.len()) @@ -387,7 +387,7 @@ let (mut web_url, mut local_url) = get_doc_base_urls(db, target, target_dir, sysroot); - let append_mod = !matches!(def, Definition::Macro(m) if m.is_macro_export(db)); + let append_mod = !matches!(def, Definition::Macro(m) if m.attrs(db).is_macro_export()); if append_mod && let Some(path) = mod_path_of_def(db, target) { web_url = join_url(web_url, &path); local_url = join_url(local_url, &path);
diff --git a/crates/ide/src/file_structure.rs b/crates/ide/src/file_structure.rs index 21254fc..1a85342 100644 --- a/crates/ide/src/file_structure.rs +++ b/crates/ide/src/file_structure.rs
@@ -106,7 +106,7 @@ Some(StructureNode { parent: None, - label: name.text().to_string(), + label: name.text().to_owned(), navigation_range: name.syntax().text_range(), node_range: node.syntax().text_range(), kind,
diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index f26b992..f70783f 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs
@@ -509,7 +509,7 @@ Some(if it >= 10 { format!("{it} ({it:#X})") } else { format!("{it}") }) } Err(err) => { - let res = it.value(db).map(|it| format!("{it:?}")); + let res = it.value(db).map(|it| it.to_string()); if env::var_os("RA_DEV").is_some() { let res = res.as_deref().unwrap_or(""); Some(format!(
diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 89f1cf2..9dbf113 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs
@@ -5620,6 +5620,30 @@ This is a doc "#]], ); + // const eval failed test + check( + r#" +#[repr(u8)] +enum E { + A$0 = {}, +} +"#, + expect![[r#" + *A* + + ```rust + ra_test_fixture::E + ``` + + ```rust + A = {} + ``` + + --- + + size = 1, align = 1, no Drop + "#]], + ); } #[test] @@ -9508,7 +9532,7 @@ *x* ```rust - x: impl Tr<{const}> + ?Sized + x: impl Tr<0> + ?Sized ``` ---
diff --git a/crates/ide/src/inlay_hints/lifetime.rs b/crates/ide/src/inlay_hints/lifetime.rs index 7a8a6eb..89a2d9f 100644 --- a/crates/ide/src/inlay_hints/lifetime.rs +++ b/crates/ide/src/inlay_hints/lifetime.rs
@@ -204,7 +204,7 @@ mut is_trivial: bool, ) -> Option<()> { let is_elided = |lt: &Option<ast::Lifetime>| match lt { - Some(lt) => matches!(lt.text().as_str(), "'_"), + Some(lt) => matches!(lt.text(), "'_"), None => true, }; let self_param = self_param.and_then(|it| { @@ -298,12 +298,12 @@ potential_lt_refs.for_each(|(name, ..)| { let name = match name { Some(it) if config.param_names_for_lifetime_elision_hints => { - if let Some(c) = used_names.get_mut(it.text().as_str()) { + if let Some(c) = used_names.get_mut(it.text()) { *c += 1; - format_smolstr!("'{}{c}", it.text().as_str()) + format_smolstr!("'{}{c}", it.text()) } else { - used_names.insert(it.text().as_str().into(), 0); - format_smolstr!("'{}", it.text().as_str()) + used_names.insert(it.text().into(), 0); + format_smolstr!("'{}", it.text()) } } _ => gen_idx_name(), @@ -316,7 +316,7 @@ let output = match potential_lt_refs.as_slice() { [(_, _, lifetime, _), ..] if self_param.is_some() || potential_lt_refs.len() == 1 => { match lifetime { - Some(lt) => match lt.text().as_str() { + Some(lt) => match lt.text() { "'_" => allocated_lifetimes.first().cloned(), "'static" => None, name => Some(name.into()),
diff --git a/crates/ide/src/inlay_hints/param_name.rs b/crates/ide/src/inlay_hints/param_name.rs index 5da8f2e..fcf1304 100644 --- a/crates/ide/src/inlay_hints/param_name.rs +++ b/crates/ide/src/inlay_hints/param_name.rs
@@ -294,7 +294,7 @@ debug_assert!(!argument.is_empty()); debug_assert!(!param_name.is_empty()); let param_name = param_name.split('_'); - let argument = argument.iter().flat_map(|it| it.text_non_mutable().split('_')); + let argument = argument.iter().flat_map(|it| it.text().split('_')); let argument = argument.map(|it| it.strip_prefix("r#").unwrap_or(it)); let prefix_match = zip(argument.clone(), param_name.clone()) @@ -313,7 +313,7 @@ let receiver = method_call_expr.receiver().and_then(|expr| get_segment_representation(&expr)); let name_ref = method_call_expr.name_ref()?; - if INSIGNIFICANT_METHOD_NAMES.contains(&name_ref.text().as_str()) { + if INSIGNIFICANT_METHOD_NAMES.contains(&name_ref.text()) { return receiver; } Some(Either::Left(match receiver { @@ -375,16 +375,16 @@ ) -> bool { (|| match sema.resolve_path(path)? { hir::PathResolution::Def(hir::ModuleDef::Adt(_)) => { - Some(to_lower_snake_case(&path.segment()?.name_ref()?.text()) == param_name) + Some(to_lower_snake_case(path.segment()?.name_ref()?.text()) == param_name) } hir::PathResolution::Def(hir::ModuleDef::Function(_) | hir::ModuleDef::EnumVariant(_)) => { - if to_lower_snake_case(&path.segment()?.name_ref()?.text()) == param_name { + if to_lower_snake_case(path.segment()?.name_ref()?.text()) == param_name { return Some(true); } let qual = path.qualifier()?; match sema.resolve_path(&qual)? { hir::PathResolution::Def(hir::ModuleDef::Adt(_)) => { - Some(to_lower_snake_case(&qual.segment()?.name_ref()?.text()) == param_name) + Some(to_lower_snake_case(qual.segment()?.name_ref()?.text()) == param_name) } _ => None, }
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index c4a3ec1..bd09cb1 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs
@@ -65,17 +65,16 @@ use cfg::CfgOptions; use fetch_crates::CrateInfo; use hir::{ChangeWithProcMacros, EditionedFileId, crate_def_map, sym}; -use ide_db::base_db::relevant_crates; -use ide_db::base_db::salsa::Durability; -use ide_db::line_index; -use ide_db::ra_fixture::RaFixtureAnalysis; use ide_db::{ FxHashMap, FxIndexSet, base_db::{ AbsPathBuf, CrateOrigin, CrateWorkspaceData, Env, FileSet, SourceDatabase, VfsPath, - salsa::{Cancelled, Database}, + relevant_crates, + salsa::{Cancelled, Database, Durability}, }, - prime_caches, symbol_index, + line_index, prime_caches, + ra_fixture::RaFixtureAnalysis, + symbol_index, }; use macros::UpmapFromRaFixture; use syntax::{AstNode, SourceFile, ast};
diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index 125b2f4..c3d620a 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs
@@ -190,7 +190,7 @@ kind: SymbolKind, ) -> UpmappingResult<NavigationTarget> { let name = - value.name().map(|it| Symbol::intern(&it.text())).unwrap_or_else(|| sym::underscore); + value.name().map(|it| Symbol::intern(it.text())).unwrap_or_else(|| sym::underscore); orig_range_with_focus(db, file_id, value.syntax(), value.name()).map( |(FileRange { file_id, range: full_range }, focus_range)| {
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index bb79fc2..2bf39ec 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs
@@ -3358,4 +3358,22 @@ "#]], ); } + + #[test] + fn pub_macro_2_scope() { + check( + r#" +//- /foo.rs crate:foo +pub macro m$0() {} + +//- /bar.rs new_source_root:local crate:bar deps:foo +foo::m!(); + "#, + expect![[r#" + m Macro FileId(0) 0..16 10..11 + + FileId(1) 5..6 + "#]], + ); + } }
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html b/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html index 982e89d..10a9478 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_block_mod_items.html
@@ -41,7 +41,7 @@ .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } </style> -<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">foo</span> <span class="brace">{</span> +<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">foo</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="punctuation">$</span>foo<span class="colon">:</span>ident<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">></span> <span class="brace">{</span> <span class="keyword">mod</span> y <span class="brace">{</span> <span class="keyword">pub</span> <span class="keyword">struct</span> <span class="punctuation">$</span>foo<span class="semicolon">;</span> @@ -49,9 +49,9 @@ <span class="brace">}</span><span class="semicolon">;</span> <span class="brace">}</span> <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> - <span class="macro public">foo</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="struct declaration macro public">Foo</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">foo</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="struct declaration macro public">Foo</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="keyword">mod</span> <span class="module declaration">module</span> <span class="brace">{</span> - <span class="macro public">foo</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="struct declaration macro public">Bar</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">foo</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="struct declaration macro public">Bar</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="keyword">fn</span> <span class="function declaration">func</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="module">y</span><span class="operator">::</span><span class="struct public">Bar</span><span class="parenthesis">)</span> <span class="brace">{</span> <span class="keyword">mod</span> <span class="module declaration">inner</span> <span class="brace">{</span> <span class="keyword">struct</span> <span class="struct declaration">Innerest</span><span class="angle"><</span><span class="keyword const">const</span> <span class="const_param const declaration">C</span><span class="colon">:</span> <span class="builtin_type">usize</span><span class="angle">></span> <span class="brace">{</span> <span class="field declaration">field</span><span class="colon">:</span> <span class="bracket">[</span><span class="builtin_type">u32</span><span class="semicolon">;</span> <span class="brace">{</span><span class="const_param const">C</span><span class="brace">}</span><span class="bracket">]</span><span class="comma">,</span> <span class="field declaration">field2</span><span class="colon">:</span> <span class="punctuation">&</span><span class="struct">Innerest</span> <span class="brace">}</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_const.html b/crates/ide/src/syntax_highlighting/test_data/highlight_const.html index 5d89147..6280ed2 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_const.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_const.html
@@ -41,7 +41,7 @@ .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } </style> -<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">id</span> <span class="brace">{</span> +<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">id</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">></span> <span class="brace">{</span> <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span> <span class="brace">}</span><span class="semicolon">;</span> @@ -53,7 +53,7 @@ <span class="keyword const">const</span> <span class="brace">{</span> <span class="keyword const">const</span> <span class="punctuation">|</span><span class="punctuation">|</span> <span class="brace">{</span><span class="brace">}</span> <span class="brace">}</span> - <span class="macro public">id</span><span class="macro_bang">!</span><span class="parenthesis">(</span> + <span class="macro">id</span><span class="macro_bang">!</span><span class="parenthesis">(</span> <span class="constant const macro">CONST_ITEM</span><span class="semicolon macro">;</span> <span class="const_param const macro">CONST_PARAM</span><span class="semicolon macro">;</span> <span class="keyword const macro">const</span> <span class="brace macro">{</span> @@ -74,7 +74,7 @@ <span class="keyword const">const</span> <span class="keyword">fn</span> <span class="function associated const declaration static trait">assoc_const_fn</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> <span class="brace">}</span> -<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">unsafe_deref</span> <span class="brace">{</span> +<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">unsafe_deref</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">></span> <span class="brace">{</span> <span class="punctuation">*</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="keyword">as</span> <span class="punctuation">*</span><span class="keyword const">const</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span> <span class="brace">}</span><span class="semicolon">;</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_deprecated.html b/crates/ide/src/syntax_highlighting/test_data/highlight_deprecated.html index 5287aff..6cbc7f0 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_deprecated.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_deprecated.html
@@ -45,7 +45,7 @@ <span class="keyword">use</span> <span class="keyword deprecated public">crate</span> <span class="keyword">as</span> <span class="punctuation">_</span><span class="semicolon">;</span> <span class="keyword">extern</span> <span class="keyword">crate</span> <span class="crate_root deprecated library">bar</span><span class="semicolon">;</span> <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">deprecated</span><span class="attribute_bracket attribute">]</span> -<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration deprecated public">macro_</span> <span class="brace">{</span> +<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration deprecated">macro_</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">></span> <span class="brace">{</span><span class="brace">}</span><span class="semicolon">;</span> <span class="brace">}</span> <span class="attribute_bracket attribute">#</span><span class="attribute_bracket attribute">[</span><span class="builtin_attr attribute">deprecated</span><span class="attribute_bracket attribute">]</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html index dcfe4dd..c95b36a 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -151,10 +151,10 @@ <span class="brace">}</span> <span class="comment documentation">/// ```</span> -<span class="comment documentation">/// </span><span class="keyword injected">macro_rules</span><span class="macro_bang injected">!</span><span class="none injected"> </span><span class="macro declaration injected public">noop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="parenthesis injected">(</span><span class="punctuation injected">$</span><span class="none injected">expr</span><span class="colon injected">:</span><span class="none injected">expr</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="operator injected">=</span><span class="operator injected">></span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="punctuation injected">$</span><span class="none injected">expr</span><span class="none injected"> </span><span class="brace injected">}</span><span class="brace injected">}</span> -<span class="comment documentation">/// </span><span class="macro injected public">noop</span><span class="macro_bang injected">!</span><span class="parenthesis injected">(</span><span class="numeric_literal injected macro">1</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> +<span class="comment documentation">/// </span><span class="keyword injected">macro_rules</span><span class="macro_bang injected">!</span><span class="none injected"> </span><span class="macro declaration injected">noop</span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="parenthesis injected">(</span><span class="punctuation injected">$</span><span class="none injected">expr</span><span class="colon injected">:</span><span class="none injected">expr</span><span class="parenthesis injected">)</span><span class="none injected"> </span><span class="operator injected">=</span><span class="operator injected">></span><span class="none injected"> </span><span class="brace injected">{</span><span class="none injected"> </span><span class="punctuation injected">$</span><span class="none injected">expr</span><span class="none injected"> </span><span class="brace injected">}</span><span class="brace injected">}</span> +<span class="comment documentation">/// </span><span class="macro injected">noop</span><span class="macro_bang injected">!</span><span class="parenthesis injected">(</span><span class="numeric_literal injected macro">1</span><span class="parenthesis injected">)</span><span class="semicolon injected">;</span> <span class="comment documentation">/// ```</span> -<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">noop</span> <span class="brace">{</span> +<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">></span> <span class="brace">{</span> <span class="punctuation">$</span>expr <span class="brace">}</span>
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html index 6366cba..a6a51f6 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html
@@ -49,22 +49,22 @@ <span class="keyword">use</span> <span class="keyword public">super</span><span class="operator">::</span><span class="punctuation">*</span><span class="semicolon">;</span> <span class="brace">}</span> -<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">void</span> <span class="brace">{</span> +<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">void</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">></span> <span class="brace">{</span>discard<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span><span class="brace">}</span> <span class="brace">}</span> <span class="keyword">struct</span> <span class="struct declaration">__</span> <span class="keyword">where</span> <span class="self_type_keyword">Self</span><span class="colon">:</span><span class="semicolon">;</span> <span class="keyword">fn</span> <span class="function declaration">__</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="unresolved_reference">Self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">Self</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">Self</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// edition dependent</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">try</span> <span class="none macro">async</span> <span class="none macro">await</span> <span class="none macro">gen</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">try</span> <span class="none macro">async</span> <span class="none macro">await</span> <span class="none macro">gen</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// edition and context dependent</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">dyn</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">dyn</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// builtin custom syntax</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// contextual</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// reserved</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis">)</span><span class="semicolon">;</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span>'static 'self 'unsafe<span class="parenthesis">)</span></code></pre> \ No newline at end of file +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span>'static 'self 'unsafe<span class="parenthesis">)</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html index a89e819..ff2b2c3 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html
@@ -49,22 +49,22 @@ <span class="keyword">use</span> <span class="keyword public">super</span><span class="operator">::</span><span class="punctuation">*</span><span class="semicolon">;</span> <span class="brace">}</span> -<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">void</span> <span class="brace">{</span> +<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">void</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">></span> <span class="brace">{</span>discard<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span><span class="brace">}</span> <span class="brace">}</span> <span class="keyword">struct</span> <span class="struct declaration">__</span> <span class="keyword">where</span> <span class="self_type_keyword">Self</span><span class="colon">:</span><span class="semicolon">;</span> <span class="keyword">fn</span> <span class="function declaration">__</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="unresolved_reference">Self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">Self</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">Self</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// edition dependent</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">try</span> <span class="keyword async macro">async</span> <span class="keyword async control macro">await</span> <span class="none macro">gen</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">try</span> <span class="keyword async macro">async</span> <span class="keyword async control macro">await</span> <span class="none macro">gen</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// edition and context dependent</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">dyn</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">dyn</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// builtin custom syntax</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// contextual</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// reserved</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis">)</span><span class="semicolon">;</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span>'static 'self 'unsafe<span class="parenthesis">)</span></code></pre> \ No newline at end of file +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span>'static 'self 'unsafe<span class="parenthesis">)</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html index a89e819..ff2b2c3 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html
@@ -49,22 +49,22 @@ <span class="keyword">use</span> <span class="keyword public">super</span><span class="operator">::</span><span class="punctuation">*</span><span class="semicolon">;</span> <span class="brace">}</span> -<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">void</span> <span class="brace">{</span> +<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">void</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">></span> <span class="brace">{</span>discard<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span><span class="brace">}</span> <span class="brace">}</span> <span class="keyword">struct</span> <span class="struct declaration">__</span> <span class="keyword">where</span> <span class="self_type_keyword">Self</span><span class="colon">:</span><span class="semicolon">;</span> <span class="keyword">fn</span> <span class="function declaration">__</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="unresolved_reference">Self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">Self</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">Self</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// edition dependent</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">try</span> <span class="keyword async macro">async</span> <span class="keyword async control macro">await</span> <span class="none macro">gen</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">try</span> <span class="keyword async macro">async</span> <span class="keyword async control macro">await</span> <span class="none macro">gen</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// edition and context dependent</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">dyn</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">dyn</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// builtin custom syntax</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// contextual</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// reserved</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis">)</span><span class="semicolon">;</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span>'static 'self 'unsafe<span class="parenthesis">)</span></code></pre> \ No newline at end of file +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span>'static 'self 'unsafe<span class="parenthesis">)</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html index aa1500b..0505d41 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html
@@ -49,22 +49,22 @@ <span class="keyword">use</span> <span class="keyword public">super</span><span class="operator">::</span><span class="punctuation">*</span><span class="semicolon">;</span> <span class="brace">}</span> -<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">void</span> <span class="brace">{</span> +<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">void</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">></span> <span class="brace">{</span>discard<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span><span class="brace">}</span> <span class="brace">}</span> <span class="keyword">struct</span> <span class="struct declaration">__</span> <span class="keyword">where</span> <span class="self_type_keyword">Self</span><span class="colon">:</span><span class="semicolon">;</span> <span class="keyword">fn</span> <span class="function declaration">__</span><span class="parenthesis">(</span><span class="punctuation">_</span><span class="colon">:</span> <span class="unresolved_reference">Self</span><span class="parenthesis">)</span> <span class="brace">{</span><span class="brace">}</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">Self</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">Self</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// edition dependent</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">try</span> <span class="keyword async macro">async</span> <span class="keyword async control macro">await</span> <span class="keyword macro">gen</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">try</span> <span class="keyword async macro">async</span> <span class="keyword async control macro">await</span> <span class="keyword macro">gen</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// edition and context dependent</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">dyn</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">dyn</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// builtin custom syntax</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">builtin</span> <span class="none macro">offset_of</span> <span class="none macro">format_args</span> <span class="none macro">asm</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// contextual</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="none macro">macro_rules</span><span class="comma macro">,</span> <span class="none macro">union</span><span class="comma macro">,</span> <span class="none macro">default</span><span class="comma macro">,</span> <span class="none macro">raw</span><span class="comma macro">,</span> <span class="none macro">auto</span><span class="comma macro">,</span> <span class="none macro">yeet</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// reserved</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis">)</span><span class="semicolon">;</span> -<span class="macro public">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span>'static 'self 'unsafe<span class="parenthesis">)</span></code></pre> \ No newline at end of file +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="keyword macro">abstract</span> <span class="keyword macro">become</span> <span class="keyword macro">box</span> <span class="keyword macro">do</span> <span class="keyword macro">final</span> <span class="keyword macro">macro</span> <span class="keyword macro">override</span> <span class="keyword macro">priv</span> <span class="keyword macro">typeof</span> <span class="keyword macro">unsized</span> <span class="keyword macro">virtual</span> <span class="keyword control macro">yield</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="macro">void</span><span class="macro_bang">!</span><span class="parenthesis">(</span>'static 'self 'unsafe<span class="parenthesis">)</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html index 740a627..5fc199f 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
@@ -50,34 +50,34 @@ <span class="comma macro proc_macro">,</span><span class="builtin_type macro proc_macro">i32</span> <span class="colon macro proc_macro">:</span><span class="field declaration macro proc_macro public">y</span> <span class="keyword macro proc_macro">pub</span> <span class="brace macro proc_macro">}</span> <span class="struct declaration macro proc_macro">Foo</span> <span class="keyword macro proc_macro">struct</span> <span class="brace">}</span> -<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">def_fn</span> <span class="brace">{</span> +<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">def_fn</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">></span> <span class="brace">{</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="brace">}</span> <span class="brace">}</span> -<span class="macro public">def_fn</span><span class="macro_bang">!</span> <span class="brace">{</span> +<span class="macro">def_fn</span><span class="macro_bang">!</span> <span class="brace">{</span> <span class="keyword macro">fn</span> <span class="function declaration macro">bar</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span> <span class="operator macro">-</span><span class="operator macro">></span> <span class="builtin_type macro">u32</span> <span class="brace macro">{</span> <span class="numeric_literal macro">100</span> <span class="brace macro">}</span> <span class="brace">}</span> -<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">dont_color_me_braces</span> <span class="brace">{</span> +<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">dont_color_me_braces</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">></span> <span class="brace">{</span><span class="numeric_literal">0</span><span class="brace">}</span> <span class="brace">}</span> -<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">noop</span> <span class="brace">{</span> +<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">></span> <span class="brace">{</span> <span class="punctuation">$</span>expr <span class="brace">}</span> <span class="brace">}</span> <span class="comment documentation">/// textually shadow previous definition</span> -<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">noop</span> <span class="brace">{</span> +<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">noop</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="punctuation">$</span>expr<span class="colon">:</span>expr<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">></span> <span class="brace">{</span> <span class="punctuation">$</span>expr <span class="brace">}</span> <span class="brace">}</span> -<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">keyword_frag</span> <span class="brace">{</span> +<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">keyword_frag</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="punctuation">$</span>type<span class="colon">:</span>ty<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">></span> <span class="parenthesis">(</span><span class="punctuation">$</span>type<span class="parenthesis">)</span> <span class="brace">}</span> @@ -91,7 +91,7 @@ <span class="brace">}</span> <span class="brace">}</span> -<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">id</span> <span class="brace">{</span> +<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">id</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">></span> <span class="brace">{</span> <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span> <span class="brace">}</span><span class="semicolon">;</span> @@ -103,10 +103,10 @@ <span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> <span class="keyword">struct</span> <span class="struct declaration">TestLocal</span><span class="semicolon">;</span> <span class="comment">// regression test, TestLocal here used to not resolve</span> - <span class="keyword">let</span> <span class="punctuation">_</span><span class="colon">:</span> <span class="struct">S</span><span class="angle"><</span><span class="macro public">id</span><span class="macro_bang">!</span><span class="bracket">[</span><span class="struct macro">TestLocal</span><span class="bracket">]</span><span class="angle">></span><span class="semicolon">;</span> + <span class="keyword">let</span> <span class="punctuation">_</span><span class="colon">:</span> <span class="struct">S</span><span class="angle"><</span><span class="macro">id</span><span class="macro_bang">!</span><span class="bracket">[</span><span class="struct macro">TestLocal</span><span class="bracket">]</span><span class="angle">></span><span class="semicolon">;</span> <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="parenthesis macro">(</span><span class="numeric_literal macro">92</span><span class="comma macro">,</span><span class="parenthesis macro">)</span><span class="operator macro">.</span><span class="field library macro">0</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">dont_color_me_braces</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">noop</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="macro macro public">noop</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">dont_color_me_braces</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">noop</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="macro macro">noop</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="numeric_literal macro">1</span><span class="parenthesis macro">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="brace">}</span> </code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_raw_string_format_specifiers.html b/crates/ide/src/syntax_highlighting/test_data/highlight_raw_string_format_specifiers.html new file mode 100644 index 0000000..4497e83 --- /dev/null +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_raw_string_format_specifiers.html
@@ -0,0 +1,49 @@ + +<style> +body { margin: 0; } +pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padding: 0.4em; } + +.lifetime { color: #DFAF8F; font-style: italic; } +.label { color: #DFAF8F; font-style: italic; } +.comment { color: #7F9F7F; } +.documentation { color: #629755; } +.intra_doc_link { font-style: italic; } +.injected { opacity: 0.65 ; } +.struct, .enum { color: #7CB8BB; } +.enum_variant { color: #BDE0F3; } +.string_literal { color: #CC9393; } +.field { color: #94BFF3; } +.function { color: #93E0E3; } +.parameter { color: #94BFF3; } +.text { color: #DCDCCC; } +.type { color: #7CB8BB; } +.builtin_type { color: #8CD0D3; } +.type_param { color: #DFAF8F; } +.attribute { color: #94BFF3; } +.numeric_literal { color: #BFEBBF; } +.bool_literal { color: #BFE6EB; } +.macro { color: #94BFF3; } +.proc_macro { color: #94BFF3; text-decoration: underline; } +.derive { color: #94BFF3; font-style: italic; } +.module { color: #AFD8AF; } +.value_param { color: #DCDCCC; } +.variable { color: #DCDCCC; } +.format_specifier { color: #CC696B; } +.mutable { text-decoration: underline; } +.escape_sequence { color: #94BFF3; } +.keyword { color: #F0DFAF; font-weight: bold; } +.control { font-style: italic; } +.reference { font-style: italic; font-weight: bold; } +.const { font-weight: bolder; } +.unsafe { color: #BC8383; } +.deprecated { text-decoration: line-through; } + +.invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } +.unresolved_reference { color: #FC5555; text-decoration: wavy underline; } +</style> +<pre><code><span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span> + <span class="keyword">let</span> <span class="variable declaration">here</span> <span class="operator">=</span> <span class="numeric_literal">1</span><span class="semicolon">;</span> + <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">r"backslash \</span><span class="format_specifier">{</span><span class="variable">here</span><span class="format_specifier">}</span><span class="string_literal macro"> arg"</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">r#"hashed \</span><span class="format_specifier">{</span><span class="variable">here</span><span class="format_specifier">}</span><span class="string_literal macro"> arg"#</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"plain </span><span class="format_specifier">{</span><span class="variable">here</span><span class="format_specifier">}</span><span class="string_literal macro"> arg"</span><span class="parenthesis">)</span><span class="semicolon">;</span> +<span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index 4e3822c..b5ace72 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -41,7 +41,7 @@ .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } </style> -<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">println</span> <span class="brace">{</span> +<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">println</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">></span> <span class="parenthesis">(</span><span class="brace">{</span> <span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>io<span class="colon">:</span><span class="colon">:</span>_print<span class="parenthesis">(</span>format_args_nl<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="brace">}</span><span class="parenthesis">)</span> @@ -69,12 +69,12 @@ <span class="brace">}</span> <span class="brace">}</span> -<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">toho</span> <span class="brace">{</span> +<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">toho</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">></span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented"</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">></span> <span class="parenthesis">(</span><span class="punctuation">$</span>crate<span class="colon">:</span><span class="colon">:</span>panic<span class="punctuation">!</span><span class="parenthesis">(</span><span class="string_literal">"not yet implemented: {}"</span><span class="comma">,</span> format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>arg<span class="parenthesis">)</span><span class="punctuation">+</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="brace">}</span> -<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">reuse_twice</span> <span class="brace">{</span> +<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">reuse_twice</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="punctuation">$</span>literal<span class="colon">:</span>literal<span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">></span> <span class="brace">{</span><span class="brace">{</span>stringify<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>literal<span class="parenthesis">)</span><span class="semicolon">;</span> format_args<span class="punctuation">!</span><span class="parenthesis">(</span><span class="punctuation">$</span>literal<span class="parenthesis">)</span><span class="brace">}</span><span class="brace">}</span><span class="semicolon">;</span> <span class="brace">}</span> @@ -95,74 +95,74 @@ <span class="keyword">let</span> <span class="variable declaration">a</span> <span class="operator">=</span> <span class="byte_literal">b'</span><span class="escape_sequence">\xFF</span><span class="byte_literal">'</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// from https://doc.rust-lang.org/std/fmt/index.html</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "Hello"</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "Hello, world!"</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "The number is 1"</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="parenthesis macro">(</span><span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "(3, 4)"</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">value</span><span class="operator macro">=</span><span class="numeric_literal macro">4</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "4"</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "1 2"</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">42</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "0042" with leading zerosV</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "2 1 1 2"</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">argument</span> <span class="operator macro">=</span> <span class="string_literal macro">"test"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "test"</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="none macro">name</span> <span class="operator macro">=</span> <span class="numeric_literal macro">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "2 1"</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">a</span><span class="operator macro">=</span><span class="string_literal macro">"a"</span><span class="comma macro">,</span> <span class="none macro">b</span><span class="operator macro">=</span><span class="char_literal macro">'b'</span><span class="comma macro">,</span> <span class="none macro">c</span><span class="operator macro">=</span><span class="numeric_literal macro">3</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "a 3 b"</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "{2}"</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="none macro">width</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier"><</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier"><</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">></span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">-</span><span class="numeric_literal macro">5</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="none macro">prec</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="none macro">number</span> <span class="operator macro">=</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 fractional digits"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="numeric_literal macro">1234.56</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">></span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 right-aligned characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="none macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "Hello"</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "Hello, world!"</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"The number is </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "The number is 1"</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="parenthesis macro">(</span><span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="numeric_literal macro">4</span><span class="parenthesis macro">)</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "(3, 4)"</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">value</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">value</span><span class="operator macro">=</span><span class="numeric_literal macro">4</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "4"</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "1 2"</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">4</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">42</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "0042" with leading zerosV</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "2 1 1 2"</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">argument</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">argument</span> <span class="operator macro">=</span> <span class="string_literal macro">"test"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "test"</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span> <span class="operator macro">=</span> <span class="numeric_literal macro">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "2 1"</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">a</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">c</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">b</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">a</span><span class="operator macro">=</span><span class="string_literal macro">"a"</span><span class="comma macro">,</span> <span class="variable declaration macro">b</span><span class="operator macro">=</span><span class="char_literal macro">'b'</span><span class="comma macro">,</span> <span class="variable declaration macro">c</span><span class="operator macro">=</span><span class="numeric_literal macro">3</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "a 3 b"</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// => "{2}"</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">width</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="variable declaration macro">width</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier"><</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">-</span><span class="format_specifier"><</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">^</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">></span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">-</span><span class="numeric_literal macro">5</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">1</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="variable">number</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="variable">prec</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="variable declaration macro">prec</span> <span class="operator macro">=</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="variable declaration macro">number</span> <span class="operator macro">=</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 fractional digits"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="numeric_literal macro">1234.56</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">, `</span><span class="format_specifier">{</span><span class="variable">name</span><span class="format_specifier">:</span><span class="format_specifier">></span><span class="numeric_literal">8</span><span class="format_specifier">.</span><span class="format_specifier">*</span><span class="format_specifier">}</span><span class="string_literal macro">` has 3 right-aligned characters"</span><span class="comma macro">,</span> <span class="string_literal macro">"Hello"</span><span class="comma macro">,</span> <span class="numeric_literal macro">3</span><span class="comma macro">,</span> <span class="variable declaration macro">name</span><span class="operator macro">=</span><span class="string_literal macro">"1234.56"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{}"</span> <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"{{}}"</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">{{</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello"</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">{{</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello"</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro">Hello </span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">{{</span><span class="string_literal macro"> Hello</span><span class="escape_sequence">}}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">r"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="string_literal macro">"world"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="comment">// escape sequences</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello</span><span class="escape_sequence">\n</span><span class="string_literal macro">World"</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">\u{48}</span><span class="escape_sequence">\x65</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6F</span><span class="string_literal macro"> World"</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"Hello</span><span class="escape_sequence">\n</span><span class="string_literal macro">World"</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="escape_sequence">\u{48}</span><span class="escape_sequence">\x65</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6C</span><span class="escape_sequence">\x6F</span><span class="string_literal macro"> World"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">"</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x00</span><span class="escape_sequence">\x63</span><span class="invalid_escape_sequence">\xFF</span><span class="escape_sequence">\u{FF}</span><span class="escape_sequence">\n</span><span class="string_literal">"</span><span class="semicolon">;</span> <span class="comment">// invalid non-UTF8 escape sequences</span> <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">b"</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x28</span><span class="escape_sequence">\x00</span><span class="escape_sequence">\x63</span><span class="escape_sequence">\xFF</span><span class="invalid_escape_sequence">\u{FF}</span><span class="escape_sequence">\n</span><span class="string_literal">"</span><span class="semicolon">;</span> <span class="comment">// valid bytes, invalid unicodes</span> <span class="keyword">let</span> <span class="punctuation">_</span> <span class="operator">=</span> <span class="string_literal">c"</span><span class="escape_sequence">\u{FF}</span><span class="escape_sequence">\xFF</span><span class="string_literal">"</span><span class="semicolon">;</span> <span class="comment">// valid bytes, valid unicodes</span> <span class="keyword">let</span> <span class="variable declaration reference">backslash</span> <span class="operator">=</span> <span class="string_literal">r"\\"</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">A</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="none macro">ничоси</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="escape_sequence">\x41</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">A</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">ничоси</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable declaration macro">ничоси</span> <span class="operator macro">=</span> <span class="numeric_literal macro">92</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> "</span><span class="comma macro">,</span> <span class="unresolved_reference macro">thingy</span><span class="comma macro">,</span> <span class="unresolved_reference macro">n2</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="variable">x</span><span class="format_specifier">?</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> "</span><span class="comma macro">,</span> <span class="unresolved_reference macro">thingy</span><span class="comma macro">,</span> <span class="unresolved_reference macro">n2</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="macro default_library library">panic</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"{}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="macro default_library library">panic</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"more </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="macro default_library library">assert</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="bool_literal macro">true</span><span class="comma">,</span> <span class="string_literal macro">"{}"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="macro default_library library">assert</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="bool_literal macro">true</span><span class="comma">,</span> <span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> asdasd"</span><span class="comma macro">,</span> <span class="numeric_literal macro">1</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">toho</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"{}fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">toho</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"{}fmt"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="keyword">let</span> <span class="variable declaration">i</span><span class="colon">:</span> <span class="builtin_type">u64</span> <span class="operator">=</span> <span class="numeric_literal">3</span><span class="semicolon">;</span> <span class="keyword">let</span> <span class="variable declaration">o</span><span class="colon">:</span> <span class="builtin_type">u64</span><span class="semicolon">;</span> <span class="crate_root default_library library">core</span><span class="operator">::</span><span class="module default_library library">arch</span><span class="operator">::</span><span class="macro default_library library unsafe">asm</span><span class="macro_bang unsafe">!</span><span class="parenthesis unsafe">(</span> @@ -175,6 +175,6 @@ <span class="keyword const">const</span> <span class="constant const declaration">CONSTANT</span><span class="colon">:</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="colon">:</span> <span class="keyword">let</span> <span class="keyword">mut</span> <span class="variable declaration mutable">m</span> <span class="operator">=</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="macro default_library library macro">concat</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"{}"</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="string_literal macro">"{}"</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">backslash</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">CONSTANT</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">m</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="comma macro">,</span> <span class="macro default_library library macro">format_args</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="unresolved_reference macro">foo</span><span class="comma macro">,</span> <span class="string_literal macro">"bar"</span><span class="comma macro">,</span> <span class="macro macro public">toho</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="parenthesis">)</span><span class="semicolon">;</span> - <span class="macro public">reuse_twice</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">backslash</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">backslash</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">CONSTANT</span><span class="format_specifier">}</span><span class="string_literal macro"> </span><span class="format_specifier">{</span><span class="variable">m</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="comma macro">,</span> <span class="macro default_library library macro">format_args</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="unresolved_reference macro">foo</span><span class="comma macro">,</span> <span class="string_literal macro">"bar"</span><span class="comma macro">,</span> <span class="macro macro">toho</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="parenthesis macro">)</span><span class="comma macro">,</span> <span class="variable macro reference">backslash</span><span class="parenthesis">)</span><span class="semicolon">;</span> + <span class="macro">reuse_twice</span><span class="macro_bang">!</span><span class="parenthesis">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="variable">backslash</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="parenthesis">)</span><span class="semicolon">;</span> <span class="brace">}</span></code></pre> \ No newline at end of file
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html index 008987d..2e889f8 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -41,12 +41,12 @@ .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } </style> -<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">id</span> <span class="brace">{</span> +<pre><code><span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">id</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="colon">:</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">></span> <span class="brace">{</span> <span class="punctuation">$</span><span class="parenthesis">(</span><span class="punctuation">$</span>tt<span class="parenthesis">)</span><span class="punctuation">*</span> <span class="brace">}</span><span class="semicolon">;</span> <span class="brace">}</span> -<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration public">unsafe_deref</span> <span class="brace">{</span> +<span class="keyword">macro_rules</span><span class="macro_bang">!</span> <span class="macro declaration">unsafe_deref</span> <span class="brace">{</span> <span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="operator">=</span><span class="operator">></span> <span class="brace">{</span> <span class="punctuation">*</span><span class="parenthesis">(</span><span class="punctuation">&</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="keyword">as</span> <span class="punctuation">*</span><span class="keyword const">const</span> <span class="parenthesis">(</span><span class="parenthesis">)</span><span class="parenthesis">)</span> <span class="brace">}</span><span class="semicolon">;</span> @@ -80,14 +80,14 @@ <span class="keyword">let</span> <span class="variable declaration">u</span><span class="colon">:</span> <span class="union">Union</span><span class="semicolon">;</span> <span class="comment">// id should be safe here, but unsafe_deref should not</span> - <span class="macro public">id</span><span class="macro_bang">!</span> <span class="brace">{</span> - <span class="keyword macro unsafe">unsafe</span> <span class="brace macro">{</span> <span class="macro macro public unsafe">unsafe_deref</span><span class="macro_bang macro unsafe">!</span><span class="parenthesis macro unsafe">(</span><span class="parenthesis macro unsafe">)</span> <span class="brace macro">}</span> + <span class="macro">id</span><span class="macro_bang">!</span> <span class="brace">{</span> + <span class="keyword macro unsafe">unsafe</span> <span class="brace macro">{</span> <span class="macro macro unsafe">unsafe_deref</span><span class="macro_bang macro unsafe">!</span><span class="parenthesis macro unsafe">(</span><span class="parenthesis macro unsafe">)</span> <span class="brace macro">}</span> <span class="brace">}</span><span class="semicolon">;</span> <span class="keyword unsafe">unsafe</span> <span class="brace">{</span> <span class="comment">// unsafe macro calls</span> - <span class="macro public unsafe">unsafe_deref</span><span class="macro_bang unsafe">!</span><span class="parenthesis unsafe">(</span><span class="parenthesis unsafe">)</span><span class="semicolon">;</span> - <span class="macro public unsafe">id</span><span class="macro_bang unsafe">!</span> <span class="brace unsafe">{</span> <span class="macro macro public unsafe">unsafe_deref</span><span class="macro_bang macro unsafe">!</span><span class="parenthesis macro unsafe">(</span><span class="parenthesis macro unsafe">)</span> <span class="brace unsafe">}</span><span class="semicolon">;</span> + <span class="macro unsafe">unsafe_deref</span><span class="macro_bang unsafe">!</span><span class="parenthesis unsafe">(</span><span class="parenthesis unsafe">)</span><span class="semicolon">;</span> + <span class="macro unsafe">id</span><span class="macro_bang unsafe">!</span> <span class="brace unsafe">{</span> <span class="macro macro unsafe">unsafe_deref</span><span class="macro_bang macro unsafe">!</span><span class="parenthesis macro unsafe">(</span><span class="parenthesis macro unsafe">)</span> <span class="brace unsafe">}</span><span class="semicolon">;</span> <span class="comment">// unsafe fn and method calls</span> <span class="function unsafe">unsafe_fn</span><span class="parenthesis unsafe">(</span><span class="parenthesis unsafe">)</span><span class="semicolon">;</span>
diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 6cb323b..7a3d71f 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs
@@ -453,6 +453,23 @@ } #[test] +fn test_raw_string_format_specifiers() { + check_highlighting( + r####" +//- minicore: fmt +fn main() { + let here = 1; + format_args!(r"backslash \{here} arg"); + format_args!(r#"hashed \{here} arg"#); + format_args!("plain {here} arg"); +} +"####, + expect_file!["./test_data/highlight_raw_string_format_specifiers.html"], + false, + ); +} + +#[test] fn test_string_highlighting() { // The format string detection is based on macro-expansion, // thus, we have to copy the macro definition from `std`
diff --git a/crates/ide/src/view_crate_graph.rs b/crates/ide/src/view_crate_graph.rs index ecfdd09..cb75c59 100644 --- a/crates/ide/src/view_crate_graph.rs +++ b/crates/ide/src/view_crate_graph.rs
@@ -1,9 +1,10 @@ use dot::{Id, LabelText}; -use ide_db::base_db::all_crates; -use ide_db::base_db::salsa::plumbing::AsId; use ide_db::{ FxHashMap, RootDatabase, - base_db::{BuiltCrateData, BuiltDependency, Crate, ExtraCrateData, SourceDatabase}, + base_db::{ + BuiltCrateData, BuiltDependency, Crate, ExtraCrateData, SourceDatabase, all_crates, + salsa::plumbing::AsId, + }, }; // Feature: View Crate Graph
diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs index 0623e7e..0305733 100644 --- a/crates/parser/src/grammar.rs +++ b/crates/parser/src/grammar.rs
@@ -343,6 +343,12 @@ name_r(p, TokenSet::EMPTY); } +fn name_any_identifier(p: &mut Parser<'_>) { + let m = p.start(); + p.bump_remap_any_ident(); + m.complete(p, NAME); +} + fn name_ref_or_self(p: &mut Parser<'_>) { if matches!(p.current(), T![ident] | T![self]) { let m = p.start();
diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs index 3f341c2..3df47bf 100644 --- a/crates/parser/src/grammar/expressions.rs +++ b/crates/parser/src/grammar/expressions.rs
@@ -36,8 +36,7 @@ /// It needs to be parsed with lower precedence than `&&`, so that /// `if let true = true && false` is parsed as `if (let true = true) && (true)` /// and not `if let true = (true && true)`. -fn expr_let(p: &mut Parser<'_>) { - let r = Restrictions { forbid_structs: true, prefer_stmt: false }; +fn expr_let(p: &mut Parser<'_>, r: Restrictions) { expr_bp(p, None, r, 5); } @@ -299,8 +298,16 @@ // match 1.. { _ => () }; // match a.b()..S { _ => () }; // } + + // test closure_postfix_range_method_call + // fn foo() { + // || 1.. .method(); + // || 1.. .field; + // } + let has_access_after = p.at(T![.]) && p.nth_at(1, SyntaxKind::IDENT); + let struct_forbidden = r.forbid_structs && p.at(T!['{']); let has_trailing_expression = - p.at_ts(EXPR_FIRST) && !(r.forbid_structs && p.at(T!['{'])); + p.at_ts(EXPR_FIRST) && !has_access_after && !struct_forbidden; if !has_trailing_expression { // no RHS lhs = m.complete(p, RANGE_EXPR); @@ -382,6 +389,7 @@ // } let has_access_after = p.at(T![.]) && p.nth_at(1, SyntaxKind::IDENT); let struct_forbidden = r.forbid_structs && p.at(T!['{']); + // NOTE: Similar logic `is_range` flag in expr_bp() if p.at_ts(EXPR_FIRST) && !has_access_after && !struct_forbidden { expr_bp(p, None, r, 2); }
diff --git a/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs index 7ce1fc2..275a79b 100644 --- a/crates/parser/src/grammar/expressions/atom.rs +++ b/crates/parser/src/grammar/expressions/atom.rs
@@ -88,7 +88,7 @@ T!['('] => tuple_expr(p), T!['['] => array_expr(p), T![if] => if_expr(p), - T![let] => let_expr(p), + T![let] => let_expr(p, r), T![_] => { // test destructuring_assignment_wildcard_pat // fn foo() { @@ -291,10 +291,8 @@ while !p.at(EOF) && !p.at(T![')']) { let m = p.start(); if p.current().is_any_identifier() && p.nth_at(1, T![=]) && !p.nth_at(2, T![=]) { - let m = p.start(); - p.bump_any(); + name_any_identifier(p); p.bump(T![=]); - m.complete(p, FORMAT_ARGS_ARG_NAME); } if expr(p).is_none() { m.abandon(p); @@ -376,8 +374,12 @@ } // Parse operand names - if p.at(T![ident]) && p.nth_at(1, T![=]) { - name(p); + if p.current().is_any_identifier() && p.nth_at(1, T![=]) { + // test asm_keyword_name + // fn foo() { + // builtin # asm("", fn = const 0); + // } + name_any_identifier(p); p.bump(T![=]); allow_templates = false; } @@ -719,12 +721,12 @@ // if let Some(_) = None && true {} // while 1 == 5 && (let None = None) {} // } -fn let_expr(p: &mut Parser<'_>) -> CompletedMarker { +fn let_expr(p: &mut Parser<'_>, r: Restrictions) -> CompletedMarker { let m = p.start(); p.bump(T![let]); patterns::pattern(p); p.expect(T![=]); - expr_let(p); + expr_let(p, r); m.complete(p, LET_EXPR) } @@ -860,6 +862,12 @@ if p.at(T![=]) { p.error("expected expression"); } else { + // test guard_let_struct + // fn foo() { + // match () { + // () if let Foo {} = Foo {} => {} + // } + // } expr(p); } m.complete(p, MATCH_GUARD)
diff --git a/crates/parser/src/parser.rs b/crates/parser/src/parser.rs index 57c501e..11a3a53 100644 --- a/crates/parser/src/parser.rs +++ b/crates/parser/src/parser.rs
@@ -154,6 +154,12 @@ true } + /// This consumes the next token as an identifier, even if it is a strict keyword. + pub(crate) fn bump_remap_any_ident(&mut self) { + assert!(self.current().is_any_identifier()); + self.bump_remap(T![ident]); + } + fn at_composite2(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind) -> bool { self.inp.kind(self.pos + n) == k1 && self.inp.kind(self.pos + n + 1) == k2
diff --git a/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs index 1c4fcf0..5604da5 100644 --- a/crates/parser/src/syntax_kind/generated.rs +++ b/crates/parser/src/syntax_kind/generated.rs
@@ -214,7 +214,6 @@ FN, FN_PTR_TYPE, FORMAT_ARGS_ARG, - FORMAT_ARGS_ARG_NAME, FORMAT_ARGS_EXPR, FOR_BINDER, FOR_EXPR, @@ -402,7 +401,6 @@ | FN | FN_PTR_TYPE | FORMAT_ARGS_ARG - | FORMAT_ARGS_ARG_NAME | FORMAT_ARGS_EXPR | FOR_BINDER | FOR_EXPR
diff --git a/crates/parser/test_data/generated/runner.rs b/crates/parser/test_data/generated/runner.rs index 8104d28..520e736 100644 --- a/crates/parser/test_data/generated/runner.rs +++ b/crates/parser/test_data/generated/runner.rs
@@ -21,6 +21,10 @@ #[test] fn asm_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_expr.rs"); } #[test] + fn asm_keyword_name() { + run_and_expect_no_errors("test_data/parser/inline/ok/asm_keyword_name.rs"); + } + #[test] fn asm_kinds() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_kinds.rs"); } #[test] fn asm_label() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_label.rs"); } @@ -121,6 +125,10 @@ run_and_expect_no_errors("test_data/parser/inline/ok/closure_params.rs"); } #[test] + fn closure_postfix_range_method_call() { + run_and_expect_no_errors("test_data/parser/inline/ok/closure_postfix_range_method_call.rs"); + } + #[test] fn closure_range_method_call() { run_and_expect_no_errors("test_data/parser/inline/ok/closure_range_method_call.rs"); } @@ -336,6 +344,10 @@ #[test] fn global_asm() { run_and_expect_no_errors("test_data/parser/inline/ok/global_asm.rs"); } #[test] + fn guard_let_struct() { + run_and_expect_no_errors("test_data/parser/inline/ok/guard_let_struct.rs"); + } + #[test] fn half_open_range_pat() { run_and_expect_no_errors("test_data/parser/inline/ok/half_open_range_pat.rs"); }
diff --git a/crates/parser/test_data/parser/inline/ok/asm_keyword_name.rast b/crates/parser/test_data/parser/inline/ok/asm_keyword_name.rast new file mode 100644 index 0000000..4c97562 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/asm_keyword_name.rast
@@ -0,0 +1,42 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + ASM_EXPR + BUILTIN_KW "builtin" + WHITESPACE " " + POUND "#" + WHITESPACE " " + ASM_KW "asm" + L_PAREN "(" + LITERAL + STRING "\"\"" + COMMA "," + WHITESPACE " " + ASM_OPERAND_NAMED + NAME + IDENT "fn" + WHITESPACE " " + EQ "=" + WHITESPACE " " + ASM_CONST + CONST_KW "const" + WHITESPACE " " + LITERAL + INT_NUMBER "0" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/asm_keyword_name.rs b/crates/parser/test_data/parser/inline/ok/asm_keyword_name.rs new file mode 100644 index 0000000..a8f1ab2 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/asm_keyword_name.rs
@@ -0,0 +1,3 @@ +fn foo() { + builtin # asm("", fn = const 0); +}
diff --git a/crates/parser/test_data/parser/inline/ok/builtin_expr.rast b/crates/parser/test_data/parser/inline/ok/builtin_expr.rast index 78c3bee..19a84ac 100644 --- a/crates/parser/test_data/parser/inline/ok/builtin_expr.rast +++ b/crates/parser/test_data/parser/inline/ok/builtin_expr.rast
@@ -44,10 +44,10 @@ COMMA "," WHITESPACE " " FORMAT_ARGS_ARG - FORMAT_ARGS_ARG_NAME + NAME IDENT "a" - WHITESPACE " " - EQ "=" + WHITESPACE " " + EQ "=" WHITESPACE " " BIN_EXPR LITERAL
diff --git a/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rast b/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rast new file mode 100644 index 0000000..555d312 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rast
@@ -0,0 +1,53 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + METHOD_CALL_EXPR + CLOSURE_EXPR + PARAM_LIST + PIPE "|" + PIPE "|" + WHITESPACE " " + RANGE_EXPR + LITERAL + INT_NUMBER "1" + DOT2 ".." + WHITESPACE " " + DOT "." + NAME_REF + IDENT "method" + ARG_LIST + L_PAREN "(" + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n " + EXPR_STMT + FIELD_EXPR + CLOSURE_EXPR + PARAM_LIST + PIPE "|" + PIPE "|" + WHITESPACE " " + RANGE_EXPR + LITERAL + INT_NUMBER "1" + DOT2 ".." + WHITESPACE " " + DOT "." + NAME_REF + IDENT "field" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rs b/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rs new file mode 100644 index 0000000..71429be --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/closure_postfix_range_method_call.rs
@@ -0,0 +1,4 @@ +fn foo() { + || 1.. .method(); + || 1.. .field; +}
diff --git a/crates/parser/test_data/parser/inline/ok/format_args_named_arg_keyword.rast b/crates/parser/test_data/parser/inline/ok/format_args_named_arg_keyword.rast index 03bc2ec..50b9208 100644 --- a/crates/parser/test_data/parser/inline/ok/format_args_named_arg_keyword.rast +++ b/crates/parser/test_data/parser/inline/ok/format_args_named_arg_keyword.rast
@@ -23,9 +23,9 @@ COMMA "," WHITESPACE " " FORMAT_ARGS_ARG - FORMAT_ARGS_ARG_NAME - TYPE_KW "type" - EQ "=" + NAME + IDENT "type" + EQ "=" LITERAL INT_NUMBER "1" R_PAREN ")"
diff --git a/crates/parser/test_data/parser/inline/ok/guard_let_struct.rast b/crates/parser/test_data/parser/inline/ok/guard_let_struct.rast new file mode 100644 index 0000000..33754e6 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/guard_let_struct.rast
@@ -0,0 +1,68 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + MATCH_EXPR + MATCH_KW "match" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + MATCH_ARM_LIST + L_CURLY "{" + WHITESPACE "\n " + MATCH_ARM + TUPLE_PAT + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + MATCH_GUARD + IF_KW "if" + WHITESPACE " " + LET_EXPR + LET_KW "let" + WHITESPACE " " + RECORD_PAT + PATH + PATH_SEGMENT + NAME_REF + IDENT "Foo" + WHITESPACE " " + RECORD_PAT_FIELD_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE " " + EQ "=" + WHITESPACE " " + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "Foo" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + R_CURLY "}" + WHITESPACE "\n " + R_CURLY "}" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n"
diff --git a/crates/parser/test_data/parser/inline/ok/guard_let_struct.rs b/crates/parser/test_data/parser/inline/ok/guard_let_struct.rs new file mode 100644 index 0000000..bd84d57 --- /dev/null +++ b/crates/parser/test_data/parser/inline/ok/guard_let_struct.rs
@@ -0,0 +1,5 @@ +fn foo() { + match () { + () if let Foo {} = Foo {} => {} + } +}
diff --git a/crates/project-model/src/build_dependencies.rs b/crates/project-model/src/build_dependencies.rs index 9f84f63..8da0a57 100644 --- a/crates/project-model/src/build_dependencies.rs +++ b/crates/project-model/src/build_dependencies.rs
@@ -462,9 +462,7 @@ cmd.arg(target_dir.as_ref()); } - if let Some(target) = &config.target { - cmd.args(["--target", target]); - } + toolchain::cargo_use_targets(toolchain, &mut cmd, config.target.as_slice()); let mut lockfile_copy = None; if let Some(toolchain) = toolchain { let lockfile_path =
diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index 97375fe..a0a9815 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs
@@ -704,9 +704,18 @@ } if !config.targets.is_empty() { - other_options.extend( - config.targets.iter().flat_map(|it| ["--filter-platform".to_owned(), it.clone()]), - ); + let mut has_json_target = false; + other_options.extend(config.targets.iter().flat_map(|target| { + has_json_target |= target.ends_with(".json"); + ["--filter-platform".to_owned(), target.clone()] + })); + if has_json_target + && config.toolchain_version.as_ref().is_some_and(|version| { + *version >= toolchain::MINIMUM_TOOLCHAIN_VERSION_REQUIRING_JSON_TARGET_SPEC_FLAG + }) + { + other_options.push("-Zjson-target-spec".to_owned()); + } } command.other_options(other_options.clone());
diff --git a/crates/project-model/src/toolchain_info/target_data.rs b/crates/project-model/src/toolchain_info/target_data.rs index b815c0b..18ceac6 100644 --- a/crates/project-model/src/toolchain_info/target_data.rs +++ b/crates/project-model/src/toolchain_info/target_data.rs
@@ -39,6 +39,7 @@ config: QueryConfig<'_>, target: Option<&str>, extra_env: &FxHashMap<String, Option<String>>, + version: Option<&semver::Version>, ) -> anyhow::Result<target::TargetData> { const RUSTC_ARGS: [&str; 2] = ["--print", "target-spec-json"]; let process = |output: String| { @@ -55,9 +56,7 @@ let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env); cmd.env("RUSTC_BOOTSTRAP", "1"); cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS); - if let Some(target) = target { - cmd.args(["--target", target]); - } + toolchain::cargo_use_targets(version, &mut cmd, target.as_slice()); cmd.args(["--", "-Z", "unstable-options"]); match utf8_stdout(&mut cmd) { Ok(output) => return process(output), @@ -95,13 +94,17 @@ let manifest_path = ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap(); let cfg = QueryConfig::Cargo(&sysroot, &manifest_path, &None); - assert!(get(cfg, None, &FxHashMap::default()).is_ok()); + let extra_env = &FxHashMap::default(); + let Ok(version) = super::super::version::get(cfg, extra_env) else { return }; + assert!(get(cfg, None, extra_env, version.as_ref()).is_ok()); } #[test] fn rustc() { let sysroot = Sysroot::empty(); let cfg = QueryConfig::Rustc(&sysroot, env!("CARGO_MANIFEST_DIR").as_ref()); - assert!(get(cfg, None, &FxHashMap::default()).is_ok()); + let extra_env = &FxHashMap::default(); + let Ok(version) = super::super::version::get(cfg, extra_env) else { return }; + assert!(get(cfg, None, extra_env, version.as_ref()).is_ok()); } }
diff --git a/crates/project-model/src/toolchain_info/version.rs b/crates/project-model/src/toolchain_info/version.rs index 357053d..c97a687 100644 --- a/crates/project-model/src/toolchain_info/version.rs +++ b/crates/project-model/src/toolchain_info/version.rs
@@ -7,7 +7,7 @@ use crate::{toolchain_info::QueryConfig, utf8_stdout}; -pub(crate) fn get( +pub fn get( config: QueryConfig<'_>, extra_env: &FxHashMap<String, Option<String>>, ) -> Result<Option<Version>, anyhow::Error> {
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 9002b0c..9010ba8 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs
@@ -316,13 +316,18 @@ let target_data = Builder::new() .name("ProjectWorkspace::target_data".to_owned()) .spawn_scoped(s, || { - target_data::get(toolchain_config, targets.first().map(Deref::deref), extra_env) - .inspect_err(|e| { - tracing::error!(%e, - "failed fetching data layout for \ - {cargo_toml:?} workspace" - ) - }) + target_data::get( + toolchain_config, + targets.first().map(Deref::deref), + extra_env, + toolchain.as_ref(), + ) + .inspect_err(|e| { + tracing::error!(%e, + "failed fetching data layout for \ + {cargo_toml:?} workspace" + ) + }) }) .expect("failed to spawn thread"); @@ -495,7 +500,12 @@ rustc_cfg::get(query_config, targets.first().map(Deref::deref), &config.extra_env) }); let data_layout = s.spawn(|| { - target_data::get(query_config, targets.first().map(Deref::deref), &config.extra_env) + target_data::get( + query_config, + targets.first().map(Deref::deref), + &config.extra_env, + toolchain.as_ref(), + ) }); let loaded_sysroot = s.spawn(|| { if let Some(sysroot_project) = sysroot_project { @@ -564,7 +574,8 @@ let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env) .unwrap_or_default(); let rustc_cfg = rustc_cfg::get(query_config, None, &config.extra_env); - let target_data = target_data::get(query_config, None, &config.extra_env); + let target_data = + target_data::get(query_config, None, &config.extra_env, toolchain.as_ref()); let loaded_sysroot = sysroot.load_workspace( &RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index ae0c957..5ec0a89 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml
@@ -29,7 +29,7 @@ indexmap.workspace = true itertools.workspace = true scip = "0.7.1" -lsp-types = { version = "0.10.0", package = "gen-lsp-types", features = ["url"] } +lsp-types = { version = "0.11.0", package = "gen-lsp-types", features = ["url"] } parking_lot = "0.12.4" xflags = "0.3.2" oorandom = "11.1.5"
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 6bd27c2..c3678e0 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs
@@ -72,7 +72,7 @@ flags::RustAnalyzerCmd::Symbols(cmd) => cmd.run()?, flags::RustAnalyzerCmd::Highlight(cmd) => cmd.run()?, flags::RustAnalyzerCmd::AnalysisStats(cmd) => cmd.run(verbosity)?, - flags::RustAnalyzerCmd::Diagnostics(cmd) => cmd.run()?, + flags::RustAnalyzerCmd::Diagnostics(cmd) => cmd.run(verbosity)?, flags::RustAnalyzerCmd::UnresolvedReferences(cmd) => cmd.run()?, flags::RustAnalyzerCmd::Ssr(cmd) => cmd.run()?, flags::RustAnalyzerCmd::Search(cmd) => cmd.run()?, @@ -166,8 +166,6 @@ Ok(()) } -const STACK_SIZE: usize = 1024 * 1024 * 8; - /// Parts of rust-analyzer can use a lot of stack space, and some operating systems only give us /// 1 MB by default (eg. Windows), so this spawns a new thread with hopefully sufficient stack /// space. @@ -176,8 +174,7 @@ thread_intent: stdx::thread::ThreadIntent, f: impl FnOnce() -> anyhow::Result<()> + Send + 'static, ) -> anyhow::Result<()> { - let handle = - stdx::thread::Builder::new(thread_intent, thread_name).stack_size(STACK_SIZE).spawn(f)?; + let handle = stdx::thread::Builder::new(thread_intent, thread_name).spawn(f)?; handle.join()?; @@ -189,6 +186,7 @@ rayon::ThreadPoolBuilder::new() .thread_name(|ix| format!("RayonWorker{}", ix)) + .stack_size(stdx::thread::DEFAULT_STACK_SIZE) .build_global() .unwrap();
diff --git a/crates/rust-analyzer/src/cli.rs b/crates/rust-analyzer/src/cli.rs index cc0efe9..0355ade 100644 --- a/crates/rust-analyzer/src/cli.rs +++ b/crates/rust-analyzer/src/cli.rs
@@ -42,6 +42,9 @@ pub fn is_spammy(self) -> bool { matches!(self, Verbosity::Spammy) } + pub fn is_quiet(self) -> bool { + matches!(self, Verbosity::Quiet) + } } fn read_stdin() -> anyhow::Result<String> {
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 073f287..6cbb0c7 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -53,6 +53,14 @@ impl flags::AnalysisStats { pub fn run(self, verbosity: Verbosity) -> anyhow::Result<()> { + let mut rayon_pool = rayon::ThreadPoolBuilder::new() + .thread_name(|ix| format!("RayonWorker{}", ix)) + .stack_size(stdx::thread::DEFAULT_STACK_SIZE); + if !self.parallel { + rayon_pool = rayon_pool.num_threads(1); + } + rayon_pool.build_global().unwrap(); + let mut rng = { let seed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_millis() as u64; Rand32::new(seed) @@ -576,10 +584,7 @@ sema: &sema, scope: &scope, goal: target_ty, - config: hir::term_search::TermSearchConfig { - enable_borrowcheck: true, - ..Default::default() - }, + config: hir::term_search::TermSearchConfig::default(), }; let found_terms = hir::term_search::term_search(&ctx); @@ -1367,7 +1372,6 @@ prefer_absolute: false, style_lints: false, term_search_fuel: 400, - term_search_borrowck: true, show_rename_conflicts: true, }, ide::AssistResolveStrategy::All,
diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs index e50e1c2..fe1a58f 100644 --- a/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -9,23 +9,20 @@ use ide_db::{base_db::SourceDatabase, line_index}; use load_cargo::{LoadCargoConfig, ProcMacroServerChoice, load_workspace_at}; -use crate::cli::{flags, progress_report::ProgressReport}; +use crate::cli::{Verbosity, flags, progress_report::ProgressReport}; impl flags::Diagnostics { - pub fn run(self) -> anyhow::Result<()> { - const STACK_SIZE: usize = 1024 * 1024 * 8; - + pub fn run(self, verbosity: Verbosity) -> anyhow::Result<()> { let handle = stdx::thread::Builder::new( stdx::thread::ThreadIntent::LatencySensitive, "BIG_STACK_THREAD", ) - .stack_size(STACK_SIZE) - .spawn(|| self.run_()) + .spawn(move || self.run_(verbosity)) .unwrap(); handle.join() } - fn run_(self) -> anyhow::Result<()> { + fn run_(self, verbosity: Verbosity) -> anyhow::Result<()> { let cargo_config = CargoConfig { sysroot: Some(RustLibSource::Discover), all_targets: true, @@ -64,7 +61,11 @@ }) .collect::<Vec<_>>(); - let mut bar = ProgressReport::new(work.len()); + let mut bar = if verbosity.is_quiet() { + ProgressReport::hidden() + } else { + ProgressReport::new(work.len()) + }; for module in work { let file_id = module.definition_source_file_id(db).original_file(db); if !visited_files.contains(&file_id) {
diff --git a/crates/rust-analyzer/src/cli/progress_report.rs b/crates/rust-analyzer/src/cli/progress_report.rs index aff2c0d..7c634cf 100644 --- a/crates/rust-analyzer/src/cli/progress_report.rs +++ b/crates/rust-analyzer/src/cli/progress_report.rs
@@ -1,7 +1,7 @@ //! A simple progress bar //! //! A single thread non-optimized progress bar -use std::io::{self, Write}; +use std::io::{self, IsTerminal, Write}; /// A Simple ASCII Progress Bar pub(crate) struct ProgressReport<'a> { @@ -16,7 +16,8 @@ impl<'a> ProgressReport<'a> { pub(crate) fn new(len: usize) -> ProgressReport<'a> { - ProgressReport { curr: 0.0, text: String::new(), hidden: false, len, pos: 0, msg: None } + let is_tty = io::stdout().is_terminal(); + ProgressReport { curr: 0.0, text: String::new(), hidden: !is_tty, len, pos: 0, msg: None } } pub(crate) fn hidden() -> ProgressReport<'a> {
diff --git a/crates/rust-analyzer/src/cli/rustc_tests.rs b/crates/rust-analyzer/src/cli/rustc_tests.rs index 69405b4..5225c49c 100644 --- a/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/crates/rust-analyzer/src/cli/rustc_tests.rs
@@ -10,7 +10,7 @@ use ide_db::base_db; use itertools::Either; use profile::StopWatch; -use project_model::toolchain_info::{QueryConfig, target_data}; +use project_model::toolchain_info::{QueryConfig, target_data, version}; use project_model::{ CargoConfig, ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, RustLibSource, RustSourceWorkspaceConfig, Sysroot, @@ -81,10 +81,13 @@ sysroot.set_workspace(loaded_sysroot); } + let query_config = QueryConfig::Rustc(&sysroot, tmp_file.parent().unwrap().as_ref()); + let toolchain_version = version::get(query_config, &cargo_config.extra_env).ok().flatten(); let target_data = target_data::get( - QueryConfig::Rustc(&sysroot, tmp_file.parent().unwrap().as_ref()), + query_config, None, &cargo_config.extra_env, + toolchain_version.as_ref(), ); let workspace = ProjectWorkspace {
diff --git a/crates/rust-analyzer/src/cli/unresolved_references.rs b/crates/rust-analyzer/src/cli/unresolved_references.rs index f8eacbb..d9a5609 100644 --- a/crates/rust-analyzer/src/cli/unresolved_references.rs +++ b/crates/rust-analyzer/src/cli/unresolved_references.rs
@@ -11,13 +11,10 @@ impl flags::UnresolvedReferences { pub fn run(self) -> anyhow::Result<()> { - const STACK_SIZE: usize = 1024 * 1024 * 8; - let handle = stdx::thread::Builder::new( stdx::thread::ThreadIntent::LatencySensitive, "BIG_STACK_THREAD", ) - .stack_size(STACK_SIZE) .spawn(|| self.run_()) .unwrap();
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 3ba0e47..1e7c825 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs
@@ -623,10 +623,6 @@ /// Prefer to use `Self` over the type name when inserting a type (e.g. in "fill match arms" assist). assist_preferSelf: bool = false, - /// Enable borrow checking for term search code assists. If set to false, also there will be - /// more suggestions, but some of them may not borrow-check. - assist_termSearch_borrowcheck: bool = true, - /// Term search fuel in "units of work" for assists (Defaults to 1800). assist_termSearch_fuel: usize = 1800, @@ -1868,7 +1864,6 @@ prefer_prelude: self.imports_preferPrelude(source_root).to_owned(), prefer_absolute: self.imports_prefixExternPrelude(source_root).to_owned(), term_search_fuel: self.assist_termSearch_fuel(source_root).to_owned() as u64, - term_search_borrowck: self.assist_termSearch_borrowcheck(source_root).to_owned(), code_action_grouping: self.code_action_group(), expr_fill_default: match self.assist_expressionFillDefault(source_root) { ExprFillDefaultDef::Todo => ExprFillDefaultMode::Todo, @@ -1998,7 +1993,6 @@ prefer_absolute: self.imports_prefixExternPrelude(source_root).to_owned(), style_lints: self.diagnostics_styleLints_enable(source_root).to_owned(), term_search_fuel: self.assist_termSearch_fuel(source_root).to_owned() as u64, - term_search_borrowck: self.assist_termSearch_borrowcheck(source_root).to_owned(), show_rename_conflicts: *self.rename_showConflicts(source_root), } } @@ -4034,7 +4028,9 @@ "enum": ["per_workspace", "once"], "enumDescriptions": [ "The command will be executed for each Rust workspace with the workspace as the working directory.", - "The command will be executed once with the opened project as the working directory." + "The command will be executed once with the opened project as the working directory.\ + \n\n**Warning:** This can cause errors when the Cargo configuration of the opened workspaces is different \ + or even only different than the default. Enabling this is not recommended." ], }, "Option<CheckOnSaveTargets>" => set! {
diff --git a/crates/rust-analyzer/src/flycheck.rs b/crates/rust-analyzer/src/flycheck.rs index b927a11..99e640a 100644 --- a/crates/rust-analyzer/src/flycheck.rs +++ b/crates/rust-analyzer/src/flycheck.rs
@@ -69,10 +69,9 @@ cmd: &mut Command, ws_target_dir: Option<&Utf8Path>, package_repr: Option<&str>, + toolchain_version: Option<&semver::Version>, ) { - for target in &self.target_tuples { - cmd.args(["--target", target.as_str()]); - } + toolchain::cargo_use_targets(toolchain_version, cmd, &self.target_tuples); if self.all_targets { if self.set_test { cmd.arg("--all-targets"); @@ -227,6 +226,7 @@ workspace_root: AbsPathBuf, manifest_path: Option<AbsPathBuf>, ws_target_dir: Option<Utf8PathBuf>, + toolchain_version: Option<semver::Version>, ) -> FlycheckHandle { let actor = FlycheckActor::new( id, @@ -238,6 +238,7 @@ workspace_root, manifest_path, ws_target_dir, + toolchain_version, ); let (sender, receiver) = unbounded::<StateChange>(); let thread = @@ -445,6 +446,7 @@ command_receiver: Option<Receiver<CheckMessage>>, diagnostics_cleared_for: FxHashSet<PackageSpecifier>, diagnostics_received: DiagnosticsReceived, + toolchain_version: Option<semver::Version>, } #[derive(PartialEq, Debug)] @@ -531,6 +533,7 @@ workspace_root: AbsPathBuf, manifest_path: Option<AbsPathBuf>, ws_target_dir: Option<Utf8PathBuf>, + toolchain_version: Option<semver::Version>, ) -> FlycheckActor { tracing::info!(%id, ?workspace_root, "Spawning flycheck"); FlycheckActor { @@ -548,6 +551,7 @@ command_receiver: None, diagnostics_cleared_for: Default::default(), diagnostics_received: DiagnosticsReceived::NotYet, + toolchain_version, } } @@ -958,6 +962,7 @@ &mut cmd, self.ws_target_dir.as_ref().map(Utf8PathBuf::as_path), package_repr, + self.toolchain_version.as_ref(), ); cmd.args(&cargo_options.extra_args); Some((cmd, FlycheckCommandOrigin::Cargo))
diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 41e3db7..d88eb35 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/crates/rust-analyzer/src/handlers/request.rs
@@ -287,6 +287,7 @@ Some(cargo.target_directory().as_ref()), target, state.test_run_sender.clone(), + ws.toolchain.as_ref(), )?; handles.push(handle); } @@ -780,10 +781,7 @@ let source_changes: Vec<SourceChange> = params .files .into_iter() - .filter_map(|file_rename| { - let from = Uri::parse(&file_rename.old_uri).ok()?; - let to = Uri::parse(&file_rename.new_uri).ok()?; - + .filter_map(|lsp_types::FileRename { new_uri: to, old_uri: from }| { let from_path = from.to_file_path().ok()?; let to_path = to.to_file_path().ok()?; @@ -2726,6 +2724,7 @@ ResourceOperationKind::Create => "create", ResourceOperationKind::Rename => "rename", ResourceOperationKind::Delete => "delete", + ResourceOperationKind::Custom(_) => unreachable!(), } ), )
diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs index bd3574a..93fa872 100644 --- a/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -281,7 +281,6 @@ prefer_prelude: false, prefer_absolute: false, term_search_fuel: 400, - term_search_borrowck: true, show_rename_conflicts: true, }; host.analysis()
diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs index ce71685..7fa3317 100644 --- a/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -183,18 +183,21 @@ pub(crate) fn completion_text_edit( line_index: &LineIndex, - insert_replace_support: Option<lsp_types::Position>, + cursor_position: lsp_types::Position, + insert_replace_support: bool, indel: Indel, ) -> lsp_types::CompletionItemTextEdit { let text_edit = text_edit(line_index, indel); - match insert_replace_support { - Some(cursor_pos) => lsp_types::InsertReplaceEdit { + let insert = lsp_types::Range { start: text_edit.range.start, end: cursor_position }; + if insert_replace_support { + lsp_types::InsertReplaceEdit { new_text: text_edit.new_text, - insert: lsp_types::Range { start: text_edit.range.start, end: cursor_pos }, + insert, replace: text_edit.range, } - .into(), - None => text_edit.into(), + .into() + } else { + lsp_types::TextEdit { range: insert, new_text: text_edit.new_text }.into() } } @@ -296,7 +299,7 @@ completion_trigger_character: Option<char>, item: CompletionItem, ) { - let insert_replace_support = config.insert_replace_support().then_some(tdpp.position); + let insert_replace_support = config.insert_replace_support(); let ref_match = item.ref_match(); let mut additional_text_edits = Vec::new(); @@ -321,7 +324,12 @@ if indel.delete.contains_range(source_range) { // Extract this indel as the main edit text_edit = Some(if indel.delete == source_range { - self::completion_text_edit(line_index, insert_replace_support, indel.clone()) + self::completion_text_edit( + line_index, + tdpp.position, + insert_replace_support, + indel.clone(), + ) } else { assert!(source_range.end() == indel.delete.end()); let range1 = TextRange::new(indel.delete.start(), source_range.start()); @@ -329,7 +337,12 @@ let indel1 = Indel::delete(range1); let indel2 = Indel::replace(range2, indel.insert.clone()); additional_text_edits.push(self::text_edit(line_index, indel1)); - self::completion_text_edit(line_index, insert_replace_support, indel2) + self::completion_text_edit( + line_index, + tdpp.position, + insert_replace_support, + indel2, + ) }) } else { assert!(source_range.intersect(indel.delete).is_none());
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 3bf3cd5..173409b 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs
@@ -901,6 +901,7 @@ self.config.default_root_path().clone(), None, None, + None, )] } crate::flycheck::InvocationStrategy::PerWorkspace => { @@ -942,21 +943,30 @@ ProjectWorkspaceKind::DetachedFile { .. } => return None, }, ws.sysroot.root().map(ToOwned::to_owned), + ws.toolchain.clone(), )) }) - .map(|(id, (config_json, root, manifest_path, target_dir), sysroot_root)| { - FlycheckHandle::spawn( + .map( + |( id, - generation.clone(), - sender.clone(), - config.clone(), - config_json, + (config_json, root, manifest_path, target_dir), sysroot_root, - root.to_path_buf(), - manifest_path.map(|it| it.to_path_buf()), - target_dir.map(|it| AsRef::<Utf8Path>::as_ref(it).to_path_buf()), - ) - }) + toolchain, + )| { + FlycheckHandle::spawn( + id, + generation.clone(), + sender.clone(), + config.clone(), + config_json, + sysroot_root, + root.to_path_buf(), + manifest_path.map(|it| it.to_path_buf()), + target_dir.map(|it| AsRef::<Utf8Path>::as_ref(it).to_path_buf()), + toolchain, + ) + }, + ) .collect() } }
diff --git a/crates/rust-analyzer/src/test_runner.rs b/crates/rust-analyzer/src/test_runner.rs index c6f8a7c..62d242f 100644 --- a/crates/rust-analyzer/src/test_runner.rs +++ b/crates/rust-analyzer/src/test_runner.rs
@@ -108,6 +108,7 @@ ws_target_dir: Option<&Utf8Path>, test_target: TestTarget, sender: Sender<CargoTestMessage>, + toolchain_version: Option<&semver::Version>, ) -> anyhow::Result<Self> { let mut cmd = toolchain::command(Tool::Cargo.path(), root, &options.extra_env); cmd.env("RUSTC_BOOTSTRAP", "1"); @@ -131,7 +132,12 @@ cmd.arg("--no-fail-fast"); cmd.arg("--manifest-path"); cmd.arg(root.join("Cargo.toml")); - options.apply_on_command(&mut cmd, ws_target_dir, Some(&test_target.package)); + options.apply_on_command( + &mut cmd, + ws_target_dir, + Some(&test_target.package), + toolchain_version, + ); cmd.arg("--"); if let Some(path) = path { cmd.arg(path);
diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs index 9edb1df..56629f7 100644 --- a/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -30,7 +30,8 @@ FileRename, FormattingOptions, HoverParams, HoverRequest, InlayHint, InlayHintParams, InlayHintRequest, InlayHintResolveRequest, Label, LanguageKind, PartialResultParams, Position, Range, RenameFilesParams, TextDocumentItem, TextDocumentPositionParams, TypeDefinitionParams, - TypeDefinitionRequest, WillRenameFilesRequest, WorkDoneProgressParams, WorkspaceSymbolRequest, + TypeDefinitionRequest, Uri, WillRenameFilesRequest, WorkDoneProgressParams, + WorkspaceSymbolRequest, }; use rust_analyzer::lsp::ext::{OnEnterRequest, RunnablesParams, RunnablesRequest}; use serde_json::json; @@ -1304,8 +1305,8 @@ server.request::<WillRenameFilesRequest>( RenameFilesParams { files: vec![FileRename { - old_uri: base_path.join("src/old_file.rs").to_str().unwrap().to_owned(), - new_uri: base_path.join("src/new_file.rs").to_str().unwrap().to_owned(), + old_uri: Uri::parse(base_path.join("src/old_file.rs").to_str().unwrap()).unwrap(), + new_uri: Uri::parse(base_path.join("src/new_file.rs").to_str().unwrap()).unwrap(), }], }, json!({ @@ -1339,8 +1340,10 @@ server.request::<WillRenameFilesRequest>( RenameFilesParams { files: vec![FileRename { - old_uri: base_path.join("src/from_mod/mod.rs").to_str().unwrap().to_owned(), - new_uri: base_path.join("src/from_mod/foo.rs").to_str().unwrap().to_owned(), + old_uri: Uri::parse(base_path.join("src/from_mod/mod.rs").to_str().unwrap()) + .unwrap(), + new_uri: Uri::parse(base_path.join("src/from_mod/foo.rs").to_str().unwrap()) + .unwrap(), }], }, json!(null), @@ -1350,8 +1353,8 @@ server.request::<WillRenameFilesRequest>( RenameFilesParams { files: vec![FileRename { - old_uri: base_path.join("src/to_mod/foo.rs").to_str().unwrap().to_owned(), - new_uri: base_path.join("src/to_mod/mod.rs").to_str().unwrap().to_owned(), + old_uri: Uri::parse(base_path.join("src/to_mod/foo.rs").to_str().unwrap()).unwrap(), + new_uri: Uri::parse(base_path.join("src/to_mod/mod.rs").to_str().unwrap()).unwrap(), }], }, json!(null), @@ -1361,8 +1364,8 @@ server.request::<WillRenameFilesRequest>( RenameFilesParams { files: vec![FileRename { - old_uri: base_path.join("src/old_folder").to_str().unwrap().to_owned(), - new_uri: base_path.join("src/new_folder").to_str().unwrap().to_owned(), + old_uri: Uri::parse(base_path.join("src/old_folder").to_str().unwrap()).unwrap(), + new_uri: Uri::parse(base_path.join("src/new_folder").to_str().unwrap()).unwrap(), }], }, json!({
diff --git a/crates/span/src/ast_id.rs b/crates/span/src/ast_id.rs index 83a6748..e6b89e7 100644 --- a/crates/span/src/ast_id.rs +++ b/crates/span/src/ast_id.rs
@@ -59,53 +59,13 @@ impl fmt::Debug for ErasedFileAstId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let kind = self.kind(); - macro_rules! kind { - ($($kind:ident),* $(,)?) => { - if false { - // Ensure we covered all variants. - match ErasedFileAstIdKind::Root { - $( ErasedFileAstIdKind::$kind => {} )* - } - unreachable!() - } - $( else if kind == ErasedFileAstIdKind::$kind as u32 { - stringify!($kind) - } )* - else { - "Unknown" - } - }; - } - let kind = kind!( - Root, - Enum, - Struct, - Union, - ExternCrate, - MacroDef, - MacroRules, - Module, - Static, - Trait, - Variant, - Const, - Fn, - MacroCall, - TypeAlias, - ExternBlock, - Use, - Impl, - BlockExpr, - AsmExpr, - Fixup, - NoDownmap, - ); + let kind = + self.kind_typed().map_or_else(|| "Unknown".to_owned(), |kind| format!("{kind:?}")); if f.alternate() { write!(f, "{kind}[{:04X}, {}]", self.hash_value(), self.index()) } else { f.debug_struct("ErasedFileAstId") - .field("kind", &format_args!("{kind}")) + .field("kind", &kind) .field("index", &self.index()) .field("hash", &format_args!("{:04X}", self.hash_value())) .finish() @@ -161,6 +121,35 @@ Root, } +impl ErasedFileAstIdKind { + fn syntax_kind(self) -> Option<SyntaxKind> { + match self { + ErasedFileAstIdKind::Enum => Some(SyntaxKind::ENUM), + ErasedFileAstIdKind::Struct => Some(SyntaxKind::STRUCT), + ErasedFileAstIdKind::Union => Some(SyntaxKind::UNION), + ErasedFileAstIdKind::ExternCrate => Some(SyntaxKind::EXTERN_CRATE), + ErasedFileAstIdKind::MacroDef => Some(SyntaxKind::MACRO_DEF), + ErasedFileAstIdKind::MacroRules => Some(SyntaxKind::MACRO_RULES), + ErasedFileAstIdKind::Module => Some(SyntaxKind::MODULE), + ErasedFileAstIdKind::Static => Some(SyntaxKind::STATIC), + ErasedFileAstIdKind::Trait => Some(SyntaxKind::TRAIT), + ErasedFileAstIdKind::Variant => Some(SyntaxKind::VARIANT), + ErasedFileAstIdKind::Const => Some(SyntaxKind::CONST), + ErasedFileAstIdKind::Fn => Some(SyntaxKind::FN), + ErasedFileAstIdKind::MacroCall => Some(SyntaxKind::MACRO_CALL), + ErasedFileAstIdKind::TypeAlias => Some(SyntaxKind::TYPE_ALIAS), + ErasedFileAstIdKind::ExternBlock => Some(SyntaxKind::EXTERN_BLOCK), + ErasedFileAstIdKind::Use => Some(SyntaxKind::USE), + ErasedFileAstIdKind::Impl => Some(SyntaxKind::IMPL), + ErasedFileAstIdKind::BlockExpr => Some(SyntaxKind::BLOCK_EXPR), + ErasedFileAstIdKind::AsmExpr => Some(SyntaxKind::ASM_EXPR), + ErasedFileAstIdKind::Fixup + | ErasedFileAstIdKind::NoDownmap + | ErasedFileAstIdKind::Root => None, + } + } +} + // First hash, then index, then kind. const HASH_BITS: u32 = 16; const INDEX_BITS: u32 = 11; @@ -206,6 +195,51 @@ self.0 >> (HASH_BITS + INDEX_BITS) } + fn kind_typed(self) -> Option<ErasedFileAstIdKind> { + let kind = self.kind(); + macro_rules! kind { + ($($kind:ident),* $(,)?) => { + if false { + // Ensure we covered all variants. + match ErasedFileAstIdKind::Root { + $( ErasedFileAstIdKind::$kind => {} )* + } + unreachable!() + } + $( else if kind == ErasedFileAstIdKind::$kind as u32 { + Some(ErasedFileAstIdKind::$kind) + } )* + else { + None + } + }; + } + kind!( + Root, + Enum, + Struct, + Union, + ExternCrate, + MacroDef, + MacroRules, + Module, + Static, + Trait, + Variant, + Const, + Fn, + MacroCall, + TypeAlias, + ExternBlock, + Use, + Impl, + BlockExpr, + AsmExpr, + Fixup, + NoDownmap, + ) + } + #[inline] pub fn is_root(self) -> bool { self.kind() == ErasedFileAstIdKind::Root as u32 @@ -294,6 +328,20 @@ } #[inline] + pub fn downcast_unchecked<M: AstIdNode + Into<N>>(self) -> FileAstId<M> { + debug_assert!( + self.erase() + .kind_typed() + .and_then(|kind| kind.syntax_kind()) + .is_some_and(|kind| M::can_cast(kind)), + "`FileAstId::<{}>::downcast_unchecked::<{}>()` is invalid", + std::any::type_name::<N>(), + std::any::type_name::<M>(), + ); + FileAstId { raw: self.raw, _marker: PhantomData } + } + + #[inline] pub fn erase(self) -> ErasedFileAstId { self.raw } @@ -381,8 +429,8 @@ let self_ty_name = type_as_name(node.self_ty()); let trait_name = type_as_name(node.trait_()); let data = ImplFileAstId { - self_ty_name: self_ty_name.as_ref().map(|it| it.text_non_mutable()), - trait_name: trait_name.as_ref().map(|it| it.text_non_mutable()), + self_ty_name: self_ty_name.as_ref().map(|it| it.text()), + trait_name: trait_name.as_ref().map(|it| it.text()), }; Some(index_map.new_id(ErasedFileAstIdKind::Impl, data)) } else { @@ -473,7 +521,7 @@ $( ast::$ident(node) => { let name = node.$name_method(); - let name = name.as_ref().map_or("", |it| it.text_non_mutable()); + let name = name.as_ref().map_or("", |it| it.text()); let result = ErasedHasNameFileAstId { name, }; @@ -519,7 +567,7 @@ $( ast::$ident(node) => { let name = $name_callback(node); - let name = name.as_ref().map_or("", |it| it.text_non_mutable()); + let name = name.as_ref().map_or("", |it| it.text()); let properties = ErasedHasNameFileAstId { name, };
diff --git a/crates/span/src/hygiene.rs b/crates/span/src/hygiene.rs index 70b0447..64ce120 100644 --- a/crates/span/src/hygiene.rs +++ b/crates/span/src/hygiene.rs
@@ -103,7 +103,9 @@ && zalsa_::HashEqLike::<T3>::eq(&self.parent, &data.3) } } - impl zalsa_struct_::Configuration for SyntaxContext { + + // SAFETY: `Self::Fields`, i.e. `SyntaxContextData`, doesn't contain any lifetimes. + unsafe impl zalsa_struct_::Configuration for SyntaxContext { const LOCATION: salsa::plumbing::Location = salsa::plumbing::Location { file: file!(), line: line!() }; const DEBUG_NAME: &'static str = "SyntaxContextData"; @@ -133,13 +135,9 @@ static CACHE: zalsa_::IngredientCache<zalsa_struct_::IngredientImpl<SyntaxContext>> = zalsa_::IngredientCache::new(); - // SAFETY: `lookup_jar_by_type` returns a valid ingredient index, and the only - // ingredient created by our jar is the struct ingredient. - unsafe { - CACHE.get_or_create(zalsa, || { - zalsa.lookup_jar_by_type::<zalsa_struct_::JarImpl<SyntaxContext>>() - }) - } + // SAFETY: The ingredient at offset 0 in `JarImpl<SyntaxContext>` has type + // `IngredientImpl<SyntaxContext>`. + unsafe { CACHE.get_or_create::<zalsa_struct_::JarImpl<SyntaxContext>, 0>(zalsa) } } } impl zalsa_::AsId for SyntaxContext { @@ -193,16 +191,7 @@ } } - unsafe impl salsa::plumbing::Update for SyntaxContext { - unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool { - if unsafe { *old_pointer } != new_value { - unsafe { *old_pointer = new_value }; - true - } else { - false - } - } - } + unsafe impl salsa::plumbing::SalsaValue for SyntaxContext {} impl<'db> SyntaxContext { pub fn new< Db,
diff --git a/crates/stdx/src/thread.rs b/crates/stdx/src/thread.rs index 37b7a9f..0000fd1 100644 --- a/crates/stdx/src/thread.rs +++ b/crates/stdx/src/thread.rs
@@ -34,6 +34,8 @@ Builder::new(intent, name).spawn(f).expect("failed to spawn thread") } +pub const DEFAULT_STACK_SIZE: usize = 16 * 1024 * 1024; + pub struct Builder { intent: ThreadIntent, inner: jod_thread::Builder, @@ -43,7 +45,11 @@ impl Builder { #[must_use] pub fn new(intent: ThreadIntent, name: impl Into<String>) -> Self { - Self { intent, inner: jod_thread::Builder::new().name(name.into()), allow_leak: false } + Self { + intent, + inner: jod_thread::Builder::new().name(name.into()).stack_size(DEFAULT_STACK_SIZE), + allow_leak: false, + } } #[must_use]
diff --git a/crates/stdx/src/thread/pool.rs b/crates/stdx/src/thread/pool.rs index 918b88d..1ef6954 100644 --- a/crates/stdx/src/thread/pool.rs +++ b/crates/stdx/src/thread/pool.rs
@@ -45,7 +45,6 @@ /// Panics if job panics #[must_use] pub fn new(threads: usize) -> Self { - const STACK_SIZE: usize = 8 * 1024 * 1024; const INITIAL_INTENT: ThreadIntent = ThreadIntent::Worker; let (job_sender, job_receiver) = crossbeam_channel::unbounded(); @@ -54,7 +53,6 @@ let mut handles = Vec::with_capacity(threads); for idx in 0..threads { let handle = Builder::new(INITIAL_INTENT, format!("Worker{idx}",)) - .stack_size(STACK_SIZE) .allow_leak(true) .spawn({ let extant_tasks = Arc::clone(&extant_tasks);
diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index e65836e..a9df1ac 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml
@@ -15,6 +15,7 @@ [dependencies] either.workspace = true itertools.workspace = true +ra-ap-rustc_lexer.workspace = true rowan.workspace = true rustc-hash.workspace = true rustc-literal-escaper.workspace = true
diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram index 05df8b9..7a24b32 100644 --- a/crates/syntax/rust.ungram +++ b/crates/syntax/rust.ungram
@@ -488,10 +488,7 @@ ')' FormatArgsArg = - arg_name:FormatArgsArgName? Expr - -FormatArgsArgName = - '=' // This also has a name, but it's any token and we can't put it here + (Name '=')? Expr MacroExpr = MacroCall
diff --git a/crates/syntax/src/ast/edit.rs b/crates/syntax/src/ast/edit.rs index 080f9a7..852b13f 100644 --- a/crates/syntax/src/ast/edit.rs +++ b/crates/syntax/src/ast/edit.rs
@@ -360,7 +360,7 @@ // shorthand `{ x }` → expand to `{ x: expr }` let new_field = editor .make() - .record_expr_field(editor.make().name_ref(&name_ref.text()), Some(expr)); + .record_expr_field(editor.make().name_ref(name_ref.text()), Some(expr)); editor.replace(self.syntax(), new_field.syntax()); } }
diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index e0992ab..5fa56cf 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs
@@ -723,16 +723,10 @@ pub struct FormatArgsArg { pub(crate) syntax: SyntaxNode, } +impl ast::HasName for FormatArgsArg {} impl FormatArgsArg { #[inline] - pub fn arg_name(&self) -> Option<FormatArgsArgName> { support::child(&self.syntax) } - #[inline] pub fn expr(&self) -> Option<Expr> { support::child(&self.syntax) } -} -pub struct FormatArgsArgName { - pub(crate) syntax: SyntaxNode, -} -impl FormatArgsArgName { #[inline] pub fn eq_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![=]) } } @@ -4105,38 +4099,6 @@ f.debug_struct("FormatArgsArg").field("syntax", &self.syntax).finish() } } -impl AstNode for FormatArgsArgName { - #[inline] - fn kind() -> SyntaxKind - where - Self: Sized, - { - FORMAT_ARGS_ARG_NAME - } - #[inline] - fn can_cast(kind: SyntaxKind) -> bool { kind == FORMAT_ARGS_ARG_NAME } - #[inline] - fn cast(syntax: SyntaxNode) -> Option<Self> { - if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None } - } - #[inline] - fn syntax(&self) -> &SyntaxNode { &self.syntax } -} -impl hash::Hash for FormatArgsArgName { - fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); } -} -impl Eq for FormatArgsArgName {} -impl PartialEq for FormatArgsArgName { - fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax } -} -impl Clone for FormatArgsArgName { - fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } } -} -impl fmt::Debug for FormatArgsArgName { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("FormatArgsArgName").field("syntax", &self.syntax).finish() - } -} impl AstNode for FormatArgsExpr { #[inline] fn kind() -> SyntaxKind @@ -9810,6 +9772,7 @@ | CONST_PARAM | ENUM | FN + | FORMAT_ARGS_ARG | IDENT_PAT | MACRO_DEF | MACRO_RULES @@ -9868,6 +9831,10 @@ #[inline] fn from(node: Fn) -> AnyHasName { AnyHasName { syntax: node.syntax } } } +impl From<FormatArgsArg> for AnyHasName { + #[inline] + fn from(node: FormatArgsArg) -> AnyHasName { AnyHasName { syntax: node.syntax } } +} impl From<IdentPat> for AnyHasName { #[inline] fn from(node: IdentPat) -> AnyHasName { AnyHasName { syntax: node.syntax } } @@ -10434,11 +10401,6 @@ std::fmt::Display::fmt(self.syntax(), f) } } -impl std::fmt::Display for FormatArgsArgName { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::fmt::Display::fmt(self.syntax(), f) - } -} impl std::fmt::Display for FormatArgsExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f)
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index 1eb658f..672e2fd 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs
@@ -3,7 +3,7 @@ //! //! These methods should only do simple, shallow tasks related to the syntax of the node itself. -use std::{borrow::Cow, fmt, iter::successors}; +use std::{fmt, iter::successors}; use itertools::Itertools; use parser::SyntaxKind; @@ -11,7 +11,7 @@ use smallvec::{SmallVec, smallvec}; use crate::{ - NodeOrToken, SmolStr, SyntaxElement, SyntaxElementChildren, SyntaxToken, T, TokenText, + NodeOrToken, SmolStr, SyntaxElement, SyntaxElementChildren, SyntaxToken, T, ast::{ self, AstNode, AstToken, HasAttrs, HasGenericArgs, HasGenericParams, HasName, HasTypeBounds, SyntaxNode, support, @@ -22,41 +22,21 @@ use super::{GenericParam, RangeItem, RangeOp}; impl ast::Lifetime { - pub fn text(&self) -> TokenText<'_> { + pub fn text(&self) -> &str { text_of_first_token(self.syntax()) } } impl ast::Name { - pub fn text(&self) -> TokenText<'_> { + pub fn text(&self) -> &str { text_of_first_token(self.syntax()) } - pub fn text_non_mutable(&self) -> &str { - fn first_token(green_ref: &GreenNodeData) -> &GreenTokenData { - green_ref.children().next().and_then(NodeOrToken::into_token).unwrap() - } - - match self.syntax().green() { - Cow::Borrowed(green_ref) => first_token(green_ref).text(), - Cow::Owned(_) => unreachable!(), - } - } } impl ast::NameRef { - pub fn text(&self) -> TokenText<'_> { + pub fn text(&self) -> &str { text_of_first_token(self.syntax()) } - pub fn text_non_mutable(&self) -> &str { - fn first_token(green_ref: &GreenNodeData) -> &GreenTokenData { - green_ref.children().next().and_then(NodeOrToken::into_token).unwrap() - } - - match self.syntax().green() { - Cow::Borrowed(green_ref) => first_token(green_ref).text(), - Cow::Owned(_) => unreachable!(), - } - } pub fn as_tuple_field(&self) -> Option<usize> { self.text().parse().ok() @@ -67,15 +47,12 @@ } } -fn text_of_first_token(node: &SyntaxNode) -> TokenText<'_> { +fn text_of_first_token(node: &SyntaxNode) -> &str { fn first_token(green_ref: &GreenNodeData) -> &GreenTokenData { green_ref.children().next().and_then(NodeOrToken::into_token).unwrap() } - match node.green() { - Cow::Borrowed(green_ref) => TokenText::borrowed(first_token(green_ref).text()), - Cow::Owned(green) => TokenText::owned(first_token(&green).to_owned()), - } + first_token(node.green()).text() } fn into_comma(it: NodeOrToken<SyntaxNode, SyntaxToken>) -> Option<SyntaxToken> { @@ -619,7 +596,7 @@ _ => None, } } - pub fn text(&self) -> TokenText<'_> { + pub fn text(&self) -> &str { match self { NameLike::NameRef(name_ref) => name_ref.text(), NameLike::Name(name) => name.text(), @@ -691,7 +668,7 @@ } impl NameOrNameRef { - pub fn text(&self) -> TokenText<'_> { + pub fn text(&self) -> &str { match self { NameOrNameRef::Name(name) => name.text(), NameOrNameRef::NameRef(name_ref) => name_ref.text(), @@ -1192,15 +1169,6 @@ } } -impl ast::FormatArgsArgName { - /// This is not a [`ast::Name`], because the name may be a keyword. - pub fn name(&self) -> SyntaxToken { - let name = self.syntax.first_token().unwrap(); - assert!(name.kind().is_any_identifier()); - name - } -} - impl ast::OrPat { pub fn leading_pipe(&self) -> Option<SyntaxToken> { self.syntax
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index b5c4e1a..bb0f53d 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs
@@ -183,6 +183,16 @@ let text = &self.text()[text_range_no_quotes - start]; let offset = text_range_no_quotes.start() - start; + if self.is_raw() { + let mut pos = offset; + for c in text.chars() { + let len = TextSize::of(c); + cb(TextRange::at(pos, len), Ok(c)); + pos += len; + } + return; + } + self.unescape(text, &mut |range: Range<usize>, unescaped_char| { if let Some((s, e)) = range.start.try_into().ok().zip(range.end.try_into().ok()) { cb(TextRange::new(s, e) + offset, unescaped_char); @@ -285,7 +295,7 @@ match (has_error, buf.capacity() == 0) { (Some(e), _) => Err(e), - (None, true) => Ok(Cow::Borrowed(text.as_bytes())), + (None, true) => Ok(Cow::Borrowed(&text.as_bytes()[..prev_end])), (None, false) => Ok(Cow::Owned(buf)), } } @@ -641,6 +651,10 @@ r"a\ bcde", b"abcde", ); + check_byte_string_value( + r"\ + ", b"", + ); } #[test]
diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs index 6146785..dc7d7af 100644 --- a/crates/syntax/src/lib.rs +++ b/crates/syntax/src/lib.rs
@@ -21,8 +21,12 @@ #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_lexer as rustc_lexer; #[cfg(feature = "in-rust-tree")] extern crate rustc_driver as _; +#[cfg(feature = "in-rust-tree")] +extern crate rustc_lexer; mod parsing; mod ptr; @@ -30,7 +34,6 @@ mod syntax_node; #[cfg(test)] mod tests; -mod token_text; mod validation; pub mod algo; @@ -54,7 +57,6 @@ PreorderWithTokens, RustLanguage, SyntaxElement, SyntaxElementChildren, SyntaxNode, SyntaxNodeChildren, SyntaxToken, SyntaxTreeBuilder, }, - token_text::TokenText, }; pub use parser::{Edition, SyntaxKind, T}; pub use rowan::{
diff --git a/crates/syntax/src/syntax_editor/edit_algo.rs b/crates/syntax/src/syntax_editor/edit_algo.rs index d24d9b1..24e7016 100644 --- a/crates/syntax/src/syntax_editor/edit_algo.rs +++ b/crates/syntax/src/syntax_editor/edit_algo.rs
@@ -449,7 +449,7 @@ let parent = parent_path.resolve(&self.root).and_then(SyntaxElement::into_node).unwrap(); let green = rowan::GreenNodeData::splice_children( - parent.green().as_ref(), + parent.green(), deleted.clone(), inserted.into_iter().map(PreparedElement::into_green), ); @@ -466,7 +466,7 @@ let NodeOrToken::Node(node) = replacement.syntax else { panic!("root node replacement should be a node") }; - self.root = SyntaxNode::new_root(node.green().into_owned()); + self.root = SyntaxNode::new_root(node.green().to_owned()); self.changed.clear(); if track_as_changed { self.changed.push(SyntaxPath { child_indices: Vec::new() }); @@ -543,7 +543,7 @@ impl PreparedElement { fn into_green(self) -> rowan::NodeOrToken<rowan::GreenNode, rowan::GreenToken> { match self.syntax { - SyntaxElement::Node(node) => NodeOrToken::Node(node.green().into_owned()), + SyntaxElement::Node(node) => NodeOrToken::Node(node.green().to_owned()), SyntaxElement::Token(token) => NodeOrToken::Token(token.green().to_owned()), } }
diff --git a/crates/syntax/src/token_text.rs b/crates/syntax/src/token_text.rs deleted file mode 100644 index e69deb4..0000000 --- a/crates/syntax/src/token_text.rs +++ /dev/null
@@ -1,102 +0,0 @@ -//! Yet another version of owned string, backed by a syntax tree token. - -use std::{cmp::Ordering, fmt, ops}; - -use rowan::GreenToken; -use smol_str::SmolStr; - -pub struct TokenText<'a>(pub(crate) Repr<'a>); - -pub(crate) enum Repr<'a> { - Borrowed(&'a str), - Owned(GreenToken), -} - -impl<'a> TokenText<'a> { - pub fn borrowed(text: &'a str) -> Self { - TokenText(Repr::Borrowed(text)) - } - - pub(crate) fn owned(green: GreenToken) -> Self { - TokenText(Repr::Owned(green)) - } - - pub fn as_str(&self) -> &str { - match &self.0 { - &Repr::Borrowed(it) => it, - Repr::Owned(green) => green.text(), - } - } -} - -impl ops::Deref for TokenText<'_> { - type Target = str; - - fn deref(&self) -> &str { - self.as_str() - } -} -impl AsRef<str> for TokenText<'_> { - fn as_ref(&self) -> &str { - self.as_str() - } -} - -impl From<TokenText<'_>> for String { - fn from(token_text: TokenText<'_>) -> Self { - token_text.as_str().into() - } -} - -impl From<TokenText<'_>> for SmolStr { - fn from(token_text: TokenText<'_>) -> Self { - SmolStr::new(token_text.as_str()) - } -} - -impl PartialEq<&'_ str> for TokenText<'_> { - fn eq(&self, other: &&str) -> bool { - self.as_str() == *other - } -} -impl PartialEq<TokenText<'_>> for &'_ str { - fn eq(&self, other: &TokenText<'_>) -> bool { - other == self - } -} -impl PartialEq<String> for TokenText<'_> { - fn eq(&self, other: &String) -> bool { - self.as_str() == other.as_str() - } -} -impl PartialEq<TokenText<'_>> for String { - fn eq(&self, other: &TokenText<'_>) -> bool { - other == self - } -} -impl PartialEq for TokenText<'_> { - fn eq(&self, other: &TokenText<'_>) -> bool { - self.as_str() == other.as_str() - } -} -impl Eq for TokenText<'_> {} -impl Ord for TokenText<'_> { - fn cmp(&self, other: &Self) -> Ordering { - self.as_str().cmp(other.as_str()) - } -} -impl PartialOrd for TokenText<'_> { - fn partial_cmp(&self, other: &Self) -> Option<Ordering> { - Some(self.cmp(other)) - } -} -impl fmt::Display for TokenText<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(self.as_str(), f) - } -} -impl fmt::Debug for TokenText<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(self.as_str(), f) - } -}
diff --git a/crates/syntax/src/utils.rs b/crates/syntax/src/utils.rs index d1f60f0..9538ae2 100644 --- a/crates/syntax/src/utils.rs +++ b/crates/syntax/src/utils.rs
@@ -1,6 +1,25 @@ //! A set of utils methods to reuse on other abstraction levels use crate::SyntaxKind; +use rustc_lexer; + +#[inline] +/// Checks that the name is an identifier. +/// This also means that it is not a strict keyword. +/// But it may be a weak keyword. +pub fn is_identifier(name: &str, edition: parser::Edition) -> bool { + if rustc_lexer::is_ident(name) { + if let Some(syntax_kind) = SyntaxKind::from_keyword(name, edition) + && syntax_kind.is_strict_keyword(edition) + { + false + } else { + true + } + } else { + false + } +} #[inline] pub fn is_raw_identifier(name: &str, edition: parser::Edition) -> bool {
diff --git a/crates/syntax/src/validation.rs b/crates/syntax/src/validation.rs index 4622590..acc5d70 100644 --- a/crates/syntax/src/validation.rs +++ b/crates/syntax/src/validation.rs
@@ -285,11 +285,11 @@ fn validate_path_keywords(segment: ast::PathSegment, errors: &mut Vec<SyntaxError>) { let path = segment.parent_path(); let is_path_start = segment.coloncolon_token().is_none() && path.qualifier().is_none(); - if let Some(token) = segment.self_token() { - if !is_path_start { + let is_last_segment = path.parent_path().is_none(); + if !is_path_start && !is_last_segment { errors.push(SyntaxError::new( - "The `self` keyword is only allowed as the first segment of a path", + "The `self` keyword is only allowed at the start or at the end of a path", token.text_range(), )); }
diff --git a/crates/syntax/src/validation/block.rs b/crates/syntax/src/validation/block.rs index 1f815f6..a140153 100644 --- a/crates/syntax/src/validation/block.rs +++ b/crates/syntax/src/validation/block.rs
@@ -9,7 +9,9 @@ pub(crate) fn validate_block_expr(block: ast::BlockExpr, errors: &mut Vec<SyntaxError>) { if let Some(parent) = block.syntax().parent() { match parent.kind() { - FN | EXPR_STMT | STMT_LIST | MACRO_STMTS => return, + FN | EXPR_STMT | STMT_LIST | MACRO_STMTS | LOOP_EXPR | WHILE_EXPR | FOR_EXPR => { + return; + } _ => {} } }
diff --git a/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast b/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast index 5fdde93..f56af1a 100644 --- a/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast +++ b/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rast
@@ -1,5 +1,5 @@ -SOURCE_FILE@0..350 - FN@0..349 +SOURCE_FILE@0..611 + FN@0..610 FN_KW@0..2 "fn" WHITESPACE@2..3 " " NAME@3..8 @@ -8,8 +8,8 @@ L_PAREN@8..9 "(" R_PAREN@9..10 ")" WHITESPACE@10..11 " " - BLOCK_EXPR@11..349 - STMT_LIST@11..349 + BLOCK_EXPR@11..610 + STMT_LIST@11..610 L_CURLY@11..12 "{" WHITESPACE@12..17 "\n " LET_STMT@17..129 @@ -90,38 +90,103 @@ WHITESPACE@251..256 "\n " R_CURLY@256..257 "}" WHITESPACE@257..262 "\n " - WHILE_EXPR@262..347 - WHILE_KW@262..267 "while" - WHITESPACE@267..268 " " - LITERAL@268..272 - TRUE_KW@268..272 "true" - WHITESPACE@272..273 " " - BLOCK_EXPR@273..347 - STMT_LIST@273..347 - L_CURLY@273..274 "{" - WHITESPACE@274..283 "\n " - ATTR@283..302 - POUND@283..284 "#" - BANG@284..285 "!" - L_BRACK@285..286 "[" - TOKEN_TREE_META@286..301 - PATH@286..289 - PATH_SEGMENT@286..289 - NAME_REF@286..289 - IDENT@286..289 "doc" - TOKEN_TREE@289..301 - L_PAREN@289..290 "(" - STRING@290..300 "\"Nor here\"" - R_PAREN@300..301 ")" - R_BRACK@301..302 "]" - WHITESPACE@302..311 "\n " - COMMENT@311..341 "//! Nor are ModuleDoc ..." - WHITESPACE@341..346 "\n " - R_CURLY@346..347 "}" - WHITESPACE@347..348 "\n" - R_CURLY@348..349 "}" - WHITESPACE@349..350 "\n" + EXPR_STMT@262..347 + WHILE_EXPR@262..347 + WHILE_KW@262..267 "while" + WHITESPACE@267..268 " " + LITERAL@268..272 + TRUE_KW@268..272 "true" + WHITESPACE@272..273 " " + BLOCK_EXPR@273..347 + STMT_LIST@273..347 + L_CURLY@273..274 "{" + WHITESPACE@274..283 "\n " + ATTR@283..302 + POUND@283..284 "#" + BANG@284..285 "!" + L_BRACK@285..286 "[" + TOKEN_TREE_META@286..301 + PATH@286..289 + PATH_SEGMENT@286..289 + NAME_REF@286..289 + IDENT@286..289 "doc" + TOKEN_TREE@289..301 + L_PAREN@289..290 "(" + STRING@290..300 "\"Nor here\"" + R_PAREN@300..301 ")" + R_BRACK@301..302 "]" + WHITESPACE@302..311 "\n " + COMMENT@311..341 "//! Nor are ModuleDoc ..." + WHITESPACE@341..346 "\n " + R_CURLY@346..347 "}" + WHITESPACE@347..353 "\n " + EXPR_STMT@353..474 + LOOP_EXPR@353..474 + LOOP_KW@353..357 "loop" + WHITESPACE@357..358 " " + BLOCK_EXPR@358..474 + STMT_LIST@358..474 + L_CURLY@358..359 "{" + WHITESPACE@359..368 "\n " + ATTR@368..430 + POUND@368..369 "#" + BANG@369..370 "!" + L_BRACK@370..371 "[" + TOKEN_TREE_META@371..429 + PATH@371..374 + PATH_SEGMENT@371..374 + NAME_REF@371..374 + IDENT@371..374 "doc" + TOKEN_TREE@374..429 + L_PAREN@374..375 "(" + STRING@375..428 "\"This is fine, `loop` ..." + R_PAREN@428..429 ")" + R_BRACK@429..430 "]" + WHITESPACE@430..439 "\n " + COMMENT@439..468 "//! So are ModuleDoc ..." + WHITESPACE@468..473 "\n " + R_CURLY@473..474 "}" + WHITESPACE@474..479 "\n " + FOR_EXPR@479..608 + FOR_KW@479..482 "for" + WHITESPACE@482..483 " " + WILDCARD_PAT@483..484 + UNDERSCORE@483..484 "_" + WHITESPACE@484..485 " " + IN_KW@485..487 "in" + WHITESPACE@487..488 " " + RANGE_EXPR@488..492 + LITERAL@488..489 + INT_NUMBER@488..489 "0" + DOT2@489..491 ".." + LITERAL@491..492 + INT_NUMBER@491..492 "1" + WHITESPACE@492..493 " " + BLOCK_EXPR@493..608 + STMT_LIST@493..608 + L_CURLY@493..494 "{" + WHITESPACE@494..503 "\n " + ATTR@503..564 + POUND@503..504 "#" + BANG@504..505 "!" + L_BRACK@505..506 "[" + TOKEN_TREE_META@506..563 + PATH@506..509 + PATH_SEGMENT@506..509 + NAME_REF@506..509 + IDENT@506..509 "doc" + TOKEN_TREE@509..563 + L_PAREN@509..510 "(" + STRING@510..562 "\"This is fine, `for` ..." + R_PAREN@562..563 ")" + R_BRACK@563..564 "]" + WHITESPACE@564..573 "\n " + COMMENT@573..602 "//! So are ModuleDoc ..." + WHITESPACE@602..607 "\n " + R_CURLY@607..608 "}" + WHITESPACE@608..609 "\n" + R_CURLY@609..610 "}" + WHITESPACE@610..611 "\n" error 39..83: A block in this position cannot accept inner attributes error 152..171: A block in this position cannot accept inner attributes error 180..212: A block in this position cannot accept inner attributes -error 283..302: A block in this position cannot accept inner attributes
diff --git a/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rs b/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rs index 6a04f2d..65bb7e1 100644 --- a/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rs +++ b/crates/syntax/test_data/parser/validation/0031_block_inner_attrs.rs
@@ -12,4 +12,12 @@ #![doc("Nor here")] //! Nor are ModuleDoc comments } + loop { + #![doc("This is fine, `loop` bodies accept inner attributes")] + //! So are ModuleDoc comments + } + for _ in 0..1 { + #![doc("This is fine, `for` bodies accept inner attributes")] + //! So are ModuleDoc comments + } }
diff --git a/crates/syntax/test_data/parser/validation/0041_illegal_self_keyword_location.rast b/crates/syntax/test_data/parser/validation/0041_illegal_self_keyword_location.rast index 01f6010..92f7469 100644 --- a/crates/syntax/test_data/parser/validation/0041_illegal_self_keyword_location.rast +++ b/crates/syntax/test_data/parser/validation/0041_illegal_self_keyword_location.rast
@@ -1,4 +1,4 @@ -SOURCE_FILE@0..25 +SOURCE_FILE@0..28 USE@0..11 USE_KW@0..3 "use" WHITESPACE@3..4 " " @@ -10,20 +10,24 @@ SELF_KW@6..10 "self" SEMICOLON@10..11 ";" WHITESPACE@11..12 "\n" - USE@12..24 + USE@12..27 USE_KW@12..15 "use" WHITESPACE@15..16 " " - USE_TREE@16..23 - PATH@16..23 - PATH@16..17 - PATH_SEGMENT@16..17 - NAME_REF@16..17 - IDENT@16..17 "a" - COLON2@17..19 "::" - PATH_SEGMENT@19..23 - NAME_REF@19..23 - SELF_KW@19..23 "self" - SEMICOLON@23..24 ";" - WHITESPACE@24..25 "\n" -error 6..10: The `self` keyword is only allowed as the first segment of a path -error 19..23: The `self` keyword is only allowed as the first segment of a path + USE_TREE@16..26 + PATH@16..26 + PATH@16..23 + PATH@16..17 + PATH_SEGMENT@16..17 + NAME_REF@16..17 + IDENT@16..17 "a" + COLON2@17..19 "::" + PATH_SEGMENT@19..23 + NAME_REF@19..23 + SELF_KW@19..23 "self" + COLON2@23..25 "::" + PATH_SEGMENT@25..26 + NAME_REF@25..26 + IDENT@25..26 "b" + SEMICOLON@26..27 ";" + WHITESPACE@27..28 "\n" +error 19..23: The `self` keyword is only allowed at the start or at the end of a path
diff --git a/crates/syntax/test_data/parser/validation/0041_illegal_self_keyword_location.rs b/crates/syntax/test_data/parser/validation/0041_illegal_self_keyword_location.rs index b9e1d7d..9a9429c 100644 --- a/crates/syntax/test_data/parser/validation/0041_illegal_self_keyword_location.rs +++ b/crates/syntax/test_data/parser/validation/0041_illegal_self_keyword_location.rs
@@ -1,2 +1,2 @@ use ::self; -use a::self; +use a::self::b;
diff --git a/crates/toolchain/Cargo.toml b/crates/toolchain/Cargo.toml index d0d5840..8ba63ae 100644 --- a/crates/toolchain/Cargo.toml +++ b/crates/toolchain/Cargo.toml
@@ -14,6 +14,7 @@ [dependencies] camino.workspace = true +semver.workspace = true [lints] workspace = true
diff --git a/crates/toolchain/src/lib.rs b/crates/toolchain/src/lib.rs index ea08f7a..6bed98f 100644 --- a/crates/toolchain/src/lib.rs +++ b/crates/toolchain/src/lib.rs
@@ -143,3 +143,33 @@ }; iter::once(path).chain(with_extension).find(|it| it.is_file()) } + +pub const MINIMUM_TOOLCHAIN_VERSION_REQUIRING_JSON_TARGET_SPEC_FLAG: semver::Version = + semver::Version { + major: 1, + minor: 95, + patch: 0, + pre: semver::Prerelease::EMPTY, + build: semver::BuildMetadata::EMPTY, + }; + +/// Uses targets in a Cargo process. +pub fn cargo_use_targets( + toolchain_version: Option<&semver::Version>, + cmd: &mut Command, + targets: impl IntoIterator<Item = impl AsRef<str>>, +) { + let mut has_json_target = false; + for target in targets { + let target = target.as_ref(); + cmd.args(["--target", target]); + has_json_target |= target.ends_with(".json"); + } + if has_json_target + && toolchain_version.is_some_and(|version| { + *version >= MINIMUM_TOOLCHAIN_VERSION_REQUIRING_JSON_TARGET_SPEC_FLAG + }) + { + cmd.arg("-Zjson-target-spec"); + } +}
diff --git a/docs/book/src/configuration_generated.md b/docs/book/src/configuration_generated.md index 4df17d7..1c46427 100644 --- a/docs/book/src/configuration_generated.md +++ b/docs/book/src/configuration_generated.md
@@ -19,14 +19,6 @@ Prefer to use `Self` over the type name when inserting a type (e.g. in "fill match arms" assist). -## rust-analyzer.assist.termSearch.borrowcheck {#assist.termSearch.borrowcheck} - -Default: `true` - -Enable borrow checking for term search code assists. If set to false, also there will be -more suggestions, but some of them may not borrow-check. - - ## rust-analyzer.assist.termSearch.fuel {#assist.termSearch.fuel} Default: `1800`
diff --git a/editors/code/package.json b/editors/code/package.json index d152cfb..de9f17b 100644 --- a/editors/code/package.json +++ b/editors/code/package.json
@@ -816,16 +816,6 @@ { "title": "Assist", "properties": { - "rust-analyzer.assist.termSearch.borrowcheck": { - "markdownDescription": "Enable borrow checking for term search code assists. If set to false, also there will be\nmore suggestions, but some of them may not borrow-check.", - "default": true, - "type": "boolean" - } - } - }, - { - "title": "Assist", - "properties": { "rust-analyzer.assist.termSearch.fuel": { "markdownDescription": "Term search fuel in \"units of work\" for assists (Defaults to 1800).", "default": 1800, @@ -914,7 +904,7 @@ ], "enumDescriptions": [ "The command will be executed for each Rust workspace with the workspace as the working directory.", - "The command will be executed once with the opened project as the working directory." + "The command will be executed once with the opened project as the working directory.\n\n**Warning:** This can cause errors when the Cargo configuration of the opened workspaces is different or even only different than the default. Enabling this is not recommended." ] } } @@ -1247,7 +1237,7 @@ ], "enumDescriptions": [ "The command will be executed for each Rust workspace with the workspace as the working directory.", - "The command will be executed once with the opened project as the working directory." + "The command will be executed once with the opened project as the working directory.\n\n**Warning:** This can cause errors when the Cargo configuration of the opened workspaces is different or even only different than the default. Enabling this is not recommended." ] } }
diff --git a/editors/code/src/bootstrap.ts b/editors/code/src/bootstrap.ts index ca5b7e3..440f21c 100644 --- a/editors/code/src/bootstrap.ts +++ b/editors/code/src/bootstrap.ts
@@ -176,14 +176,24 @@ ); } +// Captures the elements of a `components` array. They are matched with `[^\]]` rather than `.` +// so that the array may be spread over several lines, which is just as valid TOML as keeping it +// on one, while still stopping at the end of the array. +const COMPONENTS_RE = /components\s*=\s*\[(?<components>[^\]]*)\]/; +// TOML strings come in both quote flavours. +const RA_COMPONENT_RE = /["']rust-analyzer["']/; + +function declaresRaComponent(toolchainFileContents: string): boolean { + const components = toolchainFileContents.match(COMPONENTS_RE)?.groups?.["components"]; + return components !== undefined && RA_COMPONENT_RE.test(components); +} + async function hasToolchainFileWithRaDeclared(uri: vscode.Uri): Promise<boolean> { try { const toolchainFileContents = new TextDecoder().decode( await vscode.workspace.fs.readFile(uri), ); - return ( - toolchainFileContents.match(/components\s*=\s*\[.*"rust-analyzer".*\]/g)?.length === 1 - ); + return declaresRaComponent(toolchainFileContents); } catch (_) { return false; } @@ -296,6 +306,7 @@ } export const _private = { + declaresRaComponent, earliestToolchainPath, orderFromPath, };
diff --git a/editors/code/tests/unit/bootstrap.test.ts b/editors/code/tests/unit/bootstrap.test.ts index baabf4f..259428d 100644 --- a/editors/code/tests/unit/bootstrap.test.ts +++ b/editors/code/tests/unit/bootstrap.test.ts
@@ -93,4 +93,75 @@ ); }); }); + + await ctx.suite("Bootstrap/Detect RA component in toolchain file", (suite) => { + suite.addTest("Single line components array", async () => { + assert.ok( + _private.declaresRaComponent( + `[toolchain] +channel = "1.88" +components = ["cargo", "rust-analyzer", "rustfmt"] +`, + ), + ); + }); + + suite.addTest("Multi line components array", async () => { + assert.ok( + _private.declaresRaComponent( + `[toolchain] +channel = "1.88" +components = [ + "cargo", + "rust-analyzer", + "rustfmt", +] +profile = "default" +`, + ), + ); + }); + + suite.addTest("Components array with literal strings", async () => { + assert.ok(_private.declaresRaComponent(`components = ['cargo', 'rust-analyzer']`)); + }); + + suite.addTest("Components array without RA", async () => { + assert.ok( + !_private.declaresRaComponent( + `[toolchain] +channel = "1.88" +components = [ + "cargo", + "rustfmt", +] +`, + ), + ); + }); + + suite.addTest("No components array", async () => { + assert.ok( + !_private.declaresRaComponent( + `[toolchain] +channel = "1.88" +`, + ), + ); + }); + + suite.addTest("RA mentioned outside the components array", async () => { + assert.ok( + !_private.declaresRaComponent( + `[toolchain] +channel = "1.88" +components = [ + "cargo", +] +# add "rust-analyzer" here to use the matching server +`, + ), + ); + }); + }); }
diff --git a/lib/lsp-server/Cargo.toml b/lib/lsp-server/Cargo.toml index 1a179ef..4913d6b 100644 --- a/lib/lsp-server/Cargo.toml +++ b/lib/lsp-server/Cargo.toml
@@ -14,7 +14,7 @@ crossbeam-channel.workspace = true [dev-dependencies] -lsp-types = { version = "0.10.0", package = "gen-lsp-types", features = [ +lsp-types = { version = "0.11.0", package = "gen-lsp-types", features = [ "url", ] } ctrlc = "3.4.7"